xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/condition_variable.cpp (revision cc586449f76ad8d9d0d8f4724ac6193c9816d379)
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