152c8e07fSIngo Weinhold /* 220bc3710SIngo Weinhold * Copyright 2007-2009, Ingo Weinhold, ingo_weinhold@gmx.de. 320bc3710SIngo Weinhold * Distributed under the terms of the MIT License. 452c8e07fSIngo Weinhold */ 552c8e07fSIngo Weinhold 620bc3710SIngo Weinhold #include "condition_variable.h" 752c8e07fSIngo Weinhold 852c8e07fSIngo Weinhold #include <new> 920bc3710SIngo Weinhold #include <stdlib.h> 1020bc3710SIngo Weinhold #include <string.h> 1120bc3710SIngo Weinhold 1220bc3710SIngo Weinhold #include <KernelExport.h> 1320bc3710SIngo Weinhold 1420bc3710SIngo Weinhold // libroot 1520bc3710SIngo Weinhold #include <user_thread.h> 1620bc3710SIngo Weinhold 1720bc3710SIngo Weinhold // system 1820bc3710SIngo Weinhold #include <syscalls.h> 1920bc3710SIngo Weinhold #include <user_thread_defs.h> 2052c8e07fSIngo Weinhold 2152c8e07fSIngo Weinhold #include <lock.h> 2220bc3710SIngo Weinhold #include <util/AutoLock.h> 2320bc3710SIngo Weinhold 2420bc3710SIngo Weinhold #include "thread.h" 2552c8e07fSIngo Weinhold 2652c8e07fSIngo Weinhold 2752c8e07fSIngo Weinhold #define STATUS_ADDED 1 2852c8e07fSIngo Weinhold #define STATUS_WAITING 2 2952c8e07fSIngo Weinhold 3052c8e07fSIngo Weinhold 3120bc3710SIngo Weinhold static const int kConditionVariableHashSize = 512; 3220bc3710SIngo Weinhold 3320bc3710SIngo Weinhold 3420bc3710SIngo Weinhold struct ConditionVariableHashDefinition { 3520bc3710SIngo Weinhold typedef const void* KeyType; 3620bc3710SIngo Weinhold typedef ConditionVariable ValueType; 3720bc3710SIngo Weinhold 3820bc3710SIngo Weinhold size_t HashKey(const void* key) const 3920bc3710SIngo Weinhold { return (size_t)key; } 4020bc3710SIngo Weinhold size_t Hash(ConditionVariable* variable) const 4120bc3710SIngo Weinhold { return (size_t)variable->fObject; } 4220bc3710SIngo Weinhold bool Compare(const void* key, ConditionVariable* variable) const 4320bc3710SIngo Weinhold { return key == variable->fObject; } 4420bc3710SIngo Weinhold HashTableLink<ConditionVariable>* GetLink(ConditionVariable* variable) const 4520bc3710SIngo Weinhold { return variable; } 4652c8e07fSIngo Weinhold }; 4752c8e07fSIngo Weinhold 4820bc3710SIngo Weinhold typedef OpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash; 4920bc3710SIngo Weinhold static ConditionVariableHash sConditionVariableHash; 5020bc3710SIngo Weinhold static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables"); 5120bc3710SIngo Weinhold 5220bc3710SIngo Weinhold 5320bc3710SIngo Weinhold // #pragma mark - ConditionVariableEntry 5420bc3710SIngo Weinhold 5520bc3710SIngo Weinhold 5620bc3710SIngo Weinhold bool 5720bc3710SIngo Weinhold ConditionVariableEntry::Add(const void* object) 5820bc3710SIngo Weinhold { 5920bc3710SIngo Weinhold ASSERT(object != NULL); 6020bc3710SIngo Weinhold 6120bc3710SIngo Weinhold fThread = get_current_thread(); 6220bc3710SIngo Weinhold 6320bc3710SIngo Weinhold MutexLocker _(sConditionVariablesLock); 6420bc3710SIngo Weinhold 6520bc3710SIngo Weinhold fVariable = sConditionVariableHash.Lookup(object); 6620bc3710SIngo Weinhold 6720bc3710SIngo Weinhold if (fVariable == NULL) { 6820bc3710SIngo Weinhold fWaitStatus = B_ENTRY_NOT_FOUND; 6920bc3710SIngo Weinhold return false; 7020bc3710SIngo Weinhold } 7120bc3710SIngo Weinhold 7220bc3710SIngo Weinhold fWaitStatus = STATUS_ADDED; 7320bc3710SIngo Weinhold fVariable->fEntries.Add(this); 7420bc3710SIngo Weinhold 7520bc3710SIngo Weinhold return true; 7620bc3710SIngo Weinhold } 7720bc3710SIngo Weinhold 7852c8e07fSIngo Weinhold 7952c8e07fSIngo Weinhold status_t 8052c8e07fSIngo Weinhold ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout) 8152c8e07fSIngo Weinhold { 8220bc3710SIngo Weinhold MutexLocker conditionLocker(sConditionVariablesLock); 8320bc3710SIngo Weinhold 8452c8e07fSIngo Weinhold if (fVariable == NULL) 8552c8e07fSIngo Weinhold return fWaitStatus; 8652c8e07fSIngo Weinhold 8720bc3710SIngo Weinhold user_thread* userThread = get_user_thread(); 8820bc3710SIngo Weinhold 8920bc3710SIngo Weinhold userThread->wait_status = 1; 9052c8e07fSIngo Weinhold fWaitStatus = STATUS_WAITING; 9152c8e07fSIngo Weinhold 9220bc3710SIngo Weinhold conditionLocker.Unlock(); 9352c8e07fSIngo Weinhold 9420bc3710SIngo Weinhold status_t error; 9520bc3710SIngo Weinhold while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) { 9620bc3710SIngo Weinhold } 9720bc3710SIngo Weinhold 9820bc3710SIngo Weinhold conditionLocker.Lock(); 9952c8e07fSIngo Weinhold 10052c8e07fSIngo Weinhold // remove entry from variable, if not done yet 10152c8e07fSIngo Weinhold if (fVariable != NULL) { 10252c8e07fSIngo Weinhold fVariable->fEntries.Remove(this); 10352c8e07fSIngo Weinhold fVariable = NULL; 10452c8e07fSIngo Weinhold } 10552c8e07fSIngo Weinhold 10620bc3710SIngo Weinhold return error; 10720bc3710SIngo Weinhold } 10852c8e07fSIngo Weinhold 10920bc3710SIngo Weinhold 11020bc3710SIngo Weinhold status_t 11120bc3710SIngo Weinhold ConditionVariableEntry::Wait(const void* object, uint32 flags, 11220bc3710SIngo Weinhold bigtime_t timeout) 11320bc3710SIngo Weinhold { 11420bc3710SIngo Weinhold if (Add(object)) 11520bc3710SIngo Weinhold return Wait(flags, timeout); 11620bc3710SIngo Weinhold return B_ENTRY_NOT_FOUND; 11752c8e07fSIngo Weinhold } 11852c8e07fSIngo Weinhold 11952c8e07fSIngo Weinhold 12052c8e07fSIngo Weinhold inline void 12152c8e07fSIngo Weinhold ConditionVariableEntry::AddToVariable(ConditionVariable* variable) 12252c8e07fSIngo Weinhold { 12320bc3710SIngo Weinhold fThread = get_current_thread(); 12420bc3710SIngo Weinhold 12520bc3710SIngo Weinhold MutexLocker _(sConditionVariablesLock); 12620bc3710SIngo Weinhold 12752c8e07fSIngo Weinhold fVariable = variable; 12852c8e07fSIngo Weinhold fWaitStatus = STATUS_ADDED; 12952c8e07fSIngo Weinhold fVariable->fEntries.Add(this); 13052c8e07fSIngo Weinhold } 13152c8e07fSIngo Weinhold 13252c8e07fSIngo Weinhold 13320bc3710SIngo Weinhold // #pragma mark - ConditionVariable 13452c8e07fSIngo Weinhold 13552c8e07fSIngo Weinhold 13620bc3710SIngo Weinhold /*! Initialization method for anonymous (unpublished) condition variables. 13720bc3710SIngo Weinhold */ 13852c8e07fSIngo Weinhold void 13952c8e07fSIngo Weinhold ConditionVariable::Init(const void* object, const char* objectType) 14052c8e07fSIngo Weinhold { 14120bc3710SIngo Weinhold fObject = object; 14220bc3710SIngo Weinhold fObjectType = objectType; 14320bc3710SIngo Weinhold new(&fEntries) EntryList; 14420bc3710SIngo Weinhold } 14520bc3710SIngo Weinhold 14620bc3710SIngo Weinhold 14720bc3710SIngo Weinhold void 14820bc3710SIngo Weinhold ConditionVariable::Publish(const void* object, const char* objectType) 14920bc3710SIngo Weinhold { 15020bc3710SIngo Weinhold ASSERT(object != NULL); 15120bc3710SIngo Weinhold 15220bc3710SIngo Weinhold fObject = object; 15352c8e07fSIngo Weinhold fObjectType = objectType; 15452c8e07fSIngo Weinhold new(&fEntries) EntryList; 15552c8e07fSIngo Weinhold 15620bc3710SIngo Weinhold MutexLocker locker(sConditionVariablesLock); 15752c8e07fSIngo Weinhold 15820bc3710SIngo Weinhold ASSERT(sConditionVariableHash.Lookup(object) == NULL); 15952c8e07fSIngo Weinhold 16020bc3710SIngo Weinhold sConditionVariableHash.InsertUnchecked(this); 16120bc3710SIngo Weinhold } 16220bc3710SIngo Weinhold 16320bc3710SIngo Weinhold 16420bc3710SIngo Weinhold void 16520bc3710SIngo Weinhold ConditionVariable::Unpublish(bool threadsLocked) 16620bc3710SIngo Weinhold { 16720bc3710SIngo Weinhold ASSERT(fObject != NULL); 16820bc3710SIngo Weinhold 16920bc3710SIngo Weinhold MutexLocker locker(sConditionVariablesLock); 17020bc3710SIngo Weinhold 17120bc3710SIngo Weinhold sConditionVariableHash.RemoveUnchecked(this); 17220bc3710SIngo Weinhold fObject = NULL; 17320bc3710SIngo Weinhold fObjectType = NULL; 17420bc3710SIngo Weinhold 17520bc3710SIngo Weinhold if (!fEntries.IsEmpty()) 17620bc3710SIngo Weinhold _NotifyChecked(true, B_ENTRY_NOT_FOUND); 17752c8e07fSIngo Weinhold } 17852c8e07fSIngo Weinhold 17952c8e07fSIngo Weinhold 18052c8e07fSIngo Weinhold void 18152c8e07fSIngo Weinhold ConditionVariable::Add(ConditionVariableEntry* entry) 18252c8e07fSIngo Weinhold { 18352c8e07fSIngo Weinhold entry->AddToVariable(this); 18452c8e07fSIngo Weinhold } 18552c8e07fSIngo Weinhold 18652c8e07fSIngo Weinhold 18720bc3710SIngo Weinhold status_t 18820bc3710SIngo Weinhold ConditionVariable::Wait(uint32 flags, bigtime_t timeout) 18920bc3710SIngo Weinhold { 19020bc3710SIngo Weinhold ConditionVariableEntry entry; 19120bc3710SIngo Weinhold Add(&entry); 19220bc3710SIngo Weinhold return entry.Wait(flags, timeout); 19320bc3710SIngo Weinhold } 19420bc3710SIngo Weinhold 19520bc3710SIngo Weinhold 19652c8e07fSIngo Weinhold void 19752c8e07fSIngo Weinhold ConditionVariable::_Notify(bool all, bool threadsLocked) 19852c8e07fSIngo Weinhold { 19920bc3710SIngo Weinhold MutexLocker locker(sConditionVariablesLock); 20052c8e07fSIngo Weinhold 20120bc3710SIngo Weinhold if (!fEntries.IsEmpty()) 20220bc3710SIngo Weinhold _NotifyChecked(all, B_OK); 20320bc3710SIngo Weinhold } 20452c8e07fSIngo Weinhold 20520bc3710SIngo Weinhold 20620bc3710SIngo Weinhold /*! Called with interrupts disabled and the condition variable spinlock and 20720bc3710SIngo Weinhold thread lock held. 20820bc3710SIngo Weinhold */ 20920bc3710SIngo Weinhold void 21020bc3710SIngo Weinhold ConditionVariable::_NotifyChecked(bool all, status_t result) 21120bc3710SIngo Weinhold { 21220bc3710SIngo Weinhold // dequeue and wake up the blocked threads 21352c8e07fSIngo Weinhold while (ConditionVariableEntry* entry = fEntries.RemoveHead()) { 21452c8e07fSIngo Weinhold entry->fVariable = NULL; 21520bc3710SIngo Weinhold 21652c8e07fSIngo Weinhold if (entry->fWaitStatus <= 0) 21752c8e07fSIngo Weinhold continue; 21852c8e07fSIngo Weinhold 21920bc3710SIngo Weinhold if (entry->fWaitStatus == STATUS_WAITING) 22020bc3710SIngo Weinhold _kern_unblock_thread(get_thread_id(entry->fThread), result); 22120bc3710SIngo Weinhold 22220bc3710SIngo Weinhold entry->fWaitStatus = result; 22352c8e07fSIngo Weinhold 22452c8e07fSIngo Weinhold if (!all) 22552c8e07fSIngo Weinhold break; 22652c8e07fSIngo Weinhold } 22720bc3710SIngo Weinhold } 22852c8e07fSIngo Weinhold 22920bc3710SIngo Weinhold 23020bc3710SIngo Weinhold // #pragma mark - 23120bc3710SIngo Weinhold 23220bc3710SIngo Weinhold 233*cc586449SIngo Weinhold void 23420bc3710SIngo Weinhold condition_variable_init() 23520bc3710SIngo Weinhold { 23620bc3710SIngo Weinhold status_t error = sConditionVariableHash.Init(kConditionVariableHashSize); 23720bc3710SIngo Weinhold if (error != B_OK) { 23820bc3710SIngo Weinhold panic("condition_variable_init(): Failed to init hash table: %s", 23920bc3710SIngo Weinhold strerror(error)); 24020bc3710SIngo Weinhold } 24152c8e07fSIngo Weinhold } 242