Changeset 1871118 in mainline


Ignore:
Timestamp:
2023-02-10T22:59:11Z (14 months ago)
Author:
Jiří Zárevúcky <zarevucky.jiri@…>
Branches:
master, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
11d2c983
Parents:
daadfa6
git-author:
Jiří Zárevúcky <zarevucky.jiri@…> (2023-02-10 22:53:12)
git-committer:
Jiří Zárevúcky <zarevucky.jiri@…> (2023-02-10 22:59:11)
Message:

Make thread_t reference counted

This simplifies interaction between various locks and thread
lifespan, which simplifies things. For example, threads_lock can
now simply be a mutex protecting the global it was made for, and
nothing more.

Location:
kernel
Files:
20 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/include/lib/refcount.h

    rdaadfa6 r1871118  
    7676}
    7777
     78/**
     79 * Try to upgrade a weak reference.
     80 * Naturally, this is contingent on another form of synchronization being used
     81 * to ensure that the object continues to exist while the weak reference is in
     82 * use.
     83 */
     84static inline bool refcount_try_up(atomic_refcount_t *rc)
     85{
     86        int cnt = atomic_load_explicit(&rc->__cnt, memory_order_relaxed);
     87
     88        while (cnt >= 0) {
     89                if (atomic_compare_exchange_weak_explicit(&rc->__cnt, &cnt, cnt + 1,
     90                    memory_order_relaxed, memory_order_relaxed)) {
     91                        return true;
     92                }
     93        }
     94
     95        return false;
     96}
     97
    7898static inline bool refcount_unique(atomic_refcount_t *rc)
    7999{
  • kernel/generic/include/proc/thread.h

    rdaadfa6 r1871118  
    7070/** Thread structure. There is one per thread. */
    7171typedef struct thread {
     72        atomic_refcount_t refcount;
     73
    7274        link_t rq_link;  /**< Run queue link. */
    7375        link_t wq_link;  /**< Wait queue link. */
     
    7779        odlink_t lthreads;
    7880
     81        /**
     82         * If true, the thread is terminating.
     83         * It will not go to sleep in interruptible synchronization functions
     84         * and will call thread_exit() before returning to userspace.
     85         */
     86        volatile bool interrupted;
     87
     88        /** Waitq for thread_join_timeout(). */
     89        waitq_t join_wq;
     90
    7991        /** Lock protecting thread structure.
    8092         *
    81          * Protects the whole thread structure except list links above.
     93         * Protects the whole thread structure except fields listed above.
    8294         */
    8395        IRQ_SPINLOCK_DECLARE(lock);
     
    133145         */
    134146        bool in_copy_to_uspace;
    135 
    136         /**
    137          * If true, the thread will not go to sleep at all and will call
    138          * thread_exit() before returning to userspace.
    139          */
    140         bool interrupted;
    141 
    142         /** If true, thread_join_timeout() cannot be used on this thread. */
    143         bool detached;
    144         /** Waitq for thread_join_timeout(). */
    145         waitq_t join_wq;
    146         /** Link used in the joiner_head list. */
    147         link_t joiner_link;
    148147
    149148#ifdef CONFIG_FPU
     
    219218extern void thread_interrupt(thread_t *, bool);
    220219
     220static inline thread_t *thread_ref(thread_t *thread)
     221{
     222        refcount_up(&thread->refcount);
     223        return thread;
     224}
     225
     226static inline thread_t *thread_try_ref(thread_t *thread)
     227{
     228        if (refcount_try_up(&thread->refcount))
     229                return thread;
     230        else
     231                return NULL;
     232}
     233
     234extern void thread_put(thread_t *);
     235
    221236#ifndef thread_create_arch
    222237extern errno_t thread_create_arch(thread_t *, thread_flags_t);
     
    236251extern errno_t thread_join(thread_t *);
    237252extern errno_t thread_join_timeout(thread_t *, uint32_t, unsigned int);
    238 extern void thread_detach(thread_t *);
    239253
    240254extern void thread_print_list(bool);
    241 extern void thread_destroy(thread_t *, bool);
    242255extern thread_t *thread_find_by_id(thread_id_t);
    243256extern size_t thread_count(void);
     
    245258extern thread_t *thread_next(thread_t *);
    246259extern void thread_update_accounting(bool);
    247 extern bool thread_exists(thread_t *);
     260extern thread_t *thread_try_get(thread_t *);
    248261
    249262extern void thread_migration_disable(void);
  • kernel/generic/include/synch/waitq.h

    rdaadfa6 r1871118  
    4343typedef enum {
    4444        WAKEUP_FIRST = 0,
    45         WAKEUP_ALL
     45        WAKEUP_ALL,
     46        WAKEUP_CLOSE,
    4647} wakeup_mode_t;
    4748
  • kernel/generic/src/console/cmd.c

    rdaadfa6 r1871118  
    993993                        printf("cpu%u: ", i);
    994994                        thread_wire(thread, &cpus[i]);
    995                         thread_ready(thread);
     995                        thread_ready(thread_ref(thread));
    996996                        thread_join(thread);
    997                         thread_detach(thread);
     997                        thread_put(thread);
    998998                } else
    999999                        printf("Unable to create thread for cpu%u\n", i);
  • kernel/generic/src/ipc/kbox.c

    rdaadfa6 r1871118  
    9090                LOG("Join kb.thread.");
    9191                thread_join(TASK->kb.thread);
    92                 thread_detach(TASK->kb.thread);
     92                thread_put(TASK->kb.thread);
    9393                LOG("...join done.");
    9494                TASK->kb.thread = NULL;
     
    136136                /* Only detach kbox thread unless already terminating. */
    137137                if (TASK->kb.finished == false) {
    138                         /* Detach kbox thread so it gets freed from memory. */
    139                         thread_detach(TASK->kb.thread);
     138                        /* Release kbox thread so it gets freed from memory. */
     139                        thread_put(TASK->kb.thread);
    140140                        TASK->kb.thread = NULL;
    141141                }
     
    247247                }
    248248
    249                 task->kb.thread = kb_thread;
     249                task->kb.thread = thread_ref(kb_thread);
    250250                thread_ready(kb_thread);
    251251        }
  • kernel/generic/src/main/kinit.c

    rdaadfa6 r1871118  
    102102        thread_t *thread;
    103103
    104         /*
    105          * Detach kinit as nobody will call thread_join_timeout() on it.
    106          */
    107         thread_detach(THREAD);
    108 
    109104        interrupts_disable();
    110105
     
    125120
    126121                thread_wire(thread, &cpus[0]);
    127                 thread_ready(thread);
     122                thread_ready(thread_ref(thread));
    128123                thread_join(thread);
    129                 thread_detach(thread);
     124                thread_put(thread);
    130125
    131126                /*
  • kernel/generic/src/main/uinit.c

    rdaadfa6 r1871118  
    5656void uinit(void *arg)
    5757{
    58         /*
    59          * So far, we don't have a use for joining userspace threads so we
    60          * immediately detach each uinit thread. If joining of userspace threads
    61          * is required, some userspace API based on the kernel mechanism will
    62          * have to be implemented. Moreover, garbage collecting of threads that
    63          * didn't detach themselves and nobody else joined them will have to be
    64          * deployed for the event of forceful task termination.
    65          */
    66         thread_detach(THREAD);
    67 
    6858#ifdef CONFIG_UDEBUG
    6959        udebug_stoppable_end();
  • kernel/generic/src/proc/program.c

    rdaadfa6 r1871118  
    204204{
    205205        thread_ready(prg->main_thread);
     206        prg->main_thread = NULL;
    206207}
    207208
  • kernel/generic/src/proc/scheduler.c

    rdaadfa6 r1871118  
    389389void scheduler_separated_stack(void)
    390390{
    391         DEADLOCK_PROBE_INIT(p_joinwq);
    392391        task_t *old_task = TASK;
    393392        as_t *old_as = AS;
     
    419418
    420419                case Exiting:
    421                 repeat:
    422                         if (THREAD->detached) {
    423                                 thread_destroy(THREAD, false);
    424                         } else {
    425                                 /*
    426                                  * The thread structure is kept allocated until
    427                                  * somebody calls thread_detach() on it.
    428                                  */
    429                                 if (!irq_spinlock_trylock(&THREAD->join_wq.lock)) {
    430                                         /*
    431                                          * Avoid deadlock.
    432                                          */
    433                                         irq_spinlock_unlock(&THREAD->lock, false);
    434                                         delay(HZ);
    435                                         irq_spinlock_lock(&THREAD->lock, false);
    436                                         DEADLOCK_PROBE(p_joinwq,
    437                                             DEADLOCK_THRESHOLD);
    438                                         goto repeat;
    439                                 }
    440                                 _waitq_wakeup_unsafe(&THREAD->join_wq,
    441                                     WAKEUP_FIRST);
    442                                 irq_spinlock_unlock(&THREAD->join_wq.lock, false);
    443 
    444                                 THREAD->state = Lingering;
    445                                 irq_spinlock_unlock(&THREAD->lock, false);
    446                         }
     420                        irq_spinlock_unlock(&THREAD->lock, false);
     421                        waitq_wakeup(&THREAD->join_wq, WAKEUP_CLOSE);
     422
     423                        /*
     424                         * Release the reference CPU has for the thread.
     425                         * If there are no other references (e.g. threads calling join),
     426                         * the thread structure is deallocated.
     427                         */
     428                        thread_put(THREAD);
    447429                        break;
    448430
     
    556538        size_t average;
    557539        size_t rdy;
    558 
    559         /*
    560          * Detach kcpulb as nobody will call thread_join_timeout() on it.
    561          */
    562         thread_detach(THREAD);
    563540
    564541loop:
  • kernel/generic/src/proc/task.c

    rdaadfa6 r1871118  
    385385        irq_spinlock_lock(&tasks_lock, true);
    386386        irq_spinlock_lock(&TASK->lock, false);
    387         irq_spinlock_lock(&threads_lock, false);
    388387
    389388        /* Set task name */
    390389        str_cpy(TASK->name, TASK_NAME_BUFLEN, namebuf);
    391390
    392         irq_spinlock_unlock(&threads_lock, false);
    393391        irq_spinlock_unlock(&TASK->lock, false);
    394392        irq_spinlock_unlock(&tasks_lock, true);
     
    529527{
    530528        irq_spinlock_lock(&task->lock, false);
    531         irq_spinlock_lock(&threads_lock, false);
    532529
    533530        /*
     
    536533
    537534        list_foreach(task->threads, th_link, thread_t, thread) {
    538                 thread_interrupt(thread, false);
     535                thread_t *thr = thread_try_ref(thread);
     536                if (thr)
     537                        thread_interrupt(thr, false);
     538
     539                // If NULL, the thread is already getting destroyed concurrently with this.
    539540        }
    540541
    541         irq_spinlock_unlock(&threads_lock, false);
    542542        irq_spinlock_unlock(&task->lock, false);
    543543}
  • kernel/generic/src/proc/thread.c

    rdaadfa6 r1871118  
    9494 *
    9595 * Members are of type thread_t.
     96 *
     97 * This structure contains weak references. Any reference from it must not leave
     98 * threads_lock critical section unless strengthened via thread_try_ref().
    9699 */
    97100odict_t threads;
     
    249252/** Make thread ready
    250253 *
    251  * Switch thread to the ready state.
     254 * Switch thread to the ready state. Consumes reference passed by the caller.
    252255 *
    253256 * @param thread Thread to make ready.
     
    320323                return NULL;
    321324
     325        refcount_init(&thread->refcount);
     326
    322327        if (thread_create_arch(thread, flags) != EOK) {
    323328                slab_free(thread_cache, thread);
     
    368373
    369374        thread->interrupted = false;
    370         thread->detached = false;
    371375        waitq_initialize(&thread->join_wq);
    372376
     
    399403 *
    400404 */
    401 void thread_destroy(thread_t *thread, bool irq_res)
    402 {
    403         assert(irq_spinlock_locked(&thread->lock));
     405static void thread_destroy(void *obj)
     406{
     407        thread_t *thread = (thread_t *) obj;
     408
     409        irq_spinlock_lock(&thread->lock, true);
    404410        assert((thread->state == Exiting) || (thread->state == Lingering));
    405411        assert(thread->task);
     
    421427         */
    422428        list_remove(&thread->th_link);
    423         irq_spinlock_unlock(&thread->task->lock, irq_res);
     429        irq_spinlock_unlock(&thread->task->lock, true);
    424430
    425431        /*
     
    430436}
    431437
     438void thread_put(thread_t *thread)
     439{
     440        if (refcount_down(&thread->refcount)) {
     441                thread_destroy(thread);
     442        }
     443}
     444
    432445/** Make the thread visible to the system.
    433446 *
     
    441454void thread_attach(thread_t *thread, task_t *task)
    442455{
     456        ipl_t ipl = interrupts_disable();
     457
    443458        /*
    444459         * Attach to the specified task.
    445460         */
    446         irq_spinlock_lock(&task->lock, true);
     461        irq_spinlock_lock(&task->lock, false);
    447462
    448463        /* Hold a reference to the task. */
     
    455470        list_append(&thread->th_link, &task->threads);
    456471
    457         irq_spinlock_pass(&task->lock, &threads_lock);
     472        irq_spinlock_unlock(&task->lock, false);
    458473
    459474        /*
    460475         * Register this thread in the system-wide dictionary.
    461476         */
     477        irq_spinlock_lock(&threads_lock, false);
    462478        odict_insert(&thread->lthreads, &threads, NULL);
    463         irq_spinlock_unlock(&threads_lock, true);
     479        irq_spinlock_unlock(&threads_lock, false);
     480
     481        interrupts_restore(ipl);
    464482}
    465483
     
    513531 * blocking call was interruptable. See waitq_sleep_timeout().
    514532 *
    515  * The caller must guarantee the thread object is valid during the entire
    516  * function, eg by holding the threads_lock lock.
    517  *
    518533 * Interrupted threads automatically exit when returning back to user space.
    519534 *
    520  * @param thread A valid thread object. The caller must guarantee it
    521  *               will remain valid until thread_interrupt() exits.
     535 * @param thread A valid thread object.
    522536 */
    523537void thread_interrupt(thread_t *thread, bool irq_dis)
     
    534548        if (sleeping)
    535549                waitq_interrupt_sleep(thread);
     550
     551        thread_put(thread);
    536552}
    537553
     
    581597
    582598/** Wait for another thread to exit.
     599 * This function does not destroy the thread. Reference counting handles that.
    583600 *
    584601 * @param thread Thread to join on exit.
     
    594611                return EINVAL;
    595612
    596         /*
    597          * Since thread join can only be called once on an undetached thread,
    598          * the thread pointer is guaranteed to be still valid.
    599          */
    600 
    601613        irq_spinlock_lock(&thread->lock, true);
    602         assert(!thread->detached);
     614        state_t state = thread->state;
    603615        irq_spinlock_unlock(&thread->lock, true);
    604616
    605         return waitq_sleep_timeout(&thread->join_wq, usec, flags, NULL);
    606 
    607         // FIXME: join should deallocate the thread.
    608         //        Current code calls detach after join, that's contrary to how
    609         //        join is used in other threading APIs.
    610 }
    611 
    612 /** Detach thread.
    613  *
    614  * Mark the thread as detached. If the thread is already
    615  * in the Lingering state, deallocate its resources.
    616  *
    617  * @param thread Thread to be detached.
    618  *
    619  */
    620 void thread_detach(thread_t *thread)
    621 {
    622         /*
    623          * Since the thread is expected not to be already detached,
    624          * pointer to it must be still valid.
    625          */
    626         irq_spinlock_lock(&thread->lock, true);
    627         assert(!thread->detached);
    628 
    629         if (thread->state == Lingering) {
    630                 /*
    631                  * Unlock &thread->lock and restore
    632                  * interrupts in thread_destroy().
    633                  */
    634                 thread_destroy(thread, true);
    635                 return;
     617        if (state == Exiting) {
     618                return EOK;
    636619        } else {
    637                 thread->detached = true;
    638         }
    639 
    640         irq_spinlock_unlock(&thread->lock, true);
     620                return waitq_sleep_timeout(&thread->join_wq, usec,
     621                    SYNCH_FLAGS_NON_BLOCKING, NULL);
     622        }
    641623}
    642624
     
    702684        thread_t *thread;
    703685
    704         /* Messing with thread structures, avoid deadlock */
     686        /* Accessing system-wide threads list through thread_first()/thread_next(). */
    705687        irq_spinlock_lock(&threads_lock, true);
    706688
     
    730712}
    731713
    732 /** Check whether thread exists.
    733  *
    734  * Note that threads_lock must be already held and
    735  * interrupts must be already disabled.
    736  *
    737  * @param thread Pointer to thread.
    738  *
    739  * @return True if thread t is known to the system, false otherwise.
    740  *
    741  */
    742 bool thread_exists(thread_t *thread)
    743 {
    744         assert(interrupts_disabled());
    745         assert(irq_spinlock_locked(&threads_lock));
    746 
     714static bool thread_exists(thread_t *thread)
     715{
    747716        odlink_t *odlink = odict_find_eq(&threads, thread, NULL);
    748717        return odlink != NULL;
     718}
     719
     720/** Check whether the thread exists, and if so, return a reference to it.
     721 */
     722thread_t *thread_try_get(thread_t *thread)
     723{
     724        irq_spinlock_lock(&threads_lock, true);
     725
     726        if (thread_exists(thread)) {
     727                /* Try to strengthen the reference. */
     728                thread = thread_try_ref(thread);
     729        } else {
     730                thread = NULL;
     731        }
     732
     733        irq_spinlock_unlock(&threads_lock, true);
     734
     735        return thread;
    749736}
    750737
     
    777764 * interrupts must be disabled.
    778765 *
     766 * The returned reference is weak.
     767 * If the caller needs to keep it, thread_try_ref() must be used to upgrade
     768 * to a strong reference _before_ threads_lock is released.
     769 *
    779770 * @param id Thread ID.
    780771 *
     
    854845{
    855846        irq_spinlock_lock(&threads_lock, true);
    856 
    857         thread_t *thread = thread_find_by_id(thread_id);
     847        thread_t *thread = thread_try_ref(thread_find_by_id(thread_id));
     848        irq_spinlock_unlock(&threads_lock, true);
     849
    858850        if (thread == NULL) {
    859851                printf("No such thread.\n");
    860                 irq_spinlock_unlock(&threads_lock, true);
    861852                return;
    862853        }
    863 
    864         irq_spinlock_lock(&thread->lock, false);
    865854
    866855        /*
     
    876865         */
    877866
     867        irq_spinlock_lock(&thread->lock, true);
     868
    878869        bool sleeping = false;
    879870        istate_t *istate = thread->udebug.uspace_state;
     
    886877                printf("Thread interrupt state not available.\n");
    887878
    888         irq_spinlock_unlock(&thread->lock, false);
     879        irq_spinlock_unlock(&thread->lock, true);
    889880
    890881        if (sleeping)
    891882                waitq_interrupt_sleep(thread);
    892883
    893         irq_spinlock_unlock(&threads_lock, true);
     884        thread_put(thread);
    894885}
    895886
  • kernel/generic/src/synch/waitq.c

    rdaadfa6 r1871118  
    513513loop:
    514514        if (list_empty(&wq->sleepers)) {
    515                 if (mode != WAKEUP_ALL) {
     515                if (mode == WAKEUP_CLOSE) {
     516                        // FIXME: this can technically fail if we get two billion sleeps after the wakeup call.
     517                        wq->missed_wakeups = INT_MAX;
     518                } else if (mode != WAKEUP_ALL) {
    516519                        wq->missed_wakeups++;
    517520                }
  • kernel/generic/src/sysinfo/stats.c

    rdaadfa6 r1871118  
    332332    bool dry_run, void *data)
    333333{
    334         /* Messing with threads structures, avoid deadlock */
     334        /* Messing with threads structures */
    335335        irq_spinlock_lock(&threads_lock, true);
    336336
     
    597597                return ret;
    598598
    599         /* Messing with threads structures, avoid deadlock */
     599        /* Messing with threads structures */
    600600        irq_spinlock_lock(&threads_lock, true);
    601601
     
    627627                ret.data.size = sizeof(stats_thread_t);
    628628
    629                 /* Hand-over-hand locking */
    630                 irq_spinlock_exchange(&threads_lock, &thread->lock);
    631 
     629                /*
     630                 * Replaced hand-over-hand locking with regular nested sections
     631                 * to avoid weak reference leak issues.
     632                 */
     633                irq_spinlock_lock(&thread->lock, false);
    632634                produce_stats_thread(thread, stats_thread);
    633 
    634                 irq_spinlock_unlock(&thread->lock, true);
     635                irq_spinlock_unlock(&thread->lock, false);
     636
     637                irq_spinlock_unlock(&threads_lock, true);
    635638        }
    636639
     
    847850void kload(void *arg)
    848851{
    849         thread_detach(THREAD);
    850 
    851852        while (true) {
    852853                size_t ready = atomic_load(&nrdy);
  • kernel/generic/src/udebug/udebug_ops.c

    rdaadfa6 r1871118  
    8383        mutex_lock(&TASK->udebug.lock);
    8484
    85         /* thread_exists() must be called with threads_lock held */
    86         irq_spinlock_lock(&threads_lock, true);
    87 
    88         if (!thread_exists(thread)) {
    89                 irq_spinlock_unlock(&threads_lock, true);
     85        thread = thread_try_get(thread);
     86
     87        if (!thread) {
    9088                mutex_unlock(&TASK->udebug.lock);
    9189                return ENOENT;
    9290        }
    9391
    94         /* thread->lock is enough to ensure the thread's existence */
    95         irq_spinlock_exchange(&threads_lock, &thread->lock);
     92        irq_spinlock_lock(&thread->lock, true);
    9693
    9794        /* Verify that 'thread' is a userspace thread. */
     
    111108        }
    112109
    113         /*
    114          * Since the thread has active == true, TASK->udebug.lock
    115          * is enough to ensure its existence and that active remains
    116          * true.
    117          *
    118          */
    119         irq_spinlock_unlock(&thread->lock, true);
    120 
    121         /* Only mutex TASK->udebug.lock left. */
    122 
    123110        /* Now verify that the thread belongs to the current task. */
    124111        if (thread->task != TASK) {
    125112                /* No such thread belonging this task */
     113                irq_spinlock_unlock(&thread->lock, true);
    126114                mutex_unlock(&TASK->udebug.lock);
    127115                return ENOENT;
    128116        }
     117
     118        irq_spinlock_unlock(&thread->lock, true);
     119
     120        /* Only mutex TASK->udebug.lock left. */
    129121
    130122        /*
     
    153145{
    154146        mutex_unlock(&thread->udebug.lock);
     147
     148        /* Drop reference from _thread_op_begin() */
     149        thread_put(thread);
    155150}
    156151
  • kernel/test/mm/falloc2.c

    rdaadfa6 r1871118  
    5959                return;
    6060        }
    61 
    62         thread_detach(THREAD);
    6361
    6462        for (unsigned int run = 0; run < THREAD_RUNS; run++) {
  • kernel/test/mm/slab1.c

    rdaadfa6 r1871118  
    128128        int i, j;
    129129
    130         thread_detach(THREAD);
    131 
    132130        TPRINTF("Starting thread #%" PRIu64 "...\n", THREAD->tid);
    133131
  • kernel/test/mm/slab2.c

    rdaadfa6 r1871118  
    137137        void *data = NULL, *new;
    138138
    139         thread_detach(THREAD);
    140 
    141139        mutex_lock(&starter_mutex);
    142140        condvar_wait(&thread_starter, &starter_mutex);
  • kernel/test/synch/semaphore1.c

    rdaadfa6 r1871118  
    4646static void producer(void *arg)
    4747{
    48         thread_detach(THREAD);
    49 
    5048        waitq_sleep(&can_start);
    5149
     
    5856static void consumer(void *arg)
    5957{
    60         thread_detach(THREAD);
    61 
    6258        waitq_sleep(&can_start);
    6359
  • kernel/test/synch/semaphore2.c

    rdaadfa6 r1871118  
    6161        int to;
    6262
    63         thread_detach(THREAD);
    64 
    6563        waitq_sleep(&can_start);
    6664
  • kernel/test/thread/thread1.c

    rdaadfa6 r1871118  
    4242static void threadtest(void *data)
    4343{
    44         thread_detach(THREAD);
    45 
    4644        while (atomic_load(&finish)) {
    4745                TPRINTF("%" PRIu64 " ", THREAD->tid);
Note: See TracChangeset for help on using the changeset viewer.