[haiku-commits] r41438 - in haiku/branches/developer/bonefish/signals: headers/private/kernel src/system/kernel src/system/kernel/debug src/system/kernel/fs src/system/kernel/locks ...

  • From: ingo_weinhold@xxxxxx
  • To: haiku-commits@xxxxxxxxxxxxx
  • Date: Wed, 11 May 2011 15:17:36 +0200 (CEST)

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 ...]

Other related posts:

  • » [haiku-commits] r41438 - in haiku/branches/developer/bonefish/signals: headers/private/kernel src/system/kernel src/system/kernel/debug src/system/kernel/fs src/system/kernel/locks ... - ingo_weinhold