Changeset 1871118 in mainline for kernel/generic/src/proc/thread.c


Ignore:
Timestamp:
2023-02-10T22:59:11Z (21 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.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • 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
Note: See TracChangeset for help on using the changeset viewer.