1 /*************************************************************************
3 * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. *
4 * All rights reserved. Email: russ@q12.org Web: www.q12.org *
6 * Threading support header file. *
7 * Copyright (C) 2011-2019 Oleh Derevenko. All rights reserved. *
8 * e-mail: odar@eleks.com (change all "a" to "e") *
10 * This library is free software; you can redistribute it and/or *
11 * modify it under the terms of EITHER: *
12 * (1) The GNU Lesser General Public License as published by the Free *
13 * Software Foundation; either version 2.1 of the License, or (at *
14 * your option) any later version. The text of the GNU Lesser *
15 * General Public License is included with this library in the *
17 * (2) The BSD-style license that is included with this library in *
18 * the file LICENSE-BSD.TXT. *
20 * This library is distributed in the hope that it will be useful, *
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files *
23 * LICENSE.TXT and LICENSE-BSD.TXT for more details. *
25 *************************************************************************/
28 * ODE threading support interfaces
32 #ifndef _ODE_THREADING_H_
33 #define _ODE_THREADING_H_
35 #include <ode/odeconfig.h>
36 // Include <time.h> since time_t is used and it is not available by default in some OSes
45 struct dxThreadingImplementation;
46 typedef struct dxThreadingImplementation *dThreadingImplementationID;
48 typedef unsigned dmutexindex_t;
50 typedef struct dxMutexGroup *dMutexGroupID;
53 #define dTHREADING_THREAD_COUNT_UNLIMITED 0U
58 * @brief Allocates a group of muteces.
60 * The Mutex allocated do not need to support recursive locking.
62 * The Mutex names are provided to aid in debugging and thread state tracking.
64 * @param impl Threading implementation ID
65 * @param Mutex_count Number of Mutex to create
66 * @Mutex_names_ptr Pointer to optional Mutex names array to be associated with individual Mutex
67 * @returns MutexGroup ID or NULL if error occurred.
70 * @see dMutexGroupFreeFunction
71 * @see dMutexGroupMutexLockFunction
72 * @see dMutexGroupMutexUnlockFunction
74 typedef dMutexGroupID dMutexGroupAllocFunction (dThreadingImplementationID impl, dmutexindex_t Mutex_count, const char *const *Mutex_names_ptr/*=NULL*/);
77 * @brief Deletes a group of muteces.
79 * @param impl Threading implementation ID
80 * @param mutex_group Mutex group to deallocate
83 * @see dMutexGroupAllocFunction
84 * @see dMutexGroupMutexLockFunction
85 * @see dMutexGroupMutexUnlockFunction
87 typedef void dMutexGroupFreeFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group);
90 * @brief Locks a mutex in a group of muteces.
92 * The function is to block execution until requested mutex can be locked.
94 * Note: Mutex provided may not support recursive locking. Calling this function
95 * while mutex is already locked by current thread will result in unpredictable behavior.
97 * @param impl Threading implementation ID
98 * @param mutex_group Mutex group to use for locking
99 * @param mutex_index The index of mutex to be locked (0..Mutex_count - 1)
102 * @see dMutexGroupAllocFunction
103 * @see dMutexGroupFreeFunction
104 * @see dMutexGroupMutexUnlockFunction
106 typedef void dMutexGroupMutexLockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);
109 * @brief Attempts to lock a mutex in a group of muteces.
111 * The function is to lock the requested mutex if it is unoccupied or
112 * immediately return failure if mutex is already locked by other thread.
114 * Note: Mutex provided may not support recursive locking. Calling this function
115 * while mutex is already locked by current thread will result in unpredictable behavior.
117 * @param impl Threading implementation ID
118 * @param mutex_group Mutex group to use for locking
119 * @param mutex_index The index of mutex to be locked (0..Mutex_count - 1)
120 * @returns 1 for success (mutex is locked) and 0 for failure (mutex is not locked)
123 * @see dMutexGroupAllocFunction
124 * @see dMutexGroupFreeFunction
125 * @see dMutexGroupMutexLockFunction
126 * @see dMutexGroupMutexUnlockFunction
128 /* typedef int dMutexGroupMutexTryLockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);*/
131 * @brief Unlocks a mutex in a group of muteces.
133 * The function is to unlock the given mutex provided it had been locked before.
135 * @param impl Threading implementation ID
136 * @param mutex_group Mutex group to use for unlocking
137 * @param mutex_index The index of mutex to be unlocked (0..Mutex_count - 1)
140 * @see dMutexGroupAllocFunction
141 * @see dMutexGroupFreeFunction
142 * @see dMutexGroupMutexLockFunction
144 typedef void dMutexGroupMutexUnlockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);
147 struct dxCallReleasee;
148 typedef struct dxCallReleasee *dCallReleaseeID;
151 typedef struct dxCallWait *dCallWaitID;
153 typedef dsizeint ddependencycount_t;
154 typedef ddiffint ddependencychange_t;
155 typedef dsizeint dcallindex_t;
156 typedef int dThreadedCallFunction(void *call_context, dcallindex_t instance_index,
157 dCallReleaseeID this_releasee);
159 typedef struct dxThreadedWaitTime
162 unsigned long wait_nsec;
168 * @brief Allocates a Wait ID that can be used to wait for a call.
170 * @param impl Threading implementation ID
171 * @returns Wait ID or NULL if error occurred
174 * @see dThreadedCallWaitResetFunction
175 * @see dThreadedCallWaitFreeFunction
176 * @see dThreadedCallPostFunction
177 * @see dThreadedCallWaitFunction
179 typedef dCallWaitID dThreadedCallWaitAllocFunction(dThreadingImplementationID impl);
182 * @brief Resets a Wait ID so that it could be used to wait for another call.
184 * @param impl Threading implementation ID
185 * @param call_wait Wait ID to reset
188 * @see dThreadedCallWaitAllocFunction
189 * @see dThreadedCallWaitFreeFunction
190 * @see dThreadedCallPostFunction
191 * @see dThreadedCallWaitFunction
193 typedef void dThreadedCallWaitResetFunction(dThreadingImplementationID impl, dCallWaitID call_wait);
196 * @brief Frees a Wait ID.
198 * @param impl Threading implementation ID
199 * @param call_wait Wait ID to delete
202 * @see dThreadedCallWaitAllocFunction
203 * @see dThreadedCallPostFunction
204 * @see dThreadedCallWaitFunction
206 typedef void dThreadedCallWaitFreeFunction(dThreadingImplementationID impl, dCallWaitID call_wait);
210 * @brief Post a function to be called in another thread.
212 * A call is scheduled to be executed asynchronously.
214 * A @a out_summary_fault variable can be provided for call to accumulate any
215 * possible faults from its execution and execution of any possible sub-calls.
216 * This variable gets result that @a call_func returns. Also, if dependent calls
217 * are executed after the call already exits, the variable is also going to be
218 * updated with results of all those calls before control is released to master.
220 * @a out_post_releasee parameter receives a value of @c dCallReleaseeID that can
221 * later be used for @a dependent_releasee while scheduling sub-calls to make
222 * current call depend on them. The value is only returned if @a dependencies_count
223 * is not zero (i.e. if any dependencies are expected at all). The call is not going
224 * to start until all its dependencies complete.
226 * In case if number of dependencies is unknown in advance 1 can be passed on call
227 * scheduling. Then @c dThreadedCallDependenciesCountAlterFunction can be used to
228 * add one more extra dependencies before scheduling each subcall. And then, after
229 * all sub-calls had been scheduled, @c dThreadedCallDependenciesCountAlterFunction
230 * can be used again to subtract initial extra dependency from total number.
231 * Adding one dependency in advance is necessary to obtain releasee ID and to make
232 * sure the call will not start and will not terminate before all sub-calls are scheduled.
234 * Extra dependencies can also be added from the call itself after it has already
235 * been started (with parameter received in @c dThreadedCallFunction).
236 * In that case those dependencies will start immediately or after call returns
237 * but the call's master will not be released/notified until all additional
238 * dependencies complete. This can be used to schedule sub-calls from a call and
239 * then pass own job to another sub-call dependent on those initial sub-calls.
241 * By using @ call_wait it is possible to assign a Wait ID that can later
242 * be passed into @c dThreadedCallWaitFunction to wait for call completion.
244 * If @a call_name is available (and it should!) the string must remain valid until
245 * after call completion. In most cases this should be a static string literal.
247 * Since the function is an analogue of normal method call it is not supposed to fail.
248 * Any complications with resource allocation on call scheduling should be
249 * anticipated, avoided and worked around by implementation.
251 * @param impl Threading implementation ID
252 * @param out_summary_fault Optional pointer to variable to be set to 1 if function
253 * call (or any sub-call) fails internally, or 0 if all calls return success
254 * @param out_post_releasee Optional pointer to variable to receive releasee ID
255 * associated with the call
256 * @param dependencies_count Number of dependencies that are going to reference
257 * this call as dependent releasee
258 * @param dependent_releasee Optional releasee ID to reference with this call
259 * @param call_wait Optional Wait ID that can later be used to wait for the call
260 * @param call_func Pointer to function to be called
261 * @param call_context Context parameter to be passed into the call
262 * @param instance_index Index parameter to be passed into the call
263 * @param call_name Optional name to be associated with the call (for debugging and state tracking)
266 * @see dThreadedCallWaitFunction
267 * @see dThreadedCallDependenciesCountAlterFunction
268 * @see dThreadingImplResourcesForCallsPreallocateFunction
270 typedef void dThreadedCallPostFunction(dThreadingImplementationID impl, int *out_summary_fault/*=NULL*/,
271 dCallReleaseeID *out_post_releasee/*=NULL*/, ddependencycount_t dependencies_count, dCallReleaseeID dependent_releasee/*=NULL*/,
272 dCallWaitID call_wait/*=NULL*/,
273 dThreadedCallFunction *call_func, void *call_context, dcallindex_t instance_index,
274 const char *call_name/*=NULL*/);
277 * @brief Add or remove extra dependencies from call that has been scheduled
278 * or is in process of execution.
280 * Extra dependencies can be added to a call if exact number of sub-calls is
281 * not known in advance at the moment the call is scheduled. Also, some dependencies
282 * can be removed if sub-calls were planned but then dropped.
284 * In case if total dependency count of a call reaches zero by result of invoking
285 * this function, the call is free to start executing immediately.
287 * After the call execution had been started, any additional dependencies can only
288 * be added from the call function itself!
290 * @param impl Threading implementation ID
291 * @param target_releasee ID of releasee to apply dependencies count change to
292 * @param dependencies_count_change Number of dependencies to add or remove
295 * @see dThreadedCallPostFunction
297 typedef void dThreadedCallDependenciesCountAlterFunction(dThreadingImplementationID impl, dCallReleaseeID target_releasee,
298 ddependencychange_t dependencies_count_change);
301 * @brief Wait for a posted call to complete.
303 * Function blocks until a call identified by @a call_wait completes or
306 * IT IS ILLEGAL TO INVOKE THIS FUNCTION FROM WITHIN A THREADED CALL!
307 * This is because doing so will block a physical thread and will require
308 * increasing worker thread count to avoid starvation. Use call dependencies
309 * if it is necessary make sure sub-calls have been completed instead!
311 * If @a timeout_time_ptr is NULL, the function waits without time limit. If @a timeout_time_ptr
312 * points to zero value, the function only checks status and does not block.
314 * If @a wait_name is available (and it should!) the string must remain valid for
315 * the duration of wait. In most cases this should be a static string literal.
317 * Function is not expected to return failures caused by system call faults as
318 * those are hardly ever possible to be handled in this case anyway. In event of
319 * system call fault the function is supposed to terminate application.
321 * @param impl Threading implementation ID
322 * @param out_wait_status Optional pointer to variable to receive 1 if waiting succeeded
323 * or 0 in case of timeout
324 * @param call_wait Wait ID that had been passed to scheduling a call that needs to be waited for
325 * @param timeout_time_ptr Optional pointer to time specification the wait must not
326 * last longer than (pass NULL for infinite timeout)
327 * @param wait_name Optional name to be associated with the wait (for debugging and state tracking)
330 * @see dThreadedCallPostFunction
332 typedef void dThreadedCallWaitFunction(dThreadingImplementationID impl, int *out_wait_status/*=NULL*/,
333 dCallWaitID call_wait, const dThreadedWaitTime *timeout_time_ptr/*=NULL*/,
334 const char *wait_name/*=NULL*/);
337 * @brief Retrieve number of active threads that serve the implementation.
339 * @param impl Threading implementation ID
340 * @returns Number of active threads
344 typedef unsigned dThreadingImplThreadCountRetrieveFunction(dThreadingImplementationID impl);
347 * @brief Preallocate resources to handle posted calls.
349 * The function is intended to make sure enough resources is preallocated for the
350 * implementation to be able to handle posted calls. Then @c max_simultaneous_calls_estimate
351 * is an estimate of how many posted calls can potentially be active or scheduled
352 * at the same time. The value is usually derived from the way the calls are posted
353 * in library code and dependencies between them.
355 * @warning While working on an implementation be prepared that the estimate provided
356 * yet rarely but theoretically can be exceeded due to unpredictability of thread execution.
358 * This function is normally going to be invoked by library each time it is entered
359 * from outside to do the job but before any threaded calls are going to be posted.
361 * @param impl Threading implementation ID
362 * @param max_simultaneous_calls_estimate An estimated number of calls that can be posted simultaneously
363 * @returns 1 or 0 to indicate success or failure
366 * @see dThreadedCallPostFunction
368 typedef int dThreadingImplResourcesForCallsPreallocateFunction(dThreadingImplementationID impl,
369 ddependencycount_t max_simultaneous_calls_estimate);
373 * @brief An interface structure with function pointers to be provided by threading implementation.
375 typedef struct dxThreadingFunctionsInfo
377 unsigned struct_size;
379 dMutexGroupAllocFunction *alloc_mutex_group;
380 dMutexGroupFreeFunction *free_mutex_group;
381 dMutexGroupMutexLockFunction *lock_group_mutex;
382 dMutexGroupMutexUnlockFunction *unlock_group_mutex;
384 dThreadedCallWaitAllocFunction *alloc_call_wait;
385 dThreadedCallWaitResetFunction *reset_call_wait;
386 dThreadedCallWaitFreeFunction *free_call_wait;
388 dThreadedCallPostFunction *post_call;
389 dThreadedCallDependenciesCountAlterFunction *alter_call_dependencies_count;
390 dThreadedCallWaitFunction *wait_call;
392 dThreadingImplThreadCountRetrieveFunction *retrieve_thread_count;
393 dThreadingImplResourcesForCallsPreallocateFunction *preallocate_resources_for_calls;
396 * Beware of Jon Watte's anger if you dare to uncomment this!
397 * May cryptic text below be you a warning!
398 * Стародавні легенди розказують, що кожного сміливця, хто наважиться порушити табу
399 * і відкрити заборонений код, спіткає страшне прокляття і він відразу почне робити
402 * dMutexGroupMutexTryLockFunction *trylock_group_mutex;
405 } dThreadingFunctionsInfo;
412 #endif /* #ifndef _ODE_THREADING_H_ */