/* * Copyright 2007-2009, Ingo Weinhold, ingo_weinhold@gmx.de. * Distributed under the terms of the MIT License. */ #include "condition_variable.h" #include #include #include #include // libroot #include // system #include #include #include #include #include "thread.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; } ConditionVariable*& GetLink(ConditionVariable* variable) const { return variable->fNext; } }; typedef BOpenHashTable ConditionVariableHash; static ConditionVariableHash sConditionVariableHash; static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables"); // #pragma mark - ConditionVariableEntry ConditionVariableEntry::ConditionVariableEntry() : fVariable(NULL) { } ConditionVariableEntry::~ConditionVariableEntry() { if (fVariable != NULL) _RemoveFromVariable(); } bool ConditionVariableEntry::Add(const void* object) { ASSERT(object != NULL); fThread = get_current_thread(); MutexLocker _(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) { MutexLocker conditionLocker(sConditionVariablesLock); if (fVariable == NULL) return fWaitStatus; user_thread* userThread = get_user_thread(); userThread->wait_status = 1; fWaitStatus = STATUS_WAITING; conditionLocker.Unlock(); status_t error; while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) { } _RemoveFromVariable(); 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::_AddToLockedVariable(ConditionVariable* variable) { fThread = get_current_thread(); fVariable = variable; fWaitStatus = STATUS_ADDED; fVariable->fEntries.Add(this); } void ConditionVariableEntry::_RemoveFromVariable() { MutexLocker _(sConditionVariablesLock); if (fVariable != NULL) { fVariable->fEntries.Remove(this); fVariable = NULL; } } // #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; } void ConditionVariable::Publish(const void* object, const char* objectType) { ASSERT(object != NULL); fObject = object; fObjectType = objectType; new(&fEntries) EntryList; MutexLocker locker(sConditionVariablesLock); ASSERT(sConditionVariableHash.Lookup(object) == NULL); sConditionVariableHash.InsertUnchecked(this); } void ConditionVariable::Unpublish() { ASSERT(fObject != NULL); MutexLocker locker(sConditionVariablesLock); sConditionVariableHash.RemoveUnchecked(this); fObject = NULL; fObjectType = NULL; if (!fEntries.IsEmpty()) _NotifyLocked(true, B_ENTRY_NOT_FOUND); } void ConditionVariable::Add(ConditionVariableEntry* entry) { MutexLocker _(sConditionVariablesLock); entry->_AddToLockedVariable(this); } status_t ConditionVariable::Wait(uint32 flags, bigtime_t timeout) { ConditionVariableEntry entry; Add(&entry); return entry.Wait(flags, timeout); } int32 ConditionVariable::_Notify(bool all, status_t result) { MutexLocker locker(sConditionVariablesLock); if (!fEntries.IsEmpty()) { if (result > B_OK) { panic("tried to notify with invalid result %" B_PRId32 "\n", result); result = B_ERROR; } return _NotifyLocked(all, result); } return 0; } /*! Called with interrupts disabled and the condition variable spinlock and thread lock held. */ int32 ConditionVariable::_NotifyLocked(bool all, status_t result) { int32 notified = 0; // 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) _kern_unblock_thread(get_thread_id(entry->fThread), result); entry->fWaitStatus = result; notified++; if (!all) break; } return notified; } // #pragma mark - void condition_variable_init() { status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); if (error != B_OK) { panic("condition_variable_init(): Failed to init hash table: %s", strerror(error)); } }