Changeset ea7890e7 in mainline for kernel/generic/src/proc/task.c


Ignore:
Timestamp:
2007-06-01T15:47:46Z (17 years ago)
Author:
Jakub Jermar <jakub@…>
Branches:
lfn, master, serial, ticket/834-toolchain-update, topic/msim-upgrade, topic/simplify-dev-export
Children:
07be3c4
Parents:
ff3a34b
Message:

More efficient and simpler task termination.

Based on the assumption, that after its creation, only the task itself can create more threads for itself,
the last thread with userspace context to execute thread_exit() will perform futex and IPC cleanup. When
the task has no threads, it is destroyed. Both the cleanup and destruction is controlled by reference
counting.

As for userspace threads, even though there could be a global garbage collector for joining threads, it is
much simpler if the uinit thread detaches itself before switching to userspace.

task_kill() is now an idempotent operation. It just instructs the threads within a task to exit.

Change in the name of a thread state: Undead → JoinMe.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • kernel/generic/src/proc/task.c

    rff3a34b rea7890e7  
    5757#include <func.h>
    5858#include <syscall/copy.h>
    59 #include <console/klog.h>
    6059
    6160#ifndef LOADED_PROG_STACK_PAGES_NO
     
    7978
    8079static task_id_t task_counter = 0;
    81 
    82 static void ktaskclnp(void *arg);
    83 static void ktaskgc(void *arg);
    8480
    8581/** Initialize tasks
     
    165161        ta->as = as;
    166162        ta->name = name;
    167         ta->main_thread = NULL;
    168         ta->refcount = 0;
     163        atomic_set(&ta->refcount, 0);
     164        atomic_set(&ta->lifecount, 0);
    169165        ta->context = CONTEXT;
    170166
    171167        ta->capabilities = 0;
    172         ta->accept_new_threads = true;
    173168        ta->cycles = 0;
    174169       
     
    192187
    193188        spinlock_lock(&tasks_lock);
    194 
    195189        ta->taskid = ++task_counter;
    196190        btree_insert(&tasks_btree, (btree_key_t) ta->taskid, (void *) ta, NULL);
    197 
    198191        spinlock_unlock(&tasks_lock);
    199192        interrupts_restore(ipl);
     
    208201void task_destroy(task_t *t)
    209202{
     203        /*
     204         * Remove the task from the task B+tree.
     205         */
     206        spinlock_lock(&tasks_lock);
     207        btree_remove(&tasks_btree, t->taskid, NULL);
     208        spinlock_unlock(&tasks_lock);
     209
     210        /*
     211         * Perform architecture specific task destruction.
     212         */
    210213        task_destroy_arch(t);
     214
     215        /*
     216         * Free up dynamically allocated state.
     217         */
    211218        btree_destroy(&t->futexes);
    212219
     220        /*
     221         * Drop our reference to the address space.
     222         */
    213223        if (atomic_predec(&t->as->refcount) == 0)
    214224                as_destroy(t->as);
     
    230240        as_area_t *a;
    231241        int rc;
    232         thread_t *t1, *t2;
     242        thread_t *t;
    233243        task_t *task;
    234244        uspace_arg_t *kernel_uarg;
     
    264274         * Create the main thread.
    265275         */
    266         t1 = thread_create(uinit, kernel_uarg, task, THREAD_FLAG_USPACE,
     276        t = thread_create(uinit, kernel_uarg, task, THREAD_FLAG_USPACE,
    267277            "uinit", false);
    268         ASSERT(t1);
    269        
    270         /*
    271          * Create killer thread for the new task.
    272          */
    273         t2 = thread_create(ktaskgc, t1, task, 0, "ktaskgc", true);
    274         ASSERT(t2);
    275         thread_ready(t2);
    276 
    277         thread_ready(t1);
     278        ASSERT(t);
     279       
     280        thread_ready(t);
    278281
    279282        return task;
     
    348351/** Kill task.
    349352 *
     353 * This function is idempotent.
     354 * It signals all the task's threads to bail it out.
     355 *
    350356 * @param id ID of the task to be killed.
    351357 *
     
    356362        ipl_t ipl;
    357363        task_t *ta;
    358         thread_t *t;
    359364        link_t *cur;
    360365
     
    364369        ipl = interrupts_disable();
    365370        spinlock_lock(&tasks_lock);
    366 
    367371        if (!(ta = task_find_by_id(id))) {
    368372                spinlock_unlock(&tasks_lock);
     
    370374                return ENOENT;
    371375        }
    372 
     376        spinlock_unlock(&tasks_lock);
     377       
     378        /*
     379         * Interrupt all threads except ktaskclnp.
     380         */
    373381        spinlock_lock(&ta->lock);
    374         ta->refcount++;
    375         spinlock_unlock(&ta->lock);
    376 
    377         btree_remove(&tasks_btree, ta->taskid, NULL);
    378         spinlock_unlock(&tasks_lock);
    379        
    380         t = thread_create(ktaskclnp, NULL, ta, 0, "ktaskclnp", true);
    381        
    382         spinlock_lock(&ta->lock);
    383         ta->accept_new_threads = false;
    384         ta->refcount--;
    385 
    386         /*
    387          * Interrupt all threads except ktaskclnp.
    388          */     
    389382        for (cur = ta->th_head.next; cur != &ta->th_head; cur = cur->next) {
    390383                thread_t *thr;
    391                 bool  sleeping = false;
     384                bool sleeping = false;
    392385               
    393386                thr = list_get_instance(cur, thread_t, th_link);
    394                 if (thr == t)
    395                         continue;
    396387                       
    397388                spinlock_lock(&thr->lock);
     
    404395                        waitq_interrupt_sleep(thr);
    405396        }
    406        
    407397        spinlock_unlock(&ta->lock);
    408398        interrupts_restore(ipl);
    409399       
    410         if (t)
    411                 thread_ready(t);
    412 
    413400        return 0;
    414401}
     
    426413        printf("taskid name       ctx address    as         cycles     threads "
    427414            "calls  callee\n");
    428         printf("------ ---------- --- ---------- ---------- ---------- ------- "            "------ ------>\n");
     415        printf("------ ---------- --- ---------- ---------- ---------- ------- "
     416            "------ ------>\n");
    429417
    430418        for (cur = tasks_btree.leaf_head.next; cur != &tasks_btree.leaf_head;
     
    465453}
    466454
    467 /** Kernel thread used to cleanup the task after it is killed. */
    468 void ktaskclnp(void *arg)
    469 {
    470         ipl_t ipl;
    471         thread_t *t = NULL, *main_thread;
    472         link_t *cur;
    473         bool again;
    474 
    475         thread_detach(THREAD);
    476 
    477 loop:
    478         ipl = interrupts_disable();
    479         spinlock_lock(&TASK->lock);
    480        
    481         main_thread = TASK->main_thread;
    482        
    483         /*
    484          * Find a thread to join.
    485          */
    486         again = false;
    487         for (cur = TASK->th_head.next; cur != &TASK->th_head; cur = cur->next) {
    488                 t = list_get_instance(cur, thread_t, th_link);
    489 
    490                 spinlock_lock(&t->lock);
    491                 if (t == THREAD) {
    492                         spinlock_unlock(&t->lock);
    493                         continue;
    494                 } else if (t == main_thread) {
    495                         spinlock_unlock(&t->lock);
    496                         continue;
    497                 } else if (t->join_type != None) {
    498                         spinlock_unlock(&t->lock);
    499                         again = true;
    500                         continue;
    501                 } else {
    502                         t->join_type = TaskClnp;
    503                         spinlock_unlock(&t->lock);
    504                         again = false;
    505                         break;
    506                 }
    507         }
    508        
    509         spinlock_unlock(&TASK->lock);
    510         interrupts_restore(ipl);
    511        
    512         if (again) {
    513                 /*
    514                  * Other cleanup (e.g. ktaskgc) is in progress.
    515                  */
    516                 scheduler();
    517                 goto loop;
    518         }
    519        
    520         if (t != THREAD) {
    521                 ASSERT(t != main_thread);       /* uninit is joined and detached
    522                                                  * in ktaskgc */
    523                 thread_join(t);
    524                 thread_detach(t);
    525                 goto loop;                      /* go for another thread */
    526         }
    527        
    528         /*
    529          * Now there are no other threads in this task
    530          * and no new threads can be created.
    531          */
    532 
    533         ipc_cleanup();
    534         futex_cleanup();
    535         klog_printf("Cleanup of task %llu completed.", TASK->taskid);
    536 }
    537 
    538 /** Kernel thread used to kill the userspace task when its main thread exits.
    539  *
    540  * This thread waits until the main userspace thread (i.e. uninit) exits.
    541  * When this happens, the task is killed. In the meantime, exited threads
    542  * are garbage collected.
    543  *
    544  * @param arg Pointer to the thread structure of the task's main thread.
    545  */
    546 void ktaskgc(void *arg)
    547 {
    548         thread_t *t = (thread_t *) arg;
    549 loop:   
    550         /*
    551          * Userspace threads cannot detach themselves,
    552          * therefore the thread pointer is guaranteed to be valid.
    553          */
    554         if (thread_join_timeout(t, 1000000, SYNCH_FLAGS_NONE) ==
    555             ESYNCH_TIMEOUT) {   /* sleep uninterruptibly here! */
    556                 ipl_t ipl;
    557                 link_t *cur;
    558                 thread_t *thr = NULL;
    559        
    560                 /*
    561                  * The join timed out. Try to do some garbage collection of
    562                  * Undead threads.
    563                  */
    564 more_gc:               
    565                 ipl = interrupts_disable();
    566                 spinlock_lock(&TASK->lock);
    567                
    568                 for (cur = TASK->th_head.next; cur != &TASK->th_head;
    569                     cur = cur->next) {
    570                         thr = list_get_instance(cur, thread_t, th_link);
    571                         spinlock_lock(&thr->lock);
    572                         if (thr != t && thr->state == Undead &&
    573                             thr->join_type == None) {
    574                                 thr->join_type = TaskGC;
    575                                 spinlock_unlock(&thr->lock);
    576                                 break;
    577                         }
    578                         spinlock_unlock(&thr->lock);
    579                         thr = NULL;
    580                 }
    581                 spinlock_unlock(&TASK->lock);
    582                 interrupts_restore(ipl);
    583                
    584                 if (thr) {
    585                         thread_join(thr);
    586                         thread_detach(thr);
    587                         scheduler();
    588                         goto more_gc;
    589                 }
    590                        
    591                 goto loop;
    592         }
    593         thread_detach(t);
    594         task_kill(TASK->taskid);
    595 }
    596 
    597455/** @}
    598456 */
Note: See TracChangeset for help on using the changeset viewer.