Changeset 1871118 in mainline for kernel/generic/src/proc


Ignore:
Timestamp:
2023-02-10T22:59:11Z (3 years 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/generic/src/proc
Files:
4 edited

Legend:

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