Files
haiku-beta6/src/system/kernel/condition_variable.cpp
T
Ingo Weinhold 020ac56840 * Fixed bug in the "scheduler" command: The check when a thread was
unscheduled was incorrect.
* Introduced _kern_analyze_scheduling() syscall. It requires scheduler
  kernel tracing to be enabled. It uses the tracing entries for a given
  period of time to do a similar analysis the "scheduler" debugger
  command does (i.e. number of runs, run time, latencies, preemption
  times) for each thread. Additionally the analysis includes for each
  thread how long the thread waited on each locking primitive in total.
* Added kernel tracing for the creation of semaphores and initialization
  of condition variables, mutexes, and rw locks. The enabling macro is
  SCHEDULING_ANALYSIS_TRACING. The only purpose is to provide
  _kern_analyze_scheduling() with more info on the locking primitives
  (the name in particular).


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27304 a95241bf-73f2-0310-859d-f6bbb57e9c96
2008-09-03 15:10:44 +00:00

351 lines
7.6 KiB
C++

/*
* 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>
#include <ksignal.h>
#include <int.h>
#include <scheduling_analysis.h>
#include <thread.h>
#include <util/AutoLock.h>
#define STATUS_ADDED 1
#define STATUS_WAITING 2
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 must be a direct pointer to a condition variable.
variable = (ConditionVariable*)address;
}
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)
{
ASSERT(object != NULL);
fThread = thread_get_current_thread();
InterruptsSpinLocker _(sConditionVariablesLock);
fVariable = sConditionVariableHash.Lookup(object);
if (fVariable == NULL) {
fWaitStatus = B_ENTRY_NOT_FOUND;
return false;
}
fWaitStatus = STATUS_ADDED;
fVariable->fEntries.Add(this);
return true;
}
status_t
ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
{
if (!are_interrupts_enabled()) {
panic("ConditionVariableEntry::Wait() called with interrupts "
"disabled, entry: %p, variable: %p", this, fVariable);
return B_ERROR;
}
InterruptsLocker _;
SpinLocker conditionLocker(sConditionVariablesLock);
if (fVariable == NULL)
return fWaitStatus;
thread_prepare_to_block(fThread, flags,
THREAD_BLOCK_TYPE_CONDITION_VARIABLE, fVariable);
fWaitStatus = STATUS_WAITING;
conditionLocker.Unlock();
SpinLocker threadLocker(gThreadSpinlock);
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();
conditionLocker.Lock();
// remove entry from variable, if not done yet
if (fVariable != NULL) {
fVariable->fEntries.Remove(this);
fVariable = NULL;
}
return error;
}
status_t
ConditionVariableEntry::Wait(const void* object, uint32 flags,
bigtime_t timeout)
{
if (Add(object))
return Wait(flags, timeout);
return B_ENTRY_NOT_FOUND;
}
inline void
ConditionVariableEntry::AddToVariable(ConditionVariable* variable)
{
fThread = thread_get_current_thread();
InterruptsSpinLocker _(sConditionVariablesLock);
fVariable = variable;
fWaitStatus = STATUS_ADDED;
fVariable->fEntries.Add(this);
}
// #pragma mark - ConditionVariable
/*! Initialization method for anonymous (unpublished) condition variables.
*/
void
ConditionVariable::Init(const void* object, const char* objectType)
{
fObject = object;
fObjectType = objectType;
new(&fEntries) EntryList;
T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType));
}
void
ConditionVariable::Publish(const void* object, const char* objectType)
{
ASSERT(object != NULL);
fObject = object;
fObjectType = objectType;
new(&fEntries) EntryList;
T_SCHEDULING_ANALYSIS(InitConditionVariable(this, object, objectType));
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 : &gThreadSpinlock);
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);
}
void
ConditionVariable::Add(ConditionVariableEntry* entry)
{
entry->AddToVariable(this);
}
status_t
ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
{
ConditionVariableEntry entry;
Add(&entry);
return entry.Wait(flags, timeout);
}
/*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)
{
InterruptsLocker _;
SpinLocker threadLocker(threadsLocked ? NULL : &gThreadSpinlock);
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;
if (entry->fWaitStatus <= 0)
continue;
if (entry->fWaitStatus == STATUS_WAITING)
thread_unblock_locked(entry->fThread, result);
entry->fWaitStatus = result;
if (!all)
break;
}
}
// #pragma mark -
void
condition_variable_init()
{
new(&sConditionVariableHash) ConditionVariableHash;
status_t error = sConditionVariableHash.Init(kConditionVariableHashSize);
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);
}