Changeset 17652

Show
Ignore:
Timestamp:
05/30/06 10:17:09 (2 years ago)
Author:
axeld
Message:

* Fixed a big race condition that could leave threads waiting infinitely and

let them eat death stack entries: after setting the next thread state to
THREAD_STATE_FREE_ON_RESCHED, interrupts were enabled again, which could
cause the thread to be rescheduled before having called put_death_stack().
This fixes bug #434.

* Note that the above change pretty much reverts revision 7865 that was supposed

to fix interrupt problem on thread exit (patch by Jack Burton almost 2 years
ago, that's how long this problem existed!).

* Made get_death_stack() and put_death_stack() symmetrical in that they don't

change interrupts. Also pulled out rescheduling from put_death_stack[_and_reschedule]()
and put it back into thread_exit2().

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • haiku/trunk/src/system/kernel/thread.c

    r17338 r17652  
    77 */ 
    88 
    9 /* Threading routines */ 
     9/** Threading routines */ 
    1010 
    1111#include <OS.h> 
     
    4343#define THREAD_MAX_MESSAGE_SIZE         65536 
    4444 
    45 static status_t receive_data_etc(thread_id *_sender, void *buffer, size_t bufferSize, int32 flags); 
     45// used to pass messages between thread_exit and thread_exit2 
     46 
     47struct thread_exit_args { 
     48        struct thread   *thread; 
     49        area_id                 old_kernel_stack; 
     50        uint32                  death_stack; 
     51        sem_id                  death_sem; 
     52        team_id                 original_team_id; 
     53}; 
    4654 
    4755struct thread_key { 
    4856        thread_id id; 
    4957}; 
     58 
     59static status_t receive_data_etc(thread_id *_sender, void *buffer, 
     60        size_t bufferSize, int32 flags); 
    5061 
    5162// global 
     
    747758 
    748759/** Finds a free death stack for us and allocates it. 
    749  *      Leaves interrupts disabled and returns the former interrupt state. 
     760 *      Must be called with interrupts enabled. 
    750761 */ 
    751762 
    752 static cpu_status 
    753 get_death_stack(uint32 *_stack) 
     763static uint32 
     764get_death_stack(void) 
    754765{ 
    755766        cpu_status state; 
     
    768779        RELEASE_THREAD_LOCK(); 
    769780 
     781        restore_interrupts(state); 
     782 
    770783        // sanity checks 
    771784        if (!bit) 
     
    782795        TRACE(("get_death_stack: returning 0x%lx\n", sDeathStacks[i].address)); 
    783796 
    784         *_stack = (uint32)i; 
    785         return state; 
    786 } 
    787  
     797        return (uint32)i; 
     798} 
     799 
     800 
     801/**     Returns the thread's death stack to the pool. 
     802 */ 
    788803 
    789804static void 
    790 put_death_stack_and_reschedule(uint32 index) 
    791 { 
     805put_death_stack(uint32 index) 
     806{ 
     807        cpu_status state; 
     808 
    792809        TRACE(("put_death_stack...: passed %lu\n", index)); 
    793810 
     
    798815                panic("put_death_stack: passed invalid stack index %d\n", index); 
    799816 
    800         disable_interrupts(); 
     817        state = disable_interrupts(); 
     818 
    801819        GRAB_THREAD_LOCK(); 
    802820        sDeathStackBitmap &= ~(1 << index); 
    803821        RELEASE_THREAD_LOCK(); 
    804822 
     823        restore_interrupts(state); 
     824 
    805825        release_sem_etc(sDeathStackSem, 1, B_DO_NOT_RESCHEDULE); 
    806826                // we must not have acquired the thread lock when releasing a semaphore 
    807  
    808         GRAB_THREAD_LOCK(); 
    809         scheduler_reschedule(); 
    810                 // requires thread lock to be held 
    811 } 
    812  
    813  
    814 // used to pass messages between thread_exit and thread_exit2 
    815  
    816 struct thread_exit_args { 
    817         struct thread   *thread; 
    818         area_id                 old_kernel_stack; 
    819         cpu_status              int_state; 
    820         uint32                  death_stack; 
    821         sem_id                  death_sem; 
    822         team_id                 original_team_id; 
    823 }; 
     827} 
    824828 
    825829 
     
    857861        RELEASE_THREAD_LOCK(); 
    858862 
    859         // restore former thread interrupts (doesn't matter much at this point anyway) 
    860         restore_interrupts(args.int_state); 
     863        enable_interrupts(); 
     864                // needed for the debugger notification below 
    861865 
    862866        TRACE(("thread_exit2: done removing thread from lists\n")); 
     
    864868        if (args.death_sem >= 0) 
    865869                release_sem_etc(args.death_sem, 1, B_DO_NOT_RESCHEDULE);         
    866  
    867         // set the next state to be gone. Will return the thread structure to a ready pool upon reschedule 
    868         args.thread->next_state = THREAD_STATE_FREE_ON_RESCHED; 
    869870 
    870871        // notify the debugger 
     
    874875        } 
    875876 
     877        disable_interrupts(); 
     878 
     879        // Set the next state to be gone: this will cause the thread structure 
     880        // to be returned to a ready pool upon reschedule. 
     881        // Note, we need to have disabled interrupts at this point, or else 
     882        // we could get rescheduled too early. 
     883        args.thread->next_state = THREAD_STATE_FREE_ON_RESCHED; 
     884 
    876885        // return the death stack and reschedule one last time 
    877         put_death_stack_and_reschedule(args.death_stack); 
     886 
     887        put_death_stack(args.death_stack); 
     888 
     889        GRAB_THREAD_LOCK(); 
     890        scheduler_reschedule(); 
     891                // requires thread lock to be held 
    878892 
    879893        // never get to here 
     
    892906        thread_id mainParentThread = -1; 
    893907        bool deleteTeam = false; 
    894         uint32 death_stack; 
    895908        sem_id cachedDeathSem = -1, parentDeadSem = -1, groupDeadSem = -1; 
    896909        status_t status; 
     
    10531066                struct thread_exit_args args; 
    10541067 
    1055                 args.int_state = get_death_stack(&death_stack); 
    1056                         // this disables interrups for us 
    1057  
    10581068                args.thread = thread; 
    10591069                args.old_kernel_stack = thread->kernel_stack_area; 
    1060                 args.death_stack = death_stack; 
     1070                args.death_stack = get_death_stack(); 
    10611071                args.death_sem = cachedDeathSem; 
    10621072                args.original_team_id = teamID; 
    10631073 
    1064                 // set the new kernel stack officially to the death stack, wont be really switched until 
    1065                 // the next function is called. This bookkeeping must be done now before a context switch 
    1066                 // happens, or the processor will interrupt to the old stack 
    1067                 thread->kernel_stack_area = sDeathStacks[death_stack].area; 
    1068                 thread->kernel_stack_base = sDeathStacks[death_stack].address; 
     1074                disable_interrupts(); 
     1075 
     1076                // set the new kernel stack officially to the death stack, it won't be 
     1077                // switched until the next function is called. This must be done now 
     1078                // before a context switch, or we'll stay on the old stack 
     1079                thread->kernel_stack_area = sDeathStacks[args.death_stack].area; 
     1080                thread->kernel_stack_base = sDeathStacks[args.death_stack].address; 
    10691081 
    10701082                // we will continue in thread_exit2(), on the new stack