[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