Author: bonefish Date: 2011-05-11 15:17:35 +0200 (Wed, 11 May 2011) New Revision: 41438 Changeset: https://dev.haiku-os.org/changeset/41438 Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/condition_variable.h haiku/branches/developer/bonefish/signals/headers/private/kernel/kscheduler.h haiku/branches/developer/bonefish/signals/headers/private/kernel/lock.h haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h haiku/branches/developer/bonefish/signals/src/system/kernel/condition_variable.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/debug/system_profiler.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/fs/Vnode.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/fs/fifo.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/locks/lock.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_message_queue.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_semaphore.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_affine.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple_smp.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/vm/VMCache.cpp haiku/branches/developer/bonefish/signals/src/system/kernel/vm/vm_page.cpp Log: Work in progress towards replacing gThreadSpinlock: * Introduced a global spinlock gSchedulerLock. It is supposed to take over the scheduler lock aspect of gThreadSpinlock. * Adjusted the schedulers, the thread blocking API, and the locking primitives to use gSchedulerLock instead of gThreadSpinlock. * Also adjusted code using the thread blocking API accordingly. * Added (extensive) documentation to the thread blocking API. * Added static Thread::Get() to get a thread (with reference) by ID. Compiles, but won't work before the transition is complete. Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/condition_variable.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/condition_variable.h 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/condition_variable.h 2011-05-11 13:17:35 UTC (rev 41438) @@ -56,18 +56,18 @@ void Publish(const void* object, const char* objectType); - void Unpublish(bool threadsLocked = false); + void Unpublish(bool schedulerLocked = false); - inline void NotifyOne(bool threadsLocked = false, + inline void NotifyOne(bool schedulerLocked = false, status_t result = B_OK); - inline void NotifyAll(bool threadsLocked = false, + inline void NotifyAll(bool schedulerLocked = false, status_t result = B_OK); static void NotifyOne(const void* object, - bool threadsLocked = false, + bool schedulerLocked = false, status_t result = B_OK); static void NotifyAll(const void* object, - bool threadsLocked = false, + bool schedulerLocked = false, status_t result = B_OK); // (both methods) caller must ensure that // the variable is not unpublished @@ -86,7 +86,7 @@ void Dump() const; private: - void _Notify(bool all, bool threadsLocked, + void _Notify(bool all, bool schedulerLocked, status_t result); void _NotifyLocked(bool all, status_t result); @@ -124,16 +124,16 @@ inline void -ConditionVariable::NotifyOne(bool threadsLocked, status_t result) +ConditionVariable::NotifyOne(bool schedulerLocked, status_t result) { - _Notify(false, threadsLocked, result); + _Notify(false, schedulerLocked, result); } inline void -ConditionVariable::NotifyAll(bool threadsLocked, status_t result) +ConditionVariable::NotifyAll(bool schedulerLocked, status_t result) { - _Notify(true, threadsLocked, result); + _Notify(true, schedulerLocked, result); } Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/kscheduler.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/kscheduler.h 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/kscheduler.h 2011-05-11 13:17:35 UTC (rev 41438) @@ -70,6 +70,7 @@ }; extern struct scheduler_ops* gScheduler; +extern spinlock gSchedulerLock; #define scheduler_enqueue_in_run_queue(thread) \ gScheduler->enqueue_in_run_queue(thread) @@ -104,7 +105,7 @@ /*! Reschedules, if necessary. - The thread spinlock must be held. + The caller must hold the scheduler lock (with disabled interrupts). */ static inline void scheduler_reschedule_if_necessary_locked() @@ -122,9 +123,11 @@ { if (are_interrupts_enabled()) { cpu_status state = disable_interrupts(); - GRAB_THREAD_LOCK(); + acquire_spinlock(&gSchedulerLock); + scheduler_reschedule_if_necessary_locked(); - RELEASE_THREAD_LOCK(); + + release_spinlock(&gSchedulerLock); restore_interrupts(state); } } Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/lock.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/lock.h 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/lock.h 2011-05-11 13:17:35 UTC (rev 41438) @@ -1,5 +1,5 @@ /* - * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Copyright 2008-2011, Ingo Weinhold, ingo_weinhold@xxxxxxx * Copyright 2002-2009, Axel Dörfler, axeld@xxxxxxxxxxxxxxxxx * Distributed under the terms of the MIT License. * @@ -144,11 +144,11 @@ extern status_t _rw_lock_read_lock(rw_lock* lock); extern status_t _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, bigtime_t timeout); -extern void _rw_lock_read_unlock(rw_lock* lock, bool threadsLocked); -extern void _rw_lock_write_unlock(rw_lock* lock, bool threadsLocked); +extern void _rw_lock_read_unlock(rw_lock* lock, bool schedulerLocked); +extern void _rw_lock_write_unlock(rw_lock* lock, bool schedulerLocked); -extern status_t _mutex_lock(mutex* lock, bool threadsLocked); -extern void _mutex_unlock(mutex* lock, bool threadsLocked); +extern status_t _mutex_lock(mutex* lock, bool schedulerLocked); +extern void _mutex_unlock(mutex* lock, bool schedulerLocked); extern status_t _mutex_trylock(mutex* lock); extern status_t _mutex_lock_with_timeout(mutex* lock, uint32 timeoutFlags, bigtime_t timeout); Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/thread.h 2011-05-11 13:17:35 UTC (rev 41438) @@ -135,20 +135,40 @@ #endif -/*! - \a thread must be the current thread. - Thread lock can be, but doesn't need to be held. +/*! Checks whether the current thread would immediately be interrupted when + blocking it with the given wait/interrupt flags. + + The caller can hold the scheduler lock, but doesn't have to. + + \param thread The current thread. + \param flags Wait/interrupt flags to be considered. Relevant are: + - \c B_CAN_INTERRUPT: The thread can be interrupted by any non-blocked + signal. Implies \c B_KILL_CAN_INTERRUPT (specified or not). + - \c B_KILL_CAN_INTERRUPT: The thread can be interrupted by a kill + signal. + \return \c true, if the thread would be interrupted, \c false otherwise. */ static inline bool thread_is_interrupted(Thread* thread, uint32 flags) { - return ((flags & B_CAN_INTERRUPT) + return ((flags & B_CAN_INTERRUPT) != 0 && (thread->sig_pending & ~thread->sig_block_mask) != 0) - || ((flags & B_KILL_CAN_INTERRUPT) - && (thread->sig_pending & KILL_SIGNALS)); + || ((flags & B_KILL_CAN_INTERRUPT) != 0 + && (thread->sig_pending & KILL_SIGNALS) != 0); } +/*! Checks wether the given thread is currently blocked (i.e. still waiting for + something). + + If a stable answer is required, the caller must hold the scheduler lock. + Alternatively, if waiting is not interruptible and cannot time out, holding + the client lock held when calling thread_prepare_to_block() and the + unblocking functions works as well. + + \param thread The thread in question. + \return \c true, if the thread is blocked, \c false otherwise. +*/ static inline bool thread_is_blocked(Thread* thread) { @@ -156,9 +176,109 @@ } -/*! - \a thread must be the current thread. - Thread lock can be, but doesn't need to be locked. +/*! Prepares the current thread for waiting. + + This is the first of two steps necessary to block the current thread + (IOW, to let it wait for someone else to unblock it or optionally time out + after a specified delay). The process consists of two steps to avoid race + conditions in case a lock other than the scheduler lock is involved. + + Usually the thread waits for some condition to change and this condition is + something reflected in the caller's data structures which should be + protected by a client lock the caller knows about. E.g. in the semaphore + code that lock is a per-semaphore spinlock that protects the semaphore data, + including the semaphore count and the queue of waiting threads. For certain + low-level locking primitives (e.g. mutexes) that client lock is the + scheduler lock itself, which simplifies things a bit. + + If a client lock other than the scheduler lock is used, this function must + be called with that lock being held. Afterwards that lock should be dropped + and the function that actually blocks the thread shall be invoked + (thread_block[_locked]() or thread_block_with_timeout[_locked]()). In + between these two steps no functionality that uses the thread blocking API + for this thread shall be used. + + When the caller determines that the condition for unblocking the thread + occurred, it calls thread_unblock[_locked]() to unblock the thread. At that + time one of locks that are held when calling thread_prepare_to_block() must + be held. Usually that would be the client lock. In two cases it generally + isn't, however, since the unblocking code doesn't know about the client + lock: 1. When thread_block_with_timeout[_locked]() had been used and the + timeout occurs. 2. When thread_prepare_to_block() had been called with one + or both of the \c B_CAN_INTERRUPT or \c B_KILL_CAN_INTERRUPT flags specified + and someone calls thread_interrupt() that is supposed to wake up the thread. + In either of these two cases only the scheduler lock is held by the + unblocking code. A timeout can only happen after + thread_block_with_timeout_locked() has been called, but an interruption is + possible at any time. The client code must deal with those situations. + + Generally blocking and unblocking threads proceed in the following manner: + + Blocking thread: + - Acquire client lock. + - Check client condition and decide whether blocking is necessary. + - Modify some client data structure to indicate that this thread is now + waiting. + - Release client lock (unless client lock is the scheduler lock). + - Block. + - Acquire client lock (unless client lock is the scheduler lock). + - Check client condition and compare with block result. E.g. if the wait was + interrupted or timed out, but the client condition indicates success, it + may be considered a success after all, since usually that happens when + another thread concurrently changed the client condition and also tried + to unblock the waiting thread. It is even necessary when that other + thread changed the client data structures in a way that associate some + resource with the unblocked thread, or otherwise the unblocked thread + would have to reverse that here. + - If still necessary -- i.e. not already taken care of by an unblocking + thread -- modify some client structure to indicate that the thread is no + longer waiting, so it isn't erroneously unblocked later. + + Unblocking thread: + - Acquire client lock. + - Check client condition and decide whether a blocked thread can be woken + up. + - Check the client data structure that indicates whether one or more threads + are waiting and which thread(s) need(s) to be woken up. + - Unblock respective thread(s). + - Possibly change some client structure, so that an unblocked thread can + decide whether a concurrent timeout/interruption can be ignored, or + simply so that it doesn't have to do any more cleanup. + + Note that in the blocking thread the steps after blocking are strictly + required only if timeouts or interruptions are possible. If they are not, + the blocking thread can only be woken up explicitly by an unblocking thread, + which could already take care of all the necessary client data structure + modifications, so that the blocking thread wouldn't have to do that. + + Note that the client lock can but does not have to be a spinlock. + A mutex, a semaphore, or anything that doesn't try to use the thread + blocking API for the calling thread when releasing the lock is fine. + In particular that means in principle thread_prepare_to_block() can be + called with interrupts enabled. + + Care must be taken when the wait can be interrupted or can time out, + especially with a client lock that uses the thread blocking API. After a + blocked thread has been interrupted or the the time out occurred it cannot + acquire the client lock (or any other lock using the thread blocking API) + without first making sure that the thread doesn't still appears to be + waiting to other client code. Otherwise another thread could try to unblock + it which could erroneously unblock the thread while already waiting on the + client lock. So usually when interruptions or timeouts are possible a + spinlock needs to be involved. + + \param thread The current thread. + \param flags The blocking flags. Relevant are: + - \c B_CAN_INTERRUPT: The thread can be interrupted by any non-blocked + signal. Implies \c B_KILL_CAN_INTERRUPT (specified or not). + - \c B_KILL_CAN_INTERRUPT: The thread can be interrupted by a kill + signal. + \param type The type of object the thread will be blocked at. Informative/ + for debugging purposes. Must be one of the \c THREAD_BLOCK_TYPE_* + constants. \c THREAD_BLOCK_TYPE_OTHER implies that \a object is a + string. + \param object The object the thread will be blocked at. Informative/for + debugging purposes. */ static inline void thread_prepare_to_block(Thread* thread, uint32 flags, uint32 type, @@ -173,11 +293,27 @@ } +/*! Blocks the current thread. + + The thread is blocked until someone else unblock it. Must be called after a + call to thread_prepare_to_block(). If the thread has already been unblocked + after the previous call to thread_prepare_to_block(), this function will + return immediately. Cf. the documentation of thread_prepare_to_block() for + more details. + + The caller must hold the scheduler lock. + + \param thread The current thread. + \return The error code passed to the unblocking function. thread_interrupt() + uses \c B_INTERRUPTED. By convention \c B_OK means that the wait was + successful while another error code indicates a failure (what that means + depends on the client code). +*/ static inline status_t thread_block_locked(Thread* thread) { if (thread->wait.status == 1) { - // check for signals, if interruptable + // check for signals, if interruptible if (thread_is_interrupted(thread, thread->wait.flags)) { thread->wait.status = B_INTERRUPTED; } else { @@ -190,6 +326,19 @@ } +/*! Unblocks the specified blocked thread. + + If the thread is no longer waiting (e.g. because thread_unblock_locked() has + already been called in the meantime), this function does not have any + effect. + + The caller must hold the scheduler lock and the client lock (might be the + same). + + \param thread The thread to be unblocked. + \param status The unblocking status. That's what the unblocked thread's + call to thread_block_locked() will return. +*/ static inline void thread_unblock_locked(Thread* thread, status_t status) { @@ -202,6 +351,29 @@ } +/*! Interrupts the specified blocked thread, if possible. + + The function checks whether the thread can be interrupted and, if so, calls + \code thread_unblock_locked(thread, B_INTERRUPTED) \endcode. Otherwise the + function is a no-op. + + The caller must hold the scheduler lock. Normally thread_unblock_locked() + also requires the client lock to be held, but in this case the caller + usually doesn't know it. This implies that the client code needs to take + special care, if waits are interruptible. See thread_prepare_to_block() for + more information. + + \param thread The thread to be interrupted. + \param kill If \c false, the blocked thread is only interrupted, when the + flag \c B_CAN_INTERRUPT was specified for the blocked thread. If + \c true, it is only interrupted, when at least one of the flags + \c B_CAN_INTERRUPT or \c B_KILL_CAN_INTERRUPT was specified for the + blocked thread. + \return \c B_OK, if the thread is interruptible and thread_unblock_locked() + was called, \c B_NOT_ALLOWED otherwise. \c B_OK doesn't imply that the + thread actually has been interrupted -- it could have been unblocked + before already. +*/ static inline status_t thread_interrupt(Thread* thread, bool kill) { Modified: haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h =================================================================== --- haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/headers/private/kernel/thread_types.h 2011-05-11 13:17:35 UTC (rev 41438) @@ -336,17 +336,17 @@ // enabled, etc.) Thread *all_next; Thread *team_next; - Thread *queue_next; /* i.e. run queue, release queue, etc. */ + Thread *queue_next; // protected by scheduler lock timer alarm; thread_id id; char name[B_OS_NAME_LENGTH]; - int32 priority; - int32 next_priority; + int32 priority; // protected by scheduler lock + int32 next_priority; // protected by scheduler lock int32 io_priority; - int32 state; - int32 next_state; - struct cpu_ent *cpu; - struct cpu_ent *previous_cpu; + int32 state; // protected by scheduler lock + int32 next_state; // protected by scheduler lock + struct cpu_ent *cpu; // protected by scheduler lock + struct cpu_ent *previous_cpu; // protected by scheduler lock int32 pinned_to_cpu; sigset_t sig_pending; @@ -358,8 +358,8 @@ bool signal_stack_enabled; bool in_kernel; - bool was_yielded; - struct scheduler_thread_data* scheduler_data; + bool was_yielded; // protected by scheduler lock + struct scheduler_thread_data* scheduler_data; // protected by scheduler lock struct user_thread* user_thread; // write-protected by global threads // spinlock, freely readable from the @@ -448,6 +448,8 @@ struct cpu_ent *cpu); ~Thread(); + static Thread* Get(thread_id id); + void* operator new(size_t size); void* operator new(size_t, void* pointer); void operator delete(void* pointer, size_t size); Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/condition_variable.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/condition_variable.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/condition_variable.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -1,5 +1,5 @@ /* - * Copyright 2007-2008, Ingo Weinhold, bonefish@xxxxxxxxxxxxxxxx + * Copyright 2007-2011, Ingo Weinhold, ingo_weinhold@xxxxxxx * Distributed under the terms of the MIT License. */ @@ -134,15 +134,16 @@ conditionLocker.Unlock(); - SpinLocker threadLocker(gThreadSpinlock); + SpinLocker schedulerLocker(gSchedulerLock); status_t error; if ((flags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) != 0) error = thread_block_with_timeout_locked(flags, timeout); else error = thread_block_locked(thread_get_current_thread()); - threadLocker.Unlock(); + schedulerLocker.Unlock(); + conditionLocker.Lock(); // remove entry from variable, if not done yet @@ -220,12 +221,12 @@ void -ConditionVariable::Unpublish(bool threadsLocked) +ConditionVariable::Unpublish(bool schedulerLocked) { ASSERT(fObject != NULL); InterruptsLocker _; - SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); + SpinLocker schedulerLocker(schedulerLocked ? NULL : &gSchedulerLock); SpinLocker locker(sConditionVariablesLock); #if KDEBUG @@ -262,7 +263,7 @@ /*static*/ void -ConditionVariable::NotifyOne(const void* object, bool threadsLocked, +ConditionVariable::NotifyOne(const void* object, bool schedulerLocked, status_t result) { InterruptsSpinLocker locker(sConditionVariablesLock); @@ -271,13 +272,13 @@ if (variable == NULL) return; - variable->NotifyOne(threadsLocked, result); + variable->NotifyOne(schedulerLocked, result); } /*static*/ void -ConditionVariable::NotifyAll(const void* object, - bool threadsLocked, status_t result) +ConditionVariable::NotifyAll(const void* object, bool schedulerLocked, + status_t result) { InterruptsSpinLocker locker(sConditionVariablesLock); ConditionVariable* variable = sConditionVariableHash.Lookup(object); @@ -285,7 +286,7 @@ if (variable == NULL) return; - variable->NotifyAll(threadsLocked, result); + variable->NotifyAll(schedulerLocked, result); } @@ -321,10 +322,10 @@ void -ConditionVariable::_Notify(bool all, bool threadsLocked, status_t result) +ConditionVariable::_Notify(bool all, bool schedulerLocked, status_t result) { InterruptsLocker _; - SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock); + SpinLocker schedulerLocker(schedulerLocked ? NULL : &gSchedulerLock); SpinLocker locker(sConditionVariablesLock); if (!fEntries.IsEmpty()) { @@ -339,7 +340,7 @@ /*! Called with interrupts disabled and the condition variable spinlock and - thread lock held. + scheduler lock held. */ void ConditionVariable::_NotifyLocked(bool all, status_t result) Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/debug/system_profiler.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/debug/system_profiler.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/debug/system_profiler.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -219,7 +219,11 @@ if (fWaitingProfilerThread != NULL && fBufferSize > fBufferCapacity / 2) { int cpu = smp_get_current_cpu(); fReentered[cpu] = true; + + InterruptsSpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(fWaitingProfilerThread, B_OK); + schedulerLocker.Unlock(); + fWaitingProfilerThread = NULL; fReentered[cpu] = false; } @@ -294,6 +298,7 @@ // inactive. InterruptsSpinLocker locker(fLock); if (fWaitingProfilerThread != NULL) { + InterruptsSpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(fWaitingProfilerThread, B_OK); fWaitingProfilerThread = NULL; } @@ -542,9 +547,12 @@ Thread* thread = thread_get_current_thread(); fWaitingProfilerThread = thread; + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + thread_prepare_to_block(thread, B_CAN_INTERRUPT, THREAD_BLOCK_TYPE_OTHER, "system profiler buffer"); + schedulerLocker.Unlock(); locker.Unlock(); status_t error = thread_block_with_timeout(B_RELATIVE_TIMEOUT, 1000000); Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/fs/Vnode.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/fs/Vnode.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/fs/Vnode.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -1,5 +1,5 @@ /* - * Copyright 2009, Ingo Weinhold, ingo_weinhold@xxxxxxx + * Copyright 2009-2011, Ingo Weinhold, ingo_weinhold@xxxxxxx * Distributed under the terms of the MIT License. */ @@ -88,6 +88,6 @@ atomic_and(&fFlags, ~kFlagsWaitingLocker); // and wake it up - InterruptsSpinLocker threadLocker(gThreadSpinlock); + InterruptsSpinLocker threadLocker(gSchedulerLock); thread_unblock_locked(waiter->thread, B_OK); } Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/fs/fifo.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/fs/fifo.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/fs/fifo.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -100,7 +100,7 @@ TRACE("ReadRequest %p::Notify(), fNotified %d\n", this, fNotified); if (!fNotified) { - SpinLocker threadLocker(gThreadSpinlock); + SpinLocker schedulerLocker(gSchedulerLock); thread_unblock_locked(fThread, status); fNotified = true; } Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/locks/lock.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/locks/lock.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/locks/lock.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -260,7 +260,7 @@ ? (char*)lock->name : NULL; // unblock all waiters - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if KDEBUG if (lock->waiters != NULL && thread_get_current_thread_id() @@ -296,7 +296,7 @@ status_t _rw_lock_read_lock(rw_lock* lock) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // We might be the writer ourselves. if (lock->holder == thread_get_current_thread_id()) { @@ -328,7 +328,7 @@ _rw_lock_read_lock_with_timeout(rw_lock* lock, uint32 timeoutFlags, bigtime_t timeout) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // We might be the writer ourselves. if (lock->holder == thread_get_current_thread_id()) { @@ -407,9 +407,9 @@ void -_rw_lock_read_unlock(rw_lock* lock, bool threadsLocked) +_rw_lock_read_unlock(rw_lock* lock, bool schedulerLocked) { - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); // If we're still holding the write lock or if there are other readers, // no-one can be woken up. @@ -437,7 +437,7 @@ status_t rw_lock_write_lock(rw_lock* lock) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // If we're already the lock holder, we just need to increment the owner // count. @@ -473,9 +473,9 @@ void -_rw_lock_write_unlock(rw_lock* lock, bool threadsLocked) +_rw_lock_write_unlock(rw_lock* lock, bool schedulerLocked) { - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); if (thread_get_current_thread_id() != lock->holder) { panic("rw_lock_write_unlock(): lock %p not write-locked by this thread", @@ -600,7 +600,7 @@ ? (char*)lock->name : NULL; // unblock all waiters - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if KDEBUG if (lock->waiters != NULL && thread_get_current_thread_id() @@ -631,7 +631,7 @@ status_t mutex_switch_lock(mutex* from, mutex* to) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if !KDEBUG if (atomic_add(&from->count, 1) < -1) @@ -645,7 +645,7 @@ status_t mutex_switch_from_read_lock(rw_lock* from, mutex* to) { - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); #if KDEBUG_RW_LOCK_DEBUG _rw_lock_write_unlock(from, true); @@ -660,17 +660,17 @@ status_t -_mutex_lock(mutex* lock, bool threadsLocked) +_mutex_lock(mutex* lock, bool schedulerLocked) { #if KDEBUG - if (!gKernelStartup && !threadsLocked && !are_interrupts_enabled()) { + if (!gKernelStartup && !schedulerLocked && !are_interrupts_enabled()) { panic("_mutex_lock(): called with interrupts disabled for lock %p", lock); } #endif // lock only, if !threadsLocked - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); // Might have been released after we decremented the count, but before // we acquired the spinlock. @@ -716,10 +716,10 @@ void -_mutex_unlock(mutex* lock, bool threadsLocked) +_mutex_unlock(mutex* lock, bool schedulerLocked) { // lock only, if !threadsLocked - InterruptsSpinLocker locker(gThreadSpinlock, false, !threadsLocked); + InterruptsSpinLocker locker(gSchedulerLock, false, !schedulerLocked); #if KDEBUG if (thread_get_current_thread_id() != lock->holder) { @@ -768,7 +768,7 @@ _mutex_trylock(mutex* lock) { #if KDEBUG - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); if (lock->holder <= 0) { lock->holder = thread_get_current_thread_id(); @@ -789,7 +789,7 @@ } #endif - InterruptsSpinLocker locker(gThreadSpinlock); + InterruptsSpinLocker locker(gSchedulerLock); // Might have been released after we decremented the count, but before // we acquired the spinlock. Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_message_queue.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_message_queue.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_message_queue.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -126,7 +126,7 @@ // Unlock the queue before blocking queueLocker->Unlock(); - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to // interruption, we will still be queued. A WakeUpThread() at this point will // call thread_unblock() and might thus screw with our trying to re-lock the @@ -246,7 +246,8 @@ void WakeUpThread(bool waitForMessage) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + if (waitForMessage) { // Wake up all waiting thread for a message // TODO: this can cause starvation for any @@ -399,7 +400,8 @@ // Wake up any threads still waiting if (fThreadsWaitingToSend || fThreadsWaitingToReceive) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + while (queued_thread *entry = fWaitingToReceive.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, EIDRM); Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_semaphore.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_semaphore.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/posix/xsi_semaphore.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -101,7 +101,8 @@ { // For some reason the semaphore is getting destroyed. // Wake up any remaing awaiting threads - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); + while (queued_thread *entry = fWaitingToIncreaseQueue.RemoveHead()) { entry->queued = false; thread_unblock_locked(entry->thread, EIDRM); @@ -143,7 +144,7 @@ // Unlock the set before blocking setLocker->Unlock(); - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); // TODO: We've got a serious race condition: If BlockAndUnlock() returned due to // interruption, we will still be queued. A WakeUpThread() at this point will // call thread_unblock() and might thus screw with our trying to re-lock the @@ -217,7 +218,7 @@ void WakeUpThread(bool waitingForZero) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker schedulerLocker(gSchedulerLock); if (waitingForZero) { // Wake up all threads waiting on zero while (queued_thread *entry = fWaitingToBeZeroQueue.RemoveHead()) { Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -16,6 +16,7 @@ struct scheduler_ops* gScheduler; +spinlock gSchedulerLock = B_SPINLOCK_INITIALIZER; SchedulerListenerList gSchedulerListeners; static void (*sRescheduleFunction)(void); @@ -106,12 +107,19 @@ { syscall_64_bit_return_value(); - InterruptsSpinLocker locker(gThreadSpinlock); + // get the thread + Thread* thread; + if (id < 0) { + thread = thread_get_current_thread(); + thread->AcquireReference(); + } else { + thread = Thread::Get(id); + if (thread == NULL) + return 0; + } + BReference<Thread> threadReference(thread, true); - Thread* thread = id < 0 - ? thread_get_current_thread() : thread_get_thread_struct_locked(id); - if (thread == NULL) - return 0; - + // ask the scheduler for the thread's latency + InterruptsSpinLocker locker(gSchedulerLock); return gScheduler->estimate_max_scheduling_latency(thread); } Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_affine.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_affine.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_affine.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -569,11 +569,9 @@ static void affine_start(void) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); affine_reschedule(); - - RELEASE_THREAD_LOCK(); } Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -385,11 +385,9 @@ static void simple_start(void) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); simple_reschedule(); - - RELEASE_THREAD_LOCK(); } Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple_smp.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple_smp.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/scheduler/scheduler_simple_smp.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -484,11 +484,9 @@ static void start(void) { - GRAB_THREAD_LOCK(); + SpinLocker schedulerLocker(gSchedulerLock); reschedule(); - - RELEASE_THREAD_LOCK(); } Modified: haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp =================================================================== --- haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp 2011-05-11 11:24:15 UTC (rev 41437) +++ haiku/branches/developer/bonefish/signals/src/system/kernel/thread.cpp 2011-05-11 13:17:35 UTC (rev 41438) @@ -252,6 +252,17 @@ } +/*static*/ Thread* +Thread::Get(thread_id id) +{ + InterruptsSpinLocker threadHashLocker(gThreadSpinlock); + Thread* thread = sThreadHash.Lookup(id); + if (thread != NULL) + thread->AcquireReference(); + return thread; +} + + void* Thread::operator new(size_t size) { @@ -2059,7 +2070,7 @@ RELEASE_THREAD_LOCK(); restore_interrupts(state); - // If the thread is already gone, we need to wait uninterruptably for + // If the thread is already gone, we need to wait uninterruptibly for // its exit semaphore to make sure our death entry stays valid -- it // won't take long. if (thread == NULL) @@ -2350,18 +2361,31 @@ } +/*! Blocks the current thread. + + The function acquires the scheduler lock and calls thread_block_locked(). + See there for more information. +*/ status_t thread_block() { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); return thread_block_locked(thread_get_current_thread()); } +/*! Unblocks the thread specified by the given ID. + + Looks up the thread with the given, acquires the scheduler lock, and calls + thread_unblock_locked(). See there for more information. + + Interrupts must be enabled. The client lock must be held (so, obviously, + the client lock cannot be the scheduler lock or any other spinlock). +*/ void thread_unblock(status_t threadID, status_t status) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); Thread* thread = thread_get_thread_struct_locked(threadID); if (thread != NULL) @@ -2369,14 +2393,46 @@ } +/*! Blocks the current thread with a timeout. + + Acquires the scheduler lock and calls thread_block_with_timeout_locked(). + See there for more information. +*/ status_t thread_block_with_timeout(uint32 timeoutFlags, bigtime_t timeout) { - InterruptsSpinLocker _(gThreadSpinlock); + InterruptsSpinLocker _(gSchedulerLock); return thread_block_with_timeout_locked(timeoutFlags, timeout); } +/*! Blocks the current thread with a timeout. + + The thread is blocked until someone else unblock it or the specified timeout + occurs. Must be called after a call to thread_prepare_to_block(). If the + thread has already been unblocked after the previous call to + thread_prepare_to_block(), this function will return immediately. See + thread_prepare_to_block() for more details. + + The caller must hold the scheduler lock. + + \param thread The current thread. + \param timeoutFlags The standard timeout flags: + - \c B_RELATIVE_TIMEOUT: \a timeout specifies the time to wait. + - \c B_ABSOLUTE_TIMEOUT: \a timeout specifies the absolute end time when + the timeout shall occur. + - \c B_TIMEOUT_REAL_TIME_BASE: Only relevant when \c B_ABSOLUTE_TIMEOUT + is specified, too. Specifies that \a timeout is a real time, not a + system time. + If neither \c B_RELATIVE_TIMEOUT nor \c B_ABSOLUTE_TIMEOUT are + specified, an infinite timeout is implied and the function behaves like + thread_block_locked(). [... truncated: 107 lines follow ...]