xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/condition_variable.cpp (revision 387fe2d9a7679d3ccfdb68dfee789e73e477deb8)
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 
HashKeyConditionVariableHashDefinition3820bc3710SIngo Weinhold 	size_t HashKey(const void* key) const
3920bc3710SIngo Weinhold 		{ return (size_t)key; }
HashConditionVariableHashDefinition4020bc3710SIngo Weinhold 	size_t Hash(ConditionVariable* variable) const
4120bc3710SIngo Weinhold 		{ return (size_t)variable->fObject; }
CompareConditionVariableHashDefinition4220bc3710SIngo Weinhold 	bool Compare(const void* key, ConditionVariable* variable) const
4320bc3710SIngo Weinhold 		{ return key == variable->fObject; }
GetLinkConditionVariableHashDefinition445147963dSStephan Aßmus 	ConditionVariable*& GetLink(ConditionVariable* variable) const
455147963dSStephan Aßmus 		{ return variable->fNext; }
4652c8e07fSIngo Weinhold };
4752c8e07fSIngo Weinhold 
485147963dSStephan Aßmus typedef BOpenHashTable<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 
ConditionVariableEntry()561728b8c7SAugustin Cavalier ConditionVariableEntry::ConditionVariableEntry()
571728b8c7SAugustin Cavalier 	: fVariable(NULL)
581728b8c7SAugustin Cavalier {
591728b8c7SAugustin Cavalier }
601728b8c7SAugustin Cavalier 
611728b8c7SAugustin Cavalier 
~ConditionVariableEntry()621728b8c7SAugustin Cavalier ConditionVariableEntry::~ConditionVariableEntry()
631728b8c7SAugustin Cavalier {
641728b8c7SAugustin Cavalier 	if (fVariable != NULL)
651728b8c7SAugustin Cavalier 		_RemoveFromVariable();
661728b8c7SAugustin Cavalier }
671728b8c7SAugustin Cavalier 
681728b8c7SAugustin Cavalier 
6920bc3710SIngo Weinhold bool
Add(const void * object)7020bc3710SIngo Weinhold ConditionVariableEntry::Add(const void* object)
7120bc3710SIngo Weinhold {
7220bc3710SIngo Weinhold 	ASSERT(object != NULL);
7320bc3710SIngo Weinhold 
7420bc3710SIngo Weinhold 	fThread = get_current_thread();
7520bc3710SIngo Weinhold 
7620bc3710SIngo Weinhold 	MutexLocker _(sConditionVariablesLock);
7720bc3710SIngo Weinhold 
7820bc3710SIngo Weinhold 	fVariable = sConditionVariableHash.Lookup(object);
7920bc3710SIngo Weinhold 
8020bc3710SIngo Weinhold 	if (fVariable == NULL) {
8120bc3710SIngo Weinhold 		fWaitStatus = B_ENTRY_NOT_FOUND;
8220bc3710SIngo Weinhold 		return false;
8320bc3710SIngo Weinhold 	}
8420bc3710SIngo Weinhold 
8520bc3710SIngo Weinhold 	fWaitStatus = STATUS_ADDED;
8620bc3710SIngo Weinhold 	fVariable->fEntries.Add(this);
8720bc3710SIngo Weinhold 
8820bc3710SIngo Weinhold 	return true;
8920bc3710SIngo Weinhold }
9020bc3710SIngo Weinhold 
9152c8e07fSIngo Weinhold 
9252c8e07fSIngo Weinhold status_t
Wait(uint32 flags,bigtime_t timeout)9352c8e07fSIngo Weinhold ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
9452c8e07fSIngo Weinhold {
9520bc3710SIngo Weinhold 	MutexLocker conditionLocker(sConditionVariablesLock);
9620bc3710SIngo Weinhold 
9752c8e07fSIngo Weinhold 	if (fVariable == NULL)
9852c8e07fSIngo Weinhold 		return fWaitStatus;
9952c8e07fSIngo Weinhold 
10020bc3710SIngo Weinhold 	user_thread* userThread = get_user_thread();
10120bc3710SIngo Weinhold 
10220bc3710SIngo Weinhold 	userThread->wait_status = 1;
10352c8e07fSIngo Weinhold 	fWaitStatus = STATUS_WAITING;
10452c8e07fSIngo Weinhold 
10520bc3710SIngo Weinhold 	conditionLocker.Unlock();
10652c8e07fSIngo Weinhold 
10720bc3710SIngo Weinhold 	status_t error;
10820bc3710SIngo Weinhold 	while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) {
10920bc3710SIngo Weinhold 	}
11020bc3710SIngo Weinhold 
1111728b8c7SAugustin Cavalier 	_RemoveFromVariable();
11220bc3710SIngo Weinhold 	return error;
11320bc3710SIngo Weinhold }
11452c8e07fSIngo Weinhold 
11520bc3710SIngo Weinhold 
11620bc3710SIngo Weinhold status_t
Wait(const void * object,uint32 flags,bigtime_t timeout)11720bc3710SIngo Weinhold ConditionVariableEntry::Wait(const void* object, uint32 flags,
11820bc3710SIngo Weinhold 	bigtime_t timeout)
11920bc3710SIngo Weinhold {
12020bc3710SIngo Weinhold 	if (Add(object))
12120bc3710SIngo Weinhold 		return Wait(flags, timeout);
12220bc3710SIngo Weinhold 	return B_ENTRY_NOT_FOUND;
12352c8e07fSIngo Weinhold }
12452c8e07fSIngo Weinhold 
12552c8e07fSIngo Weinhold 
12652c8e07fSIngo Weinhold inline void
_AddToLockedVariable(ConditionVariable * variable)1271728b8c7SAugustin Cavalier ConditionVariableEntry::_AddToLockedVariable(ConditionVariable* variable)
12852c8e07fSIngo Weinhold {
12920bc3710SIngo Weinhold 	fThread = get_current_thread();
13052c8e07fSIngo Weinhold 	fVariable = variable;
13152c8e07fSIngo Weinhold 	fWaitStatus = STATUS_ADDED;
13252c8e07fSIngo Weinhold 	fVariable->fEntries.Add(this);
13352c8e07fSIngo Weinhold }
13452c8e07fSIngo Weinhold 
13552c8e07fSIngo Weinhold 
1361728b8c7SAugustin Cavalier void
_RemoveFromVariable()1371728b8c7SAugustin Cavalier ConditionVariableEntry::_RemoveFromVariable()
1381728b8c7SAugustin Cavalier {
1391728b8c7SAugustin Cavalier 	MutexLocker _(sConditionVariablesLock);
1401728b8c7SAugustin Cavalier 	if (fVariable != NULL) {
1411728b8c7SAugustin Cavalier 		fVariable->fEntries.Remove(this);
1421728b8c7SAugustin Cavalier 		fVariable = NULL;
1431728b8c7SAugustin Cavalier 	}
1441728b8c7SAugustin Cavalier }
1451728b8c7SAugustin Cavalier 
1461728b8c7SAugustin Cavalier 
14720bc3710SIngo Weinhold // #pragma mark - ConditionVariable
14852c8e07fSIngo Weinhold 
14952c8e07fSIngo Weinhold 
15020bc3710SIngo Weinhold /*!	Initialization method for anonymous (unpublished) condition variables.
15120bc3710SIngo Weinhold */
15252c8e07fSIngo Weinhold void
Init(const void * object,const char * objectType)15352c8e07fSIngo Weinhold ConditionVariable::Init(const void* object, const char* objectType)
15452c8e07fSIngo Weinhold {
15520bc3710SIngo Weinhold 	fObject = object;
15620bc3710SIngo Weinhold 	fObjectType = objectType;
15720bc3710SIngo Weinhold 	new(&fEntries) EntryList;
15820bc3710SIngo Weinhold }
15920bc3710SIngo Weinhold 
16020bc3710SIngo Weinhold 
16120bc3710SIngo Weinhold void
Publish(const void * object,const char * objectType)16220bc3710SIngo Weinhold ConditionVariable::Publish(const void* object, const char* objectType)
16320bc3710SIngo Weinhold {
16420bc3710SIngo Weinhold 	ASSERT(object != NULL);
16520bc3710SIngo Weinhold 
16620bc3710SIngo Weinhold 	fObject = object;
16752c8e07fSIngo Weinhold 	fObjectType = objectType;
16852c8e07fSIngo Weinhold 	new(&fEntries) EntryList;
16952c8e07fSIngo Weinhold 
17020bc3710SIngo Weinhold 	MutexLocker locker(sConditionVariablesLock);
17152c8e07fSIngo Weinhold 
17220bc3710SIngo Weinhold 	ASSERT(sConditionVariableHash.Lookup(object) == NULL);
17352c8e07fSIngo Weinhold 
17420bc3710SIngo Weinhold 	sConditionVariableHash.InsertUnchecked(this);
17520bc3710SIngo Weinhold }
17620bc3710SIngo Weinhold 
17720bc3710SIngo Weinhold 
17820bc3710SIngo Weinhold void
Unpublish()179604770b3SJulian Harnath ConditionVariable::Unpublish()
18020bc3710SIngo Weinhold {
18120bc3710SIngo Weinhold 	ASSERT(fObject != NULL);
18220bc3710SIngo Weinhold 
18320bc3710SIngo Weinhold 	MutexLocker locker(sConditionVariablesLock);
18420bc3710SIngo Weinhold 
18520bc3710SIngo Weinhold 	sConditionVariableHash.RemoveUnchecked(this);
18620bc3710SIngo Weinhold 	fObject = NULL;
18720bc3710SIngo Weinhold 	fObjectType = NULL;
18820bc3710SIngo Weinhold 
18920bc3710SIngo Weinhold 	if (!fEntries.IsEmpty())
1904f5d9ca6SMichael Lotz 		_NotifyLocked(true, B_ENTRY_NOT_FOUND);
19152c8e07fSIngo Weinhold }
19252c8e07fSIngo Weinhold 
19352c8e07fSIngo Weinhold 
19452c8e07fSIngo Weinhold void
Add(ConditionVariableEntry * entry)19552c8e07fSIngo Weinhold ConditionVariable::Add(ConditionVariableEntry* entry)
19652c8e07fSIngo Weinhold {
1972f1e2ae4SAugustin Cavalier 	MutexLocker _(sConditionVariablesLock);
1981728b8c7SAugustin Cavalier 	entry->_AddToLockedVariable(this);
19952c8e07fSIngo Weinhold }
20052c8e07fSIngo Weinhold 
20152c8e07fSIngo Weinhold 
20220bc3710SIngo Weinhold status_t
Wait(uint32 flags,bigtime_t timeout)20320bc3710SIngo Weinhold ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
20420bc3710SIngo Weinhold {
20520bc3710SIngo Weinhold 	ConditionVariableEntry entry;
20620bc3710SIngo Weinhold 	Add(&entry);
20720bc3710SIngo Weinhold 	return entry.Wait(flags, timeout);
20820bc3710SIngo Weinhold }
20920bc3710SIngo Weinhold 
21020bc3710SIngo Weinhold 
211*387fe2d9SAugustin Cavalier int32
_Notify(bool all,status_t result)212604770b3SJulian Harnath ConditionVariable::_Notify(bool all, status_t result)
21352c8e07fSIngo Weinhold {
21420bc3710SIngo Weinhold 	MutexLocker locker(sConditionVariablesLock);
21552c8e07fSIngo Weinhold 
2164f5d9ca6SMichael Lotz 	if (!fEntries.IsEmpty()) {
2174f5d9ca6SMichael Lotz 		if (result > B_OK) {
218ffba6606SMichael Lotz 			panic("tried to notify with invalid result %" B_PRId32 "\n",
219ffba6606SMichael Lotz 				result);
2204f5d9ca6SMichael Lotz 			result = B_ERROR;
2214f5d9ca6SMichael Lotz 		}
2224f5d9ca6SMichael Lotz 
223*387fe2d9SAugustin Cavalier 		return _NotifyLocked(all, result);
2244f5d9ca6SMichael Lotz 	}
225*387fe2d9SAugustin Cavalier 	return 0;
22620bc3710SIngo Weinhold }
22752c8e07fSIngo Weinhold 
22820bc3710SIngo Weinhold 
22920bc3710SIngo Weinhold /*! Called with interrupts disabled and the condition variable spinlock and
23020bc3710SIngo Weinhold 	thread lock held.
23120bc3710SIngo Weinhold */
232*387fe2d9SAugustin Cavalier int32
_NotifyLocked(bool all,status_t result)2334f5d9ca6SMichael Lotz ConditionVariable::_NotifyLocked(bool all, status_t result)
23420bc3710SIngo Weinhold {
235*387fe2d9SAugustin Cavalier 	int32 notified = 0;
236*387fe2d9SAugustin Cavalier 
23720bc3710SIngo Weinhold 	// dequeue and wake up the blocked threads
23852c8e07fSIngo Weinhold 	while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
23952c8e07fSIngo Weinhold 		entry->fVariable = NULL;
24020bc3710SIngo Weinhold 
24152c8e07fSIngo Weinhold 		if (entry->fWaitStatus <= 0)
24252c8e07fSIngo Weinhold 			continue;
24352c8e07fSIngo Weinhold 
24420bc3710SIngo Weinhold 		if (entry->fWaitStatus == STATUS_WAITING)
24520bc3710SIngo Weinhold 			_kern_unblock_thread(get_thread_id(entry->fThread), result);
24620bc3710SIngo Weinhold 
24720bc3710SIngo Weinhold 		entry->fWaitStatus = result;
24852c8e07fSIngo Weinhold 
249*387fe2d9SAugustin Cavalier 		notified++;
25052c8e07fSIngo Weinhold 		if (!all)
25152c8e07fSIngo Weinhold 			break;
25252c8e07fSIngo Weinhold 	}
253*387fe2d9SAugustin Cavalier 
254*387fe2d9SAugustin Cavalier 	return notified;
25520bc3710SIngo Weinhold }
25652c8e07fSIngo Weinhold 
25720bc3710SIngo Weinhold 
25820bc3710SIngo Weinhold // #pragma mark -
25920bc3710SIngo Weinhold 
26020bc3710SIngo Weinhold 
261cc586449SIngo Weinhold void
condition_variable_init()26220bc3710SIngo Weinhold condition_variable_init()
26320bc3710SIngo Weinhold {
26420bc3710SIngo Weinhold 	status_t error = sConditionVariableHash.Init(kConditionVariableHashSize);
26520bc3710SIngo Weinhold 	if (error != B_OK) {
26620bc3710SIngo Weinhold 		panic("condition_variable_init(): Failed to init hash table: %s",
26720bc3710SIngo Weinhold 			strerror(error));
26820bc3710SIngo Weinhold 	}
26952c8e07fSIngo Weinhold }
270