xref: /haiku/src/tests/add-ons/kernel/kernelland_emu/condition_variable.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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 ConditionVariableEntry::ConditionVariableEntry()
57 	: fVariable(NULL)
58 {
59 }
60 
61 
62 ConditionVariableEntry::~ConditionVariableEntry()
63 {
64 	if (fVariable != NULL)
65 		_RemoveFromVariable();
66 }
67 
68 
69 bool
70 ConditionVariableEntry::Add(const void* object)
71 {
72 	ASSERT(object != NULL);
73 
74 	fThread = get_current_thread();
75 
76 	MutexLocker _(sConditionVariablesLock);
77 
78 	fVariable = sConditionVariableHash.Lookup(object);
79 
80 	if (fVariable == NULL) {
81 		fWaitStatus = B_ENTRY_NOT_FOUND;
82 		return false;
83 	}
84 
85 	fWaitStatus = STATUS_ADDED;
86 	fVariable->fEntries.Add(this);
87 
88 	return true;
89 }
90 
91 
92 status_t
93 ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
94 {
95 	MutexLocker conditionLocker(sConditionVariablesLock);
96 
97 	if (fVariable == NULL)
98 		return fWaitStatus;
99 
100 	user_thread* userThread = get_user_thread();
101 
102 	userThread->wait_status = 1;
103 	fWaitStatus = STATUS_WAITING;
104 
105 	conditionLocker.Unlock();
106 
107 	status_t error;
108 	while ((error = _kern_block_thread(flags, timeout)) == B_INTERRUPTED) {
109 	}
110 
111 	_RemoveFromVariable();
112 	return error;
113 }
114 
115 
116 status_t
117 ConditionVariableEntry::Wait(const void* object, uint32 flags,
118 	bigtime_t timeout)
119 {
120 	if (Add(object))
121 		return Wait(flags, timeout);
122 	return B_ENTRY_NOT_FOUND;
123 }
124 
125 
126 inline void
127 ConditionVariableEntry::_AddToLockedVariable(ConditionVariable* variable)
128 {
129 	fThread = get_current_thread();
130 	fVariable = variable;
131 	fWaitStatus = STATUS_ADDED;
132 	fVariable->fEntries.Add(this);
133 }
134 
135 
136 void
137 ConditionVariableEntry::_RemoveFromVariable()
138 {
139 	MutexLocker _(sConditionVariablesLock);
140 	if (fVariable != NULL) {
141 		fVariable->fEntries.Remove(this);
142 		fVariable = NULL;
143 	}
144 }
145 
146 
147 // #pragma mark - ConditionVariable
148 
149 
150 /*!	Initialization method for anonymous (unpublished) condition variables.
151 */
152 void
153 ConditionVariable::Init(const void* object, const char* objectType)
154 {
155 	fObject = object;
156 	fObjectType = objectType;
157 	new(&fEntries) EntryList;
158 }
159 
160 
161 void
162 ConditionVariable::Publish(const void* object, const char* objectType)
163 {
164 	ASSERT(object != NULL);
165 
166 	fObject = object;
167 	fObjectType = objectType;
168 	new(&fEntries) EntryList;
169 
170 	MutexLocker locker(sConditionVariablesLock);
171 
172 	ASSERT(sConditionVariableHash.Lookup(object) == NULL);
173 
174 	sConditionVariableHash.InsertUnchecked(this);
175 }
176 
177 
178 void
179 ConditionVariable::Unpublish()
180 {
181 	ASSERT(fObject != NULL);
182 
183 	MutexLocker locker(sConditionVariablesLock);
184 
185 	sConditionVariableHash.RemoveUnchecked(this);
186 	fObject = NULL;
187 	fObjectType = NULL;
188 
189 	if (!fEntries.IsEmpty())
190 		_NotifyLocked(true, B_ENTRY_NOT_FOUND);
191 }
192 
193 
194 void
195 ConditionVariable::Add(ConditionVariableEntry* entry)
196 {
197 	MutexLocker _(sConditionVariablesLock);
198 	entry->_AddToLockedVariable(this);
199 }
200 
201 
202 status_t
203 ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
204 {
205 	ConditionVariableEntry entry;
206 	Add(&entry);
207 	return entry.Wait(flags, timeout);
208 }
209 
210 
211 int32
212 ConditionVariable::_Notify(bool all, status_t result)
213 {
214 	MutexLocker locker(sConditionVariablesLock);
215 
216 	if (!fEntries.IsEmpty()) {
217 		if (result > B_OK) {
218 			panic("tried to notify with invalid result %" B_PRId32 "\n",
219 				result);
220 			result = B_ERROR;
221 		}
222 
223 		return _NotifyLocked(all, result);
224 	}
225 	return 0;
226 }
227 
228 
229 /*! Called with interrupts disabled and the condition variable spinlock and
230 	thread lock held.
231 */
232 int32
233 ConditionVariable::_NotifyLocked(bool all, status_t result)
234 {
235 	int32 notified = 0;
236 
237 	// dequeue and wake up the blocked threads
238 	while (ConditionVariableEntry* entry = fEntries.RemoveHead()) {
239 		entry->fVariable = NULL;
240 
241 		if (entry->fWaitStatus <= 0)
242 			continue;
243 
244 		if (entry->fWaitStatus == STATUS_WAITING)
245 			_kern_unblock_thread(get_thread_id(entry->fThread), result);
246 
247 		entry->fWaitStatus = result;
248 
249 		notified++;
250 		if (!all)
251 			break;
252 	}
253 
254 	return notified;
255 }
256 
257 
258 // #pragma mark -
259 
260 
261 void
262 condition_variable_init()
263 {
264 	status_t error = sConditionVariableHash.Init(kConditionVariableHashSize);
265 	if (error != B_OK) {
266 		panic("condition_variable_init(): Failed to init hash table: %s",
267 			strerror(error));
268 	}
269 }
270