Files
haiku-beta6/src/system/kernel/condition_variable.cpp
T

331 lines
7.3 KiB
C++
Raw Normal View History

/*
* Copyright 2007-2008, Ingo Weinhold, [email protected].
* Distributed under the terms of the MIT License.
*/
#include <condition_variable.h>
#include <new>
#include <stdlib.h>
#include <string.h>
#include <debug.h>
#include <kscheduler.h>
2007-08-26 23:53:12 +00:00
#include <ksignal.h>
#include <int.h>
#include <thread.h>
#include <util/AutoLock.h>
2007-10-01 22:28:54 +00:00
static const int kConditionVariableHashSize = 512;
struct ConditionVariableHashDefinition {
typedef const void* KeyType;
typedef ConditionVariable ValueType;
size_t HashKey(const void* key) const
{ return (size_t)key; }
size_t Hash(ConditionVariable* variable) const
{ return (size_t)variable->fObject; }
bool Compare(const void* key, ConditionVariable* variable) const
{ return key == variable->fObject; }
HashTableLink<ConditionVariable>* GetLink(ConditionVariable* variable) const
{ return variable; }
};
typedef OpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash;
static ConditionVariableHash sConditionVariableHash;
static spinlock sConditionVariablesLock;
static int
list_condition_variables(int argc, char** argv)
{
ConditionVariable::ListAll();
return 0;
}
static int
dump_condition_variable(int argc, char** argv)
{
if (argc != 2) {
print_debugger_command_usage(argv[0]);
return 0;
}
addr_t address = parse_expression(argv[1]);
if (address == 0)
return 0;
ConditionVariable* variable = sConditionVariableHash.Lookup((void*)address);
if (variable == NULL) {
// It might be a direct pointer to a condition variable. Search the
// hash.
ConditionVariableHash::Iterator it(&sConditionVariableHash);
while (ConditionVariable* hashVariable = it.Next()) {
if (hashVariable == (void*)address) {
variable = hashVariable;
break;
}
}
}
if (variable != NULL) {
variable->Dump();
set_debug_variable("_cvar", (addr_t)variable);
set_debug_variable("_object", (addr_t)variable->Object());
} else
kprintf("no condition variable at or with key %p\n", (void*)address);
return 0;
}
// #pragma mark - ConditionVariableEntry
bool
ConditionVariableEntry::Add(const void* object, uint32 flags)
{
ASSERT(object != NULL);
fThread = thread_get_current_thread();
2008-04-22 21:46:23 +00:00
InterruptsSpinLocker _(sConditionVariablesLock);
fVariable = sConditionVariableHash.Lookup(object);
2008-04-22 21:46:23 +00:00
thread_prepare_to_block(fThread, flags,
THREAD_BLOCK_TYPE_CONDITION_VARIABLE, fVariable);
if (fVariable == NULL) {
SpinLocker threadLocker(thread_spinlock);
2008-04-22 21:46:23 +00:00
thread_unblock_locked(fThread, B_ENTRY_NOT_FOUND);
return false;
}
// add to the queue for the variable
fVariable->fEntries.Add(this);
return true;
}
status_t
2008-04-22 21:46:23 +00:00
ConditionVariableEntry::Wait(uint32 timeoutFlags, bigtime_t timeout)
{
if (!are_interrupts_enabled()) {
panic("wait_for_condition_variable_entry() called with interrupts "
"disabled");
return B_ERROR;
}
InterruptsLocker _;
SpinLocker threadLocker(thread_spinlock);
2008-04-22 21:46:23 +00:00
status_t error;
if ((timeoutFlags & (B_RELATIVE_TIMEOUT | B_ABSOLUTE_TIMEOUT)) != 0)
error = thread_block_with_timeout_locked(timeoutFlags, timeout);
else
error = thread_block_locked(thread_get_current_thread());
threadLocker.Unlock();
SpinLocker locker(sConditionVariablesLock);
2007-08-26 23:53:12 +00:00
// remove entry from variable, if not done yet
if (fVariable != NULL) {
fVariable->fEntries.Remove(this);
fVariable = NULL;
2008-02-17 14:38:23 +00:00
}
return error;
}
status_t
2008-04-22 21:46:23 +00:00
ConditionVariableEntry::Wait(const void* object, uint32 flags,
bigtime_t timeout)
{
if (Add(object, flags))
2008-04-22 21:46:23 +00:00
return Wait(flags, timeout);
return B_ENTRY_NOT_FOUND;
}
2008-04-22 21:46:23 +00:00
inline void
ConditionVariableEntry::AddToVariable(ConditionVariable* variable, uint32 flags)
{
fThread = thread_get_current_thread();
thread_prepare_to_block(fThread, flags,
THREAD_BLOCK_TYPE_CONDITION_VARIABLE, fVariable);
// add to the variable
InterruptsSpinLocker _(sConditionVariablesLock);
fVariable = variable;
fVariable->fEntries.Add(this);
}
// #pragma mark - ConditionVariable
2008-04-22 21:46:23 +00:00
/*! Initialization method for anonymous (unpublished) condition variables.
*/
void
ConditionVariable::Init(const void* object, const char* objectType)
{
fObject = object;
fObjectType = objectType;
new(&fEntries) EntryList;
}
void
ConditionVariable::Publish(const void* object, const char* objectType)
{
ASSERT(object != NULL);
fObject = object;
fObjectType = objectType;
new(&fEntries) EntryList;
InterruptsLocker _;
SpinLocker locker(sConditionVariablesLock);
ASSERT_PRINT(sConditionVariableHash.Lookup(object) == NULL,
"condition variable: %p\n", sConditionVariableHash.Lookup(object));
sConditionVariableHash.InsertUnchecked(this);
}
void
ConditionVariable::Unpublish(bool threadsLocked)
{
ASSERT(fObject != NULL);
InterruptsLocker _;
SpinLocker threadLocker(threadsLocked ? NULL : &thread_spinlock);
SpinLocker locker(sConditionVariablesLock);
#if KDEBUG
ConditionVariable* variable = sConditionVariableHash.Lookup(fObject);
if (variable != this) {
panic("Condition variable %p not published, found: %p", this, variable);
return;
}
#endif
sConditionVariableHash.RemoveUnchecked(this);
fObject = NULL;
fObjectType = NULL;
if (!fEntries.IsEmpty())
_NotifyChecked(true, B_ENTRY_NOT_FOUND);
}
2008-04-22 21:46:23 +00:00
void
ConditionVariable::Add(ConditionVariableEntry* entry, uint32 flags)
{
entry->AddToVariable(this, flags);
}
/*static*/ void
ConditionVariable::ListAll()
{
kprintf(" variable object (type) waiting threads\n");
kprintf("------------------------------------------------------------\n");
ConditionVariableHash::Iterator it(&sConditionVariableHash);
while (ConditionVariable* variable = it.Next()) {
// count waiting threads
int count = variable->fEntries.Size();
kprintf("%p %p %-20s %15d\n", variable, variable->fObject,
variable->fObjectType, count);
}
}
void
ConditionVariable::Dump() const
{
kprintf("condition variable %p\n", this);
kprintf(" object: %p (%s)\n", fObject, fObjectType);
kprintf(" threads:");
for (EntryList::ConstIterator it = fEntries.GetIterator();
ConditionVariableEntry* entry = it.Next();) {
kprintf(" %ld", entry->fThread->id);
}
kprintf("\n");
}
void
ConditionVariable::_Notify(bool all, bool threadsLocked)
{
ASSERT(fObject != NULL);
InterruptsLocker _;
SpinLocker threadLocker(threadsLocked ? NULL : &thread_spinlock);
SpinLocker locker(sConditionVariablesLock);
if (!fEntries.IsEmpty())
_NotifyChecked(all, B_OK);
}
/*! Called with interrupts disabled and the condition variable spinlock and
thread lock held.
*/
void
ConditionVariable::_NotifyChecked(bool all, status_t result)
{
// dequeue and wake up the blocked threads
while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
entry->fVariable = NULL;
thread_unblock_locked(entry->fThread, B_OK);
2007-08-26 23:53:12 +00:00
if (!all)
break;
}
}
2007-08-26 23:53:12 +00:00
// #pragma mark -
void
condition_variable_init()
{
new(&sConditionVariableHash) ConditionVariableHash(
kConditionVariableHashSize);
status_t error = sConditionVariableHash.InitCheck();
if (error != B_OK) {
panic("condition_variable_init(): Failed to init hash table: %s",
strerror(error));
}
add_debugger_command_etc("cvar", &dump_condition_variable,
"Dump condition variable info",
"<address>\n"
"Prints info for the specified condition variable.\n"
" <address> - Address of the condition variable or the object it is\n"
" associated with.\n", 0);
add_debugger_command_etc("cvars", &list_condition_variables,
"List condition variables",
"\n"
"Lists all existing condition variables\n", 0);
}