1 /*************************************************************************
2  *                                                                       *
3  * Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith.       *
4  * All rights reserved.  Email: russ@q12.org   Web: www.q12.org          *
5  *                                                                       *
6  * Threading support header file.                                        *
7  * Copyright (C) 2011-2012 Oleh Derevenko. All rights reserved.          *
8  * e-mail: odar@eleks.com (change all "a" to "e")                        *
9  *                                                                       *
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     *
16  *       file LICENSE.TXT.                                               *
17  *   (2) The BSD-style license that is included with this library in     *
18  *       the file LICENSE-BSD.TXT.                                       *
19  *                                                                       *
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.                     *
24  *                                                                       *
25  *************************************************************************/
26 
27 /*
28  *   ODE threading support interfaces
29  */
30 
31 
32 #ifndef _ODE_THREADING_H_
33 #define _ODE_THREADING_H_
34 
35 #include <ode/odeconfig.h>
36 // Include <time.h> since time_t is used and it is not available by default in some OSes
37 #include <time.h>
38 
39 
40 #ifdef __cplusplus
41 extern "C" {
42 #endif
43 
44 
45 struct dxThreadingImplementation;
46 typedef struct dxThreadingImplementation *dThreadingImplementationID;
47 
48 typedef unsigned dmutexindex_t;
49 struct dxMutexGroup;
50 typedef struct dxMutexGroup *dMutexGroupID;
51 
52 /**
53  * @brief Allocates a group of muteces.
54  *
55  * The Mutex allocated do not need to support recursive locking.
56  *
57  * The Mutex names are provided to aid in debugging and thread state tracking.
58  *
59  * @param impl Threading implementation ID
60  * @param Mutex_count Number of Mutex to create
61  * @Mutex_names_ptr Pointer to optional Mutex names array to be associated with individual Mutex
62  * @returns MutexGroup ID or NULL if error occurred.
63  *
64  * @ingroup threading
65  * @see dMutexGroupFreeFunction
66  * @see dMutexGroupMutexLockFunction
67  * @see dMutexGroupMutexUnlockFunction
68  */
69 typedef dMutexGroupID dMutexGroupAllocFunction (dThreadingImplementationID impl, dmutexindex_t Mutex_count, const char *const *Mutex_names_ptr/*=NULL*/);
70 
71 /**
72  * @brief Deletes a group of muteces.
73  *
74  * @param impl Threading implementation ID
75  * @param mutex_group Mutex group to deallocate
76  *
77  * @ingroup threading
78  * @see dMutexGroupAllocFunction
79  * @see dMutexGroupMutexLockFunction
80  * @see dMutexGroupMutexUnlockFunction
81  */
82 typedef void dMutexGroupFreeFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group);
83 
84 /**
85  * @brief Locks a mutex in a group of muteces.
86  *
87  * The function is to block execution until requested mutex can be locked.
88  *
89  * Note: Mutex provided may not support recursive locking. Calling this function
90  * while mutex is already locked by current thread will result in unpredictable behavior.
91  *
92  * @param impl Threading implementation ID
93  * @param mutex_group Mutex group to use for locking
94  * @param mutex_index The index of mutex to be locked (0..Mutex_count - 1)
95  *
96  * @ingroup threading
97  * @see dMutexGroupAllocFunction
98  * @see dMutexGroupFreeFunction
99  * @see dMutexGroupMutexUnlockFunction
100  */
101 typedef void dMutexGroupMutexLockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);
102 
103 /**
104  * @brief Attempts to lock a mutex in a group of muteces.
105  *
106  * The function is to lock the requested mutex if it is unoccupied or
107  * immediately return failure if mutex is already locked by other thread.
108  *
109  * Note: Mutex provided may not support recursive locking. Calling this function
110  * while mutex is already locked by current thread will result in unpredictable behavior.
111  *
112  * @param impl Threading implementation ID
113  * @param mutex_group Mutex group to use for locking
114  * @param mutex_index The index of mutex to be locked (0..Mutex_count - 1)
115  * @returns 1 for success (mutex is locked) and 0 for failure (mutex is not locked)
116  *
117  * @ingroup threading
118  * @see dMutexGroupAllocFunction
119  * @see dMutexGroupFreeFunction
120  * @see dMutexGroupMutexLockFunction
121  * @see dMutexGroupMutexUnlockFunction
122  */
123 /* typedef int dMutexGroupMutexTryLockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);*/
124 
125 /**
126  * @brief Unlocks a mutex in a group of muteces.
127  *
128  * The function is to unlock the given mutex provided it had been locked before.
129  *
130  * @param impl Threading implementation ID
131  * @param mutex_group Mutex group to use for unlocking
132  * @param mutex_index The index of mutex to be unlocked (0..Mutex_count - 1)
133  *
134  * @ingroup threading
135  * @see dMutexGroupAllocFunction
136  * @see dMutexGroupFreeFunction
137  * @see dMutexGroupMutexLockFunction
138  */
139 typedef void dMutexGroupMutexUnlockFunction (dThreadingImplementationID impl, dMutexGroupID mutex_group, dmutexindex_t mutex_index);
140 
141 
142 struct dxCallReleasee;
143 typedef struct dxCallReleasee *dCallReleaseeID;
144 
145 struct dxCallWait;
146 typedef struct dxCallWait *dCallWaitID;
147 
148 typedef size_t ddependencycount_t;
149 typedef ptrdiff_t ddependencychange_t;
150 typedef size_t dcallindex_t;
151 typedef int dThreadedCallFunction(void *call_context, dcallindex_t instance_index,
152   dCallReleaseeID this_releasee);
153 
154 typedef struct dxThreadedWaitTime
155 {
156   time_t          wait_sec;
157   unsigned long   wait_nsec;
158 
159 } dThreadedWaitTime;
160 
161 
162 /**
163  * @brief Allocates a Wait ID that can be used to wait for a call.
164  *
165  * @param impl Threading implementation ID
166  * @returns Wait ID or NULL if error occurred
167  *
168  * @ingroup threading
169  * @see dThreadedCallWaitResetFunction
170  * @see dThreadedCallWaitFreeFunction
171  * @see dThreadedCallPostFunction
172  * @see dThreadedCallWaitFunction
173  */
174 typedef dCallWaitID dThreadedCallWaitAllocFunction(dThreadingImplementationID impl);
175 
176 /**
177  * @brief Resets a Wait ID so that it could be used to wait for another call.
178  *
179  * @param impl Threading implementation ID
180  * @param call_wait Wait ID to reset
181  *
182  * @ingroup threading
183  * @see dThreadedCallWaitAllocFunction
184  * @see dThreadedCallWaitFreeFunction
185  * @see dThreadedCallPostFunction
186  * @see dThreadedCallWaitFunction
187  */
188 typedef void dThreadedCallWaitResetFunction(dThreadingImplementationID impl, dCallWaitID call_wait);
189 
190 /**
191  * @brief Frees a Wait ID.
192  *
193  * @param impl Threading implementation ID
194  * @param call_wait Wait ID to delete
195  *
196  * @ingroup threading
197  * @see dThreadedCallWaitAllocFunction
198  * @see dThreadedCallPostFunction
199  * @see dThreadedCallWaitFunction
200  */
201 typedef void dThreadedCallWaitFreeFunction(dThreadingImplementationID impl, dCallWaitID call_wait);
202 
203 
204 /**
205  * @brief Post a function to be called in another thread.
206  *
207  * A call is scheduled to be executed asynchronously.
208  *
209  * A @a out_summary_fault variable can be provided for call to accumulate any
210  * possible faults from its execution and execution of any possible sub-calls.
211  * This variable gets result that @a call_func returns. Also, if dependent calls
212  * are executed after the call already exits, the variable is also going to be
213  * updated with results of all those calls before control is released to master.
214  *
215  * @a out_post_releasee parameter receives a value of @c dCallReleaseeID that can
216  * later be used for @a dependent_releasee while scheduling sub-calls to make
217  * current call depend on them. The value is only returned if @a dependencies_count
218  * is not zero (i.e. if any dependencies are expected at all). The call is not going
219  * to start until all its dependencies complete.
220  *
221  * In case if number of dependencies is unknown in advance 1 can be passed on call
222  * scheduling. Then @c dThreadedCallDependenciesCountAlterFunction can be used to
223  * add one more extra dependencies before scheduling each subcall. And then, after
224  * all sub-calls had been scheduled, @c dThreadedCallDependenciesCountAlterFunction
225  * can be used again to subtract initial extra dependency from total number.
226  * Adding one dependency in advance is necessary to obtain releasee ID and to make
227  * sure the call will not start and will not terminate before all sub-calls are scheduled.
228  *
229  * Extra dependencies can also be added from the call itself after it has already
230  * been started (with parameter received in @c dThreadedCallFunction).
231  * In that case those dependencies will start immediately or after call returns
232  * but the call's master will not be released/notified until all additional
233  * dependencies complete. This can be used to schedule sub-calls from a call and
234  * then pass own job to another sub-call dependent on those initial sub-calls.
235  *
236  * By using @ call_wait it is possible to assign a Wait ID that can later
237  * be passed into @c dThreadedCallWaitFunction to wait for call completion.
238  *
239  * If @a call_name is available (and it should!) the string must remain valid until
240  * after call completion. In most cases this should be a static string literal.
241  *
242  * Since the function is an analogue of normal method call it is not supposed to fail.
243  * Any complications with resource allocation on call scheduling should be
244  * anticipated, avoided and worked around by implementation.
245  *
246  * @param impl Threading implementation ID
247  * @param out_summary_fault Optional pointer to variable to be set to 1 if function
248  *        call (or any sub-call) fails internally, or 0 if all calls return success
249  * @param out_post_releasee Optional pointer to variable to receive releasee ID
250  *        associated with the call
251  * @param dependencies_count Number of dependencies that are going to reference
252  *        this call as dependent releasee
253  * @param dependent_releasee Optional releasee ID to reference with this call
254  * @param call_wait Optional Wait ID that can later be used to wait for the call
255  * @param call_func Pointer to function to be called
256  * @param call_context Context parameter to be passed into the call
257  * @param instance_index Index parameter to be passed into the call
258  * @param call_name Optional name to be associated with the call (for debugging and state tracking)
259  *
260  * @ingroup threading
261  * @see dThreadedCallWaitFunction
262  * @see dThreadedCallDependenciesCountAlterFunction
263  * @see dThreadingImplResourcesForCallsPreallocateFunction
264  */
265 typedef void dThreadedCallPostFunction(dThreadingImplementationID impl, int *out_summary_fault/*=NULL*/,
266   dCallReleaseeID *out_post_releasee/*=NULL*/, ddependencycount_t dependencies_count, dCallReleaseeID dependent_releasee/*=NULL*/,
267   dCallWaitID call_wait/*=NULL*/,
268   dThreadedCallFunction *call_func, void *call_context, dcallindex_t instance_index,
269   const char *call_name/*=NULL*/);
270 
271 /**
272  * @brief Add or remove extra dependencies from call that has been scheduled
273  * or is in process of execution.
274  *
275  * Extra dependencies can be added to a call if exact number of sub-calls is
276  * not known in advance at the moment the call is scheduled. Also, some dependencies
277  * can be removed if sub-calls were planned but then dropped.
278  *
279  * In case if total dependency count of a call reaches zero by result of invoking
280  * this function, the call is free to start executing immediately.
281  *
282  * After the call execution had been started, any additional dependencies can only
283  * be added from the call function itself!
284  *
285  * @param impl Threading implementation ID
286  * @param target_releasee ID of releasee to apply dependencies count change to
287  * @param dependencies_count_change Number of dependencies to add or remove
288  *
289  * @ingroup threading
290  * @see dThreadedCallPostFunction
291  */
292 typedef void dThreadedCallDependenciesCountAlterFunction(dThreadingImplementationID impl, dCallReleaseeID target_releasee,
293   ddependencychange_t dependencies_count_change);
294 
295 /**
296  * @brief Wait for a posted call to complete.
297  *
298  * Function blocks until a call identified by @a call_wait completes or
299  * timeout elapses.
300  *
301  * IT IS ILLEGAL TO INVOKE THIS FUNCTION FROM WITHIN A THREADED CALL!
302  * This is because doing so will block a physical thread and will require
303  * increasing worker thread count to avoid starvation. Use call dependencies
304  * if it is necessary make sure sub-calls have been completed instead!
305  *
306  * If @a timeout_time_ptr is NULL, the function waits without time limit. If @a timeout_time_ptr
307  * points to zero value, the function only checks status and does not block.
308  *
309  * If @a wait_name is available (and it should!) the string must remain valid for
310  * the duration of wait. In most cases this should be a static string literal.
311  *
312  * Function is not expected to return failures caused by system call faults as
313  * those are hardly ever possible to be handled in this case anyway. In event of
314  * system call fault the function is supposed to terminate application.
315  *
316  * @param impl Threading implementation ID
317  * @param out_wait_status Optional pointer to variable to receive 1 if waiting succeeded
318  *        or 0 in case of timeout
319  * @param call_wait Wait ID that had been passed to scheduling a call that needs to be waited for
320  * @param timeout_time_ptr Optional pointer to time specification the wait must not
321  *        last longer than (pass NULL for infinite timeout)
322  * @param wait_name Optional name to be associated with the wait (for debugging and state tracking)
323  *
324  * @ingroup threading
325  * @see dThreadedCallPostFunction
326  */
327 typedef void dThreadedCallWaitFunction(dThreadingImplementationID impl, int *out_wait_status/*=NULL*/,
328   dCallWaitID call_wait, const dThreadedWaitTime *timeout_time_ptr/*=NULL*/,
329   const char *wait_name/*=NULL*/);
330 
331 /**
332  * @brief Retrieve number of active threads that serve the implementation.
333  *
334  * @param impl Threading implementation ID
335  * @returns Number of active threads
336  *
337  * @ingroup threading
338  */
339 typedef unsigned dThreadingImplThreadCountRetrieveFunction(dThreadingImplementationID impl);
340 
341 /**
342  * @brief Preallocate resources to handle posted calls.
343  *
344  * The function is intended to make sure enough resources is preallocated for the
345  * implementation to be able to handle posted calls. Then @c max_simultaneous_calls_estimate
346  * is an estimate of how many posted calls can potentially be active or scheduled
347  * at the same time. The value is usually derived from the way the calls are posted
348  * in library code and dependencies between them.
349  *
350  * @warning While working on an implementation be prepared that the estimate provided
351  * yet rarely but theoretically can be exceeded due to unpredictability of thread execution.
352  *
353  * This function is normally going to be invoked by library each time it is entered
354  * from outside to do the job but before any threaded calls are going to be posted.
355  *
356  * @param impl Threading implementation ID
357  * @param max_simultaneous_calls_estimate An estimated number of calls that can be posted simultaneously
358  * @returns 1 or 0 to indicate success or failure
359  *
360  * @ingroup threading
361  * @see dThreadedCallPostFunction
362  */
363 typedef int dThreadingImplResourcesForCallsPreallocateFunction(dThreadingImplementationID impl,
364   ddependencycount_t max_simultaneous_calls_estimate);
365 
366 
367 /**
368  * @brief An interface structure with function pointers to be provided by threading implementation.
369  */
370 typedef struct dxThreadingFunctionsInfo
371 {
372   unsigned struct_size;
373 
374   dMutexGroupAllocFunction *alloc_mutex_group;
375   dMutexGroupFreeFunction *free_mutex_group;
376   dMutexGroupMutexLockFunction *lock_group_mutex;
377   dMutexGroupMutexUnlockFunction *unlock_group_mutex;
378 
379   dThreadedCallWaitAllocFunction *alloc_call_wait;
380   dThreadedCallWaitResetFunction *reset_call_wait;
381   dThreadedCallWaitFreeFunction *free_call_wait;
382 
383   dThreadedCallPostFunction *post_call;
384   dThreadedCallDependenciesCountAlterFunction *alter_call_dependencies_count;
385   dThreadedCallWaitFunction *wait_call;
386 
387   dThreadingImplThreadCountRetrieveFunction *retrieve_thread_count;
388   dThreadingImplResourcesForCallsPreallocateFunction *preallocate_resources_for_calls;
389 
390   /*
391    * Beware of Jon Watte's anger if you dare to uncomment this!
392    * May cryptic text below be you a warning!
393    * Стародавні легенди розказують, що кожного сміливця, хто наважиться порушити табу
394    * і відкрити заборонений код, спіткає страшне прокляття і він відразу почне робити
395    * одні лиш помилки.
396    *
397    * dMutexGroupMutexTryLockFunction *trylock_group_mutex;
398    */
399 
400 } dThreadingFunctionsInfo;
401 
402 
403 #ifdef __cplusplus
404 }
405 #endif
406 
407 #endif /* #ifndef _ODE_THREADING_H_ */
408