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