* Introduced a set of functions (thread_prepare_to_block(),

thread_block(), thread_unblock(),...) that allow a thread to wait for
  something without needing a semaphore or condition variable. It can
  simply block and another thread can unblock it. Supports timeouts and
  interrupting. Both semaphores and condition variables use this
  common mechanism, now.
* Semaphores:
  - Some simplifications due to the thread blocking mechanism.
  - Changed locking order to sem -> thread. It was the other way around
    before and when introducing the wait_for_objects() support I had
    also introduced a situation where the locking was reverse, which
    could potentially cause a dead lock on SMP systems.
  - Instead of queueing thread structures, a semaphore queues
    queued_thread entries now, which are created on the stack. The
    thread::sem structure could thus be removed.
  - Added sem_entry::net_count, which is sem_entry::count plus the
    acquisition count of all waiting threads. This number is needed in
    remove_thread_from_sem() and instead of computing it there we
    maintain it.
  - Fixed remove_thread_from_sem(). It would not unblock threads, if
    the sem count was <= 0.
  - Made sem::last_acquirer unconditional. It is actually needed for
    sem_info::latest_holder. Fixed fill_sem_info() accordingly.
  - Added some optional tracing output, though only via ktrace_printf().
* Condition variables:
  - Could be simplified significantly through the use of the thread
    blocking mechanism. Removed a good deal of unnecessary code.
  - Moved the ConditionVariableEntry "flags" parameter from Wait() to
    Add(), and adjusted all places where condition variables are used
    accordingly.
* snooze() uses thread_block_with_timeout() instead of a semaphore.
* Simplified thread interrupting in the signal and user debugger code.
  Instead of separate functions for threads waiting on a semaphore or
  condititon variable, we only have a single thread_interrupt(), now.



git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@25099 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-04-22 16:22:42 +00:00
parent e01cebeb0a
commit b95f6d4710
12 changed files with 492 additions and 551 deletions
+31 -114
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2007, Ingo Weinhold, [email protected].
* Copyright 2007-2008, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
@@ -92,30 +92,36 @@ dump_condition_variable(int argc, char** argv)
bool
PrivateConditionVariableEntry::Add(const void* object)
PrivateConditionVariableEntry::Add(const void* object, uint32 flags)
{
ASSERT(object != NULL);
fThread = thread_get_current_thread();
fFlags = 0;
fResult = B_OK;
InterruptsLocker _;
SpinLocker locker(sConditionVariablesLock);
// add to the queue for the variable
fVariable = sConditionVariableHash.Lookup(object);
if (fVariable)
fVariable->fEntries.Add(this);
else
fResult = B_ENTRY_NOT_FOUND;
return (fVariable != NULL);
struct thread* thread = thread_get_current_thread();
thread_prepare_to_block(thread, flags, THREAD_BLOCK_TYPE_CONDITION_VARIABLE,
fVariable);
if (fVariable == NULL) {
SpinLocker threadLocker(thread_spinlock);
thread_unblock_locked(thread, B_ENTRY_NOT_FOUND);
return false;
}
// add to the queue for the variable
fVariable->fEntries.Add(this);
return true;
}
status_t
PrivateConditionVariableEntry::Wait(uint32 flags)
PrivateConditionVariableEntry::Wait()
{
if (!are_interrupts_enabled()) {
panic("wait_for_condition_variable_entry() called with interrupts "
@@ -124,77 +130,32 @@ PrivateConditionVariableEntry::Wait(uint32 flags)
}
InterruptsLocker _;
SpinLocker threadLocker(thread_spinlock);
SpinLocker locker(sConditionVariablesLock);
// check whether this entry has already been notified
// (set the flags while at it)
if (fVariable == NULL)
return fResult;
fFlags = flags;
// When interruptable, check pending signals first
struct thread* thread = thread_get_current_thread();
if (((flags & B_CAN_INTERRUPT)
&& (thread->sig_pending & ~thread->sig_block_mask) != 0)
|| ((flags & B_KILL_CAN_INTERRUPT)
&& (thread->sig_pending & KILL_SIGNALS))) {
// remove entry from the variables
_Remove();
return B_INTERRUPTED;
}
// wait
thread->next_state = B_THREAD_WAITING;
thread->condition_variable_entry = this;
thread->sem.blocking = -1;
locker.Unlock();
scheduler_reschedule();
status_t error = thread_block_locked(thread_get_current_thread());
threadLocker.Unlock();
return fResult;
SpinLocker locker(sConditionVariablesLock);
// remove entry from variable, if not done yet
if (fVariable != NULL) {
fVariable->fEntries.Remove(this);
fVariable = NULL;
}
return error;
}
status_t
PrivateConditionVariableEntry::Wait(const void* object, uint32 flags)
{
if (Add(object))
return Wait(flags);
if (Add(object, flags))
return Wait();
return B_ENTRY_NOT_FOUND;
}
/*! Removes the entry from its variable.
Interrupts must be disabled, sConditionVariablesLock must be held.
*/
void
PrivateConditionVariableEntry::_Remove()
{
if (fVariable) {
fVariable->fEntries.Remove(this);
fVariable = NULL;
}
}
class PrivateConditionVariableEntry::Private {
public:
inline Private(PrivateConditionVariableEntry& entry)
: fEntry(entry)
{
}
inline uint32 Flags() const { return fEntry.fFlags; }
inline void Remove() const { fEntry._Remove(); }
inline void SetResult(status_t result) { fEntry.fResult = result; }
private:
PrivateConditionVariableEntry& fEntry;
};
// #pragma mark - PrivateConditionVariable
@@ -306,15 +267,7 @@ PrivateConditionVariable::_Notify(bool all, status_t result)
while (PrivateConditionVariableEntry* entry = fEntries.RemoveHead()) {
entry->fVariable = NULL;
struct thread* thread = entry->fThread;
if (thread->condition_variable_entry != NULL)
thread->condition_variable_entry->fResult = result;
// wake up the thread
thread->condition_variable_entry = NULL;
if (thread->state == B_THREAD_WAITING)
scheduler_enqueue_in_run_queue(thread);
thread_unblock_locked(entry->fThread, B_OK);
if (!all)
break;
@@ -325,41 +278,6 @@ PrivateConditionVariable::_Notify(bool all, status_t result)
// #pragma mark -
/*! Interrupts must be disabled, thread lock must be held.
*/
status_t
condition_variable_interrupt_thread(struct thread* thread)
{
SpinLocker locker(sConditionVariablesLock);
if (thread == NULL || thread->state != B_THREAD_WAITING
|| thread->condition_variable_entry == NULL) {
return B_BAD_VALUE;
}
PrivateConditionVariableEntry* entry = thread->condition_variable_entry;
uint32 flags = PrivateConditionVariableEntry::Private(*entry).Flags();
// interruptable?
if ((flags & B_CAN_INTERRUPT) == 0
&& ((flags & B_KILL_CAN_INTERRUPT) == 0
|| (thread->sig_pending & KILL_SIGNALS) == 0)) {
return B_NOT_ALLOWED;
}
PrivateConditionVariableEntry::Private(*entry).SetResult(B_INTERRUPTED);
// remove entry from its variable
PrivateConditionVariableEntry::Private(*entry).Remove();
// wake up the thread
thread->condition_variable_entry = NULL;
scheduler_enqueue_in_run_queue(thread);
return B_OK;
}
void
condition_variable_init()
{
@@ -383,4 +301,3 @@ condition_variable_init()
"\n"
"Lists all existing condition variables\n", 0);
}