xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/condition_variable.cpp (revision b84574958d92055d2e33c26e1f2d43d48c9ed50c)
1 /*
2  * Copyright 2007-2009, Ingo Weinhold, ingo_weinhold@gmx.de.
3  * Distributed under the terms of the MIT License.
4  */
5 
6 #include "condition_variable.h"
7 
8 #include <new>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include <KernelExport.h>
13 
14 // libroot
15 #include <user_thread.h>
16 
17 // system
18 #include <syscalls.h>
19 #include <user_thread_defs.h>
20 
21 #include <lock.h>
22 #include <util/AutoLock.h>
23 
24 #include "thread.h"
25 
26 
27 #define STATUS_ADDED	1
28 #define STATUS_WAITING	2
29 
30 
31 static const int kConditionVariableHashSize = 512;
32 
33 
34 struct ConditionVariableHashDefinition {
35 	typedef const void* KeyType;
36 	typedef	ConditionVariable ValueType;
37 
38 	size_t HashKey(const void* key) const
39 		{ return (size_t)key; }
40 	size_t Hash(ConditionVariable* variable) const
41 		{ return (size_t)variable->fObject; }
42 	bool Compare(const void* key, ConditionVariable* variable) const
43 		{ return key == variable->fObject; }
44 	ConditionVariable*& GetLink(ConditionVariable* variable) const
45 		{ return variable->fNext; }
46 };
47 
48 typedef BOpenHashTable<ConditionVariableHashDefinition> ConditionVariableHash;
49 static ConditionVariableHash sConditionVariableHash;
50 static mutex sConditionVariablesLock = MUTEX_INITIALIZER("condition variables");
51 
52 
53 // #pragma mark - ConditionVariableEntry
54 
55 
56 bool
57 ConditionVariableEntry::Add(const void* object)
58 {
59 	ASSERT(object != NULL);
60 
61 	fThread = get_current_thread();
62 
63 	MutexLocker _(sConditionVariablesLock);
64 
65 	fVariable = sConditionVariableHash.Lookup(object);
66 
67 	if (fVariable == NULL) {
68 		fWaitStatus = B_ENTRY_NOT_FOUND;
69 		return false;
70 	}
71 
72 	fWaitStatus = STATUS_ADDED;
73 	fVariable->fEntries.Add(this);
74 
75 	return true;
76 }
77 
78 
79 status_t
80 ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
81 {
82 	MutexLocker conditionLocker(sConditionVariablesLock);
83 
84 	if (fVariable == NULL)
85 		return fWaitStatus;
86 
87 	user_thread* userThread = get_user_thread();
88 
89 	userThread->wait_status = 1;
90 	fWaitStatus = STATUS_WAITING;
91 
92 	conditionLocker.Unlock();
93 
94 	status_t error;
95 	while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) {
96 	}
97 
98 	conditionLocker.Lock();
99 
100 	// remove entry from variable, if not done yet
101 	if (fVariable != NULL) {
102 		fVariable->fEntries.Remove(this);
103 		fVariable = NULL;
104 	}
105 
106 	return error;
107 }
108 
109 
110 status_t
111 ConditionVariableEntry::Wait(const void* object, uint32 flags,
112 	bigtime_t timeout)
113 {
114 	if (Add(object))
115 		return Wait(flags, timeout);
116 	return B_ENTRY_NOT_FOUND;
117 }
118 
119 
120 inline void
121 ConditionVariableEntry::AddToLockedVariable(ConditionVariable* variable)
122 {
123 	fThread = get_current_thread();
124 	fVariable = variable;
125 	fWaitStatus = STATUS_ADDED;
126 	fVariable->fEntries.Add(this);
127 }
128 
129 
130 // #pragma mark - ConditionVariable
131 
132 
133 /*!	Initialization method for anonymous (unpublished) condition variables.
134 */
135 void
136 ConditionVariable::Init(const void* object, const char* objectType)
137 {
138 	fObject = object;
139 	fObjectType = objectType;
140 	new(&fEntries) EntryList;
141 }
142 
143 
144 void
145 ConditionVariable::Publish(const void* object, const char* objectType)
146 {
147 	ASSERT(object != NULL);
148 
149 	fObject = object;
150 	fObjectType = objectType;
151 	new(&fEntries) EntryList;
152 
153 	MutexLocker locker(sConditionVariablesLock);
154 
155 	ASSERT(sConditionVariableHash.Lookup(object) == NULL);
156 
157 	sConditionVariableHash.InsertUnchecked(this);
158 }
159 
160 
161 void
162 ConditionVariable::Unpublish()
163 {
164 	ASSERT(fObject != NULL);
165 
166 	MutexLocker locker(sConditionVariablesLock);
167 
168 	sConditionVariableHash.RemoveUnchecked(this);
169 	fObject = NULL;
170 	fObjectType = NULL;
171 
172 	if (!fEntries.IsEmpty())
173 		_NotifyLocked(true, B_ENTRY_NOT_FOUND);
174 }
175 
176 
177 void
178 ConditionVariable::Add(ConditionVariableEntry* entry)
179 {
180 	MutexLocker _(sConditionVariablesLock);
181 	entry->AddToLockedVariable(this);
182 }
183 
184 
185 status_t
186 ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
187 {
188 	ConditionVariableEntry entry;
189 	Add(&entry);
190 	return entry.Wait(flags, timeout);
191 }
192 
193 
194 void
195 ConditionVariable::_Notify(bool all, status_t result)
196 {
197 	MutexLocker locker(sConditionVariablesLock);
198 
199 	if (!fEntries.IsEmpty()) {
200 		if (result > B_OK) {
201 			panic("tried to notify with invalid result %" B_PRId32 "\n",
202 				result);
203 			result = B_ERROR;
204 		}
205 
206 		_NotifyLocked(all, result);
207 	}
208 }
209 
210 
211 /*! Called with interrupts disabled and the condition variable spinlock and
212 	thread lock held.
213 */
214 void
215 ConditionVariable::_NotifyLocked(bool all, status_t result)
216 {
217 	// dequeue and wake up the blocked threads
218 	while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
219 		entry->fVariable = NULL;
220 
221 		if (entry->fWaitStatus <= 0)
222 			continue;
223 
224 		if (entry->fWaitStatus == STATUS_WAITING)
225 			_kern_unblock_thread(get_thread_id(entry->fThread), result);
226 
227 		entry->fWaitStatus = result;
228 
229 		if (!all)
230 			break;
231 	}
232 }
233 
234 
235 // #pragma mark -
236 
237 
238 void
239 condition_variable_init()
240 {
241 	status_t error = sConditionVariableHash.Init(kConditionVariableHashSize);
242 	if (error != B_OK) {
243 		panic("condition_variable_init(): Failed to init hash table: %s",
244 			strerror(error));
245 	}
246 }
247