From 227402f2bfd4187053a07abd674464cf88885a3d Mon Sep 17 00:00:00 2001 From: Ingo Weinhold Date: Thu, 9 Aug 2007 20:03:17 +0000 Subject: [PATCH] Added simple condition variables to the kernel. They are a relatively cheap means to block threads until notified explicitely. threads git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@21874 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- headers/private/kernel/condition_variable.h | 151 +++++++ src/system/kernel/Jamfile | 1 + src/system/kernel/condition_variable.cpp | 427 ++++++++++++++++++++ 3 files changed, 579 insertions(+) create mode 100644 headers/private/kernel/condition_variable.h create mode 100644 src/system/kernel/condition_variable.cpp diff --git a/headers/private/kernel/condition_variable.h b/headers/private/kernel/condition_variable.h new file mode 100644 index 0000000000..919a3bddb0 --- /dev/null +++ b/headers/private/kernel/condition_variable.h @@ -0,0 +1,151 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_CONDITION_VARIABLE_H +#define _KERNEL_CONDITION_VARIABLE_H + + +#include + +#ifdef __cplusplus + +#include + + +class PrivateConditionVariable; + +class PrivateConditionVariableEntry { +protected: + bool Add(const void* object); + void Wait(); + void Wait(const void* object); + +protected: + PrivateConditionVariableEntry* fNext; + PrivateConditionVariable* fVariable; + struct thread* fThread; + + friend class PrivateConditionVariable; +}; + + +struct PrivateConditionVariable + : protected HashTableLink { +public: + static void ListAll(); + void Dump(); + +protected: + void Publish(const void* object, + const char* objectType); + void Unpublish(); + void Notify(); + +private: + void _Notify(); + +protected: + const void* fObject; + const char* fObjectType; + PrivateConditionVariableEntry* fEntries; + + friend class PrivateConditionVariableEntry; + friend class ConditionVariableHashDefinition; +}; + + +template +class ConditionVariable : private PrivateConditionVariable { +public: + inline void Publish(const Type* object, + const char* objectType); + + inline void Unpublish(); + inline void Notify(); +}; + + +template +class ConditionVariableEntry : private PrivateConditionVariableEntry { +public: + inline bool Add(const Type* object); + inline void Wait(); + inline void Wait(const Type* object); + +private: + bool fAdded; +}; + + +template +inline void +ConditionVariable::Publish(const Type* object, const char* objectType) +{ + PrivateConditionVariable::Publish(object, objectType); +} + + +template +inline void +ConditionVariable::Unpublish() +{ + PrivateConditionVariable::Unpublish(); +} + + +template +inline void +ConditionVariable::Notify() +{ + PrivateConditionVariable::Notify(); +} + + +template +inline bool +ConditionVariableEntry::Add(const Type* object) +{ + return PrivateConditionVariableEntry::Add(object); +} + + +template +inline void +ConditionVariableEntry::Wait() +{ + PrivateConditionVariableEntry::Wait(); +} + + +template +inline void +ConditionVariableEntry::Wait(const Type* object) +{ + PrivateConditionVariableEntry::Wait(object); +} + + +#if 0 +extern void publish_stack_condition_variable(condition_variable* variable, + const void* object, const char* objectType); +extern void unpublish_condition_variable(const void* object); +extern void notify_condition_variable(const void* object); + +extern void wait_for_condition_variable(const void* object); +extern bool add_condition_variable_entry(const void* object, + condition_variable_entry* entry); +extern void wait_for_condition_variable_entry( + condition_variable_entry* entry); +#endif // 0 + +extern "C" { +#endif // __cplusplus + +extern void condition_variable_init(); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* _KERNEL_CONDITION_VARIABLE_H */ diff --git a/src/system/kernel/Jamfile b/src/system/kernel/Jamfile index 96ca4d3746..8f6e92c195 100644 --- a/src/system/kernel/Jamfile +++ b/src/system/kernel/Jamfile @@ -16,6 +16,7 @@ AddResources kernel_$(TARGET_ARCH) : kernel.rdef ; KernelMergeObject kernel_core.o : boot_item.cpp + condition_variable.cpp cpu.c elf.cpp heap.c diff --git a/src/system/kernel/condition_variable.cpp b/src/system/kernel/condition_variable.cpp new file mode 100644 index 0000000000..b69d7756b2 --- /dev/null +++ b/src/system/kernel/condition_variable.cpp @@ -0,0 +1,427 @@ +/* + * Copyright 2007, Ingo Weinhold, bonefish@cs.tu-berlin.de. + * Distributed under the terms of the MIT License. + */ + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include + + +static const int kConditionVariableHashSize = 64; + + +struct ConditionVariableHashDefinition { + typedef const void* KeyType; + typedef PrivateConditionVariable ValueType; + + size_t HashKey(const void* key) const + { return (size_t)key; } + size_t Hash(PrivateConditionVariable* variable) const + { return (size_t)variable->fObject; } + bool Compare(const void* key, PrivateConditionVariable* variable) const + { return key == variable->fObject; } + HashTableLink* GetLink( + PrivateConditionVariable* variable) const + { return variable; } +}; + +typedef OpenHashTable ConditionVariableHash; +static ConditionVariableHash sConditionVariableHash; +static spinlock sConditionVariablesLock; + + +static int +list_condition_variables(int argc, char** argv) +{ + PrivateConditionVariable::ListAll(); + return 0; +} + + +static int +dump_condition_variable(int argc, char** argv) +{ + if (argc < 2 || strlen(argv[1]) < 2 + || argv[1][0] != '0' + || argv[1][1] != 'x') { + kprintf("%s: invalid argument, pass address\n", argv[0]); + return 0; + } + + addr_t address = strtoul(argv[1], NULL, 0); + if (address == NULL) + return 0; + + PrivateConditionVariable* 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 (PrivateConditionVariable* hashVariable = it.Next()) { + if (hashVariable == (void*)address) { + variable = hashVariable; + break; + } + } + } + + if (variable != NULL) + variable->Dump(); + else + kprintf("no condition variable at or with key %p\n", (void*)address); + + return 0; +} + + +// #pragma mark - PrivateConditionVariableEntry + + +bool +PrivateConditionVariableEntry::Add(const void* object) +{ + ASSERT(object != NULL); + + fThread = thread_get_current_thread(); + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + fVariable = sConditionVariableHash.Lookup(object); + if (fVariable) { + fNext = fVariable->fEntries; + fVariable->fEntries = this; + } + + return (fVariable != NULL); +} + + +void +PrivateConditionVariableEntry::Wait() +{ + if (!are_interrupts_enabled()) { + panic("wait_for_condition_variable_entry() called with interrupts " + "disabled"); + return; + } + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + if (fVariable != NULL) { + struct thread* thread = thread_get_current_thread(); + thread->next_state = B_THREAD_WAITING; + thread->condition_variable = fVariable; + thread->sem.blocking = -1; + + GRAB_THREAD_LOCK(); + locker.Unlock(); + scheduler_reschedule(); + RELEASE_THREAD_LOCK(); + } +} + + +void +PrivateConditionVariableEntry::Wait(const void* object) +{ + if (Add(object)) + Wait(object); +} + + +// #pragma mark - PrivateConditionVariable + + +/*static*/ void +PrivateConditionVariable::ListAll() +{ + kprintf(" variable object (type) waiting threads\n"); + kprintf("------------------------------------------------------------\n"); + ConditionVariableHash::Iterator it(&sConditionVariableHash); + while (PrivateConditionVariable* variable = it.Next()) { + // count waiting threads + int count = 0; + PrivateConditionVariableEntry* entry = variable->fEntries; + while (entry) { + count++; + entry = entry->fNext; + } + + kprintf("%p %p %-20s %15d\n", variable, variable->fObject, + variable->fObjectType, count); + } +} + + +void +PrivateConditionVariable::Dump() +{ + kprintf("condition variable %p\n", this); + kprintf(" object: %p (%s)\n", fObject, fObjectType); + kprintf(" threads:"); + PrivateConditionVariableEntry* entry = fEntries; + while (entry) { + kprintf(" %ld", entry->fThread->id); + entry = entry->fNext; + } + kprintf("\n"); +} + + +void +PrivateConditionVariable::Publish(const void* object, const char* objectType) +{ + ASSERT(object != NULL); + + fObject = object; + fObjectType = objectType; + fEntries = NULL; + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + ASSERT_PRINT(sConditionVariableHash.Lookup(object) == NULL, + "condition variable: %p\n", sConditionVariableHash.Lookup(object)); + + sConditionVariableHash.InsertUnchecked(this); +} + + +void +PrivateConditionVariable::Unpublish() +{ + ASSERT(fObject != NULL); + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + +#if KDEBUG + PrivateConditionVariable* 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) + _Notify(); +} + + +void +PrivateConditionVariable::Notify() +{ + ASSERT(fObject != NULL); + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + +#if KDEBUG + PrivateConditionVariable* variable = sConditionVariableHash.Lookup(fObject); + if (variable != this) { + panic("Condition variable %p not published, found: %p", this, variable); + return; + } +#endif + + if (fEntries) + _Notify(); +} + + +//! Called with interrupts disabled and the condition variable spinlock held. +void +PrivateConditionVariable::_Notify() +{ + // dequeue and wake up the blocked threads + GRAB_THREAD_LOCK(); + + while (PrivateConditionVariableEntry* entry = fEntries) { + fEntries = entry->fNext; + + entry->fNext = NULL; + entry->fVariable = NULL; + + entry->fThread->condition_variable = NULL; + if (entry->fThread->state == B_THREAD_WAITING) { + entry->fThread->state = B_THREAD_READY; + scheduler_enqueue_in_run_queue(entry->fThread); + } + } + + RELEASE_THREAD_LOCK(); +} + + +#if 0 + +void +publish_stack_condition_variable(condition_variable* variable, + const void* object, const char* objectType) +{ + ASSERT(variable != NULL); + ASSERT(object != NULL); + + variable->object = object; + variable->object_type = objectType; + variable->entries = NULL; + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + ASSERT_PRINT(sConditionVariableHash.Lookup(object) == NULL, + "condition variable: %p\n", sConditionVariableHash.Lookup(object)); + + sConditionVariableHash.InsertUnchecked(variable); +} + + +void +unpublish_condition_variable(const void* object) +{ + ASSERT(object != NULL); + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + condition_variable* variable = sConditionVariableHash.Lookup(object); + condition_variable_entry* entries = NULL; + if (variable) { + sConditionVariableHash.RemoveUnchecked(variable); + entries = variable->entries; + variable->object = NULL; + variable->object_type = NULL; + variable->entries = NULL; + } else { + panic("No condition variable for %p\n", object); + } + + if (entries) + notify_condition_variable_entries(entries); +} + + +void +notify_condition_variable(const void* object) +{ + ASSERT(object != NULL); + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + condition_variable* variable = sConditionVariableHash.Lookup(object); + condition_variable_entry* entries = NULL; + if (variable) { + entries = variable->entries; + variable->entries = NULL; + } else { + panic("No condition variable for %p\n", object); + } + + locker.Unlock(); + + if (entries) + notify_condition_variable_entries(entries); +} + + +void +wait_for_condition_variable(const void* object) +{ + condition_variable_entry entry; + if (add_condition_variable_entry(object, &entry)) + wait_for_condition_variable_entry(&entry); +} + + +bool +add_condition_variable_entry(const void* object, + condition_variable_entry* entry) +{ + ASSERT(object != NULL); + ASSERT(entry != NULL); + + entry->thread = thread_get_current_thread(); + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + condition_variable* variable = sConditionVariableHash.Lookup(object); + if (variable) { + entry->variable = variable; + entry->next = variable->entries; + variable->entries = entry; + } else + entry->variable = NULL; + + return (variable != NULL); +} + + +void +wait_for_condition_variable_entry(condition_variable_entry* entry) +{ + ASSERT(entry != NULL); + + if (!are_interrupts_enabled()) { + panic("wait_for_condition_variable_entry() called with interrupts " + "disabled"); + return; + } + + InterruptsLocker _; + SpinLocker locker(sConditionVariablesLock); + + condition_variable* variable = entry->variable; + if (variable != NULL) { + struct thread* thread = thread_get_current_thread(); + thread->next_state = B_THREAD_WAITING; + thread->condition_variable = variable; + thread->sem.blocking = -1; + } + + if (variable != NULL) { + GRAB_THREAD_LOCK(); + locker.Unlock(); + scheduler_reschedule(); + RELEASE_THREAD_LOCK(); + } +} +#endif + + +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("condition_variable", &dump_condition_variable, + "Dump condition_variable"); + add_debugger_command("condition_variables", &list_condition_variables, + "List condition variables"); +} +