libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmTaskMgr.h 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. //| Copyright: (C) 2009-2020 Kevin Larke <contact AT larke DOT org>
  2. //| License: GNU GPL version 3.0 or above. See the accompanying LICENSE file.
  3. #ifndef cmTaskMgr_h
  4. #define cmTaskMgr_h
  5. #ifdef __cplusplus
  6. extern "C" {
  7. #endif
  8. /*( { file_desc:"Task manager for controlling and monitoring tasks running in independent thread." kw:[parallel]}
  9. Usage:
  10. 1) Use cmTaskMgrInstall() to register a worker function
  11. (cmTaskMgrFunc_t) with the task manager.
  12. 2) Use cmTaskMgrCall() to queue a new instance of a
  13. task to run.
  14. 3) An internal scheduling program will start the task
  15. when there are less than 'maxActiveTaskCnt' running.
  16. This will occur when active tasks complete or are
  17. paused.
  18. When 'maxActiveTaskCnt' tasks are active and
  19. a previously paused task is unpaused the unpaused
  20. task will take precedence over tasks started after
  21. it and one of these new tasks will be deactivated.
  22. As tasks are pause/unpaused and activated/deactivated
  23. the number of active tasks may briefly exceed
  24. 'maxActiveTaskCnt'.
  25. 4) Once a task is instantiated the task manager
  26. will keep the client notified of the task status
  27. via callbacks to the cmTaskMgrStatusCb_t function.
  28. Status:
  29. kQueuedTmId - The task instance is waiting to be
  30. exectued in the task queue.
  31. kStartedTmId - The task has been made active and
  32. is running.
  33. kPausedTmId - The client sent a kPauseTmId to the
  34. task via cmTaskMgrCtl() and the task
  35. has been paused. The only way to
  36. restart the task is to send kStartTmId
  37. command.
  38. kDeactivatedTmId - The task was previously started but has
  39. now been deactivated by the system
  40. in an effort to keep the number of
  41. activate tasks at 'maxActiveTaskCnt'.
  42. kKilledTmId - The task was killed either by the client
  43. (via a cmTaskMgrCtl(kKillTmId)) or
  44. or by the system within cmTaskMgrClose()
  45. or cmTaskMgrDestroy().
  46. kCompletedTmId - The task has completed. Note that any
  47. task that gets queued is guaranteed to
  48. generate kCompletedTmId status callback.
  49. 5) Closing the task manager should follow a two step
  50. process. First all active tasks should be ended and then
  51. cmTaskMgrDestroy() should be called.
  52. Ending all active tasks can be accomplished by sending
  53. kill signals via cmTaskMgrCtl(), use of cmTaskMgrClose()
  54. or if all tasks are guaranteed to eventually end - waiting
  55. for the task count to go to zero (See cmTaskMgrActiveTaskCount()).
  56. Note that waiting for the task count to go to zero may be
  57. error prone unless one can guarantee that no tasks are
  58. queued or that the last task started has completed.
  59. */
  60. enum
  61. {
  62. kOkTmRC,
  63. kThreadFailTmRC,
  64. kInvalidArgTmRC,
  65. kOpFailTmRC,
  66. kQueueFailTmRC,
  67. kAssertFailTmRC,
  68. kTestFailTmRC
  69. };
  70. typedef cmRC_t cmTmRC_t;
  71. typedef cmHandle_t cmTaskMgrH_t;
  72. extern cmTaskMgrH_t cmTaskMgrNullHandle;
  73. typedef enum
  74. {
  75. kInvalidTmId,
  76. kQueuedTmId, // The task is waiting in the queue.
  77. kStartedTmId, // The task is running.
  78. kPausedTmId, // The task is paused.
  79. kDeactivatedTmId, // The task was temporarily deactivated by the system
  80. kCompletedTmId, // The task successfully completed.
  81. kKilledTmId // The task was killed by the client.
  82. } cmStatusTmId_t;
  83. typedef enum
  84. {
  85. kStatusTmId, // Task status updates. These are automatically sent by the system when the task instance changes state.
  86. kProgTmId, // Task progress update. The user function should increment the 'prog' toward 'progCnt'.
  87. kErrorTmId, // Error message ('cmTaskMgrStatusArg_t.prog' has error result code)
  88. kMsgTmId // Msg from a task instance in cmTaskMgrStatusArg_t.msg[msgByteCnt].
  89. } cmSelTmId_t;
  90. typedef enum
  91. {
  92. kStartTmId,
  93. kPauseTmId,
  94. kKillTmId
  95. } cmTaskMgrCtlId_t;
  96. typedef struct cmTaskMgrStatusArg_str
  97. {
  98. void* arg; // Client status arg. as passed to cmTaskMgrCreate().
  99. unsigned instId; // Task instance id of the task which generated the callback.
  100. cmSelTmId_t selId; // See cmSelTmId_t.
  101. cmStatusTmId_t statusId; // See cmStatusTmId_t.
  102. unsigned prog; // selId==kProgTmId (0<=prog<=cmTaskMgrFuncArg_t.progCnt) selId=kErrorTmid prog=error result code.
  103. const cmChar_t* text; // Used by kErrorTmId.
  104. const void* msg; // Used by kMsgTmId. msg[msgByteCnt]
  105. unsigned msgByteCnt; // Count of bytes in msg[].
  106. } cmTaskMgrStatusArg_t;
  107. typedef void (*cmTaskMgrStatusCb_t)( const cmTaskMgrStatusArg_t* status );
  108. typedef struct cmTaskMgrFuncArg_str
  109. {
  110. void* reserved;
  111. void* arg; // 'funcArg' provided by cmTaskMgrCall();
  112. unsigned argByteCnt; // 'funcArgByteCnt' provided by cmTaskMgrCall()
  113. unsigned instId; // Task instance id.
  114. cmTaskMgrStatusCb_t statusCb; // Status update function provided by cmTaskMgrCreate().
  115. void* statusCbArg; // Status update function arg. provided by cmTaskMgrCreate().
  116. unsigned progCnt; // Maximum expected value of cmTaskMgrStatusArg_t.prog during execution of this task instance.
  117. unsigned pauseSleepMs; // Length of time to sleep if the task receives a pause command.
  118. } cmTaskMgrFuncArg_t;
  119. // Task process function.
  120. typedef void (*cmTaskMgrFunc_t)(cmTaskMgrFuncArg_t* arg );
  121. // Task message receive function.
  122. typedef void (*cmTaskMgrRecv_t)(cmTaskMgrFuncArg_t* arg, const void* msg, unsigned msgByteCnt );
  123. // Allocate the task manager.
  124. cmTmRC_t cmTaskMgrCreate(
  125. cmCtx_t* ctx, //
  126. cmTaskMgrH_t* hp, //
  127. cmTaskMgrStatusCb_t statusCb, // Task status callbacks.
  128. void* statusCbArg, // Status callback arg
  129. unsigned maxActiveTaskCnt, // Max. number of active tasks (see Usage notes above.)
  130. unsigned queueByteCnt, // Size of task client->taskMgr and taskMgr->client msg queues.
  131. unsigned pauseSleepMs ); // Scheduler sleep time. (20-50ms)
  132. // Calling cmTaskMgrDestroy() will send a 'kill' control message
  133. // to any existing worker threads. The threads will shutdown
  134. // gracefully but the task they were computing will not be completed.
  135. cmTmRC_t cmTaskMgrDestroy( cmTaskMgrH_t* hp );
  136. enum
  137. {
  138. kKillQueuedTmFl = 0x01, // Kill any queued (otherwise queued tasks will be started)
  139. kTimeOutKillTmFl = 0x02 // KIll any instance not completed before returning
  140. };
  141. // Wait for the current task instances to complete.
  142. // Set 'timeOutMs' to the number of milliseconds to wait for the current tasks to complete.
  143. // Set kKillQueueTmFl to kill any queued tasks otherwise queued tasks will run as usual.
  144. // Set kRefuseCallTmFl to refuse to queue any new tasks following the return from this function.
  145. // Set kTimeOutKillTmFl to kill any tasks that are still running after timeOutMs has expired
  146. // otherwise these tasks will still be running when the function returns.
  147. cmTmRC_t cmTaskMgrClose( cmTaskMgrH_t h, unsigned flags, unsigned timeOutMs );
  148. // Return the current number of active tasks.
  149. unsigned cmTaskMgrActiveTaskCount( cmTaskMgrH_t h );
  150. // Return true if the task manager handle is valid.
  151. bool cmTaskMgrIsValid( cmTaskMgrH_t h );
  152. // Given a statusId return a the associated label.
  153. const cmChar_t* cmTaskMgrStatusIdToLabel( cmStatusTmId_t statusId );
  154. // Called by the client to give the task mgr an opportunity to execute
  155. // period functions from within the client thread. Note that 'statusCb()'
  156. // (as passed to cmTaskMgrCreate()) is only called within this function
  157. // This guarantees that all communication with the client occurs in the
  158. // clients thread.
  159. cmTmRC_t cmTaskMgrOnIdle( cmTaskMgrH_t h );
  160. // Pause/unpause the task mgr.
  161. // This function pauses/unpauses each worker thread and then the master thread.
  162. // If waitFl is set then the function will not return until each of
  163. // the threads has entered the requested state. If 'waitFl' is false
  164. // The function will wait for the worker threads to pause but will
  165. // only signal the master thread to pause before returning.
  166. bool cmTaskMgrIsEnabled( cmTaskMgrH_t h );
  167. cmTmRC_t cmTaskMgrEnable( cmTaskMgrH_t h, bool enableFl );
  168. // Install a task function and associate it with a label and unique id.
  169. cmTmRC_t cmTaskMgrInstall(
  170. cmTaskMgrH_t h,
  171. unsigned taskId, // Unique task id.
  172. const cmChar_t* label, // (optional) Task label
  173. cmTaskMgrFunc_t func, // Task worker function.
  174. cmTaskMgrRecv_t recv); // (optional) Task message receive function.
  175. // Queue a new task instance.
  176. // The 'queued' status callback occurs from inside this call.
  177. // If this function completes successfully then the client is
  178. // guaranteed to get both a 'kQueuedTmId' status update and
  179. // and a 'kCompletedTmId' update. Any per task instance cleanup
  180. // can therefore be triggered by the 'kCompleteTmId' status callback.
  181. cmTmRC_t cmTaskMgrCall(
  182. cmTaskMgrH_t h,
  183. unsigned taskId, // Task id of a task previously registered by cmTaskMgrInstall().
  184. void* funcArg, // The value assigned to cmTaskMgrFuncArg_t.arg for this instance.
  185. unsigned progCnt, // Max. expected progress value to be eventually reported by this task instances 'kProgTmId' progress updates.
  186. unsigned queueByteCnt, // Size of the client->task message buffer.
  187. const cmChar_t* label, // (optional) Instance label.
  188. unsigned* retInstIdPtr ); // (optional) Unique id assigned to this instance.
  189. // Start,pause, or kill a task instance.
  190. //
  191. // If a queued task is paused then it will remain at the front
  192. // of the queue and tasks behind it in the queue may be executed.
  193. // See the usage note above regarding the interaction between
  194. // pausing/unpausing, activating/deactivating and the
  195. // maxActiveTaskCnt parameter.
  196. //
  197. // Note that killing a task causes it to terminate quickly
  198. // but this does not imply that the task ends in an uncontrolled
  199. // manner. Even killed tasks properly release any resource they
  200. // may hold prior to ending. For long running tasks which do not
  201. // have a natural stopping point prior to the end of the client
  202. // process using the kill signal to end the task is both convenient
  203. // and efficient.
  204. //
  205. // Once a task is paused it is safe to directly interact with
  206. // the data space it has access to (e.g. cmTaskMgrFuncArg_t.funcArg).
  207. // This is true because the client has control over when the task
  208. // may start again via the cmStartTmId signal. Note that this is
  209. // not the case for deactivated signals. Deactivated signals may
  210. // be restarted at any time. Note however that it is possible to
  211. // pause a deactivated task in which case it's data space may
  212. // be accessed as soon as the client is notified that the task
  213. // has switched to the paused state.
  214. cmTmRC_t cmTaskMgrCtl( cmTaskMgrH_t h, unsigned instId, cmTaskMgrCtlId_t ctlId );
  215. // Get the status of a task.
  216. cmStatusTmId_t cmTaskMgrStatus( cmTaskMgrH_t h, unsigned instId );
  217. // Send a thread-safe msg to a task instance.
  218. cmTmRC_t cmTaskMgrSendMsg( cmTaskMgrH_t h, unsigned instId, const void* msg, unsigned msgByteCnt );
  219. const cmChar_t* cmTaskMgrTaskIdToLabel( cmTaskMgrH_t h, unsigned taskId );
  220. const cmChar_t* cmTaskMgrInstIdToLabel( cmTaskMgrH_t h, unsigned instId );
  221. // The following functions are only valid when the task has completed and
  222. // has a status of either kCompletedTmId.
  223. const void* cmTaskMgrResult( cmTaskMgrH_t h, unsigned instId );
  224. unsigned cmTaskMgrResultByteCount( cmTaskMgrH_t h, unsigned instId );
  225. void* cmTaskMgrFuncArg( cmTaskMgrH_t h, unsigned instId );
  226. cmTmRC_t cmTaskMgrInstDelete( cmTaskMgrH_t h, unsigned instId );
  227. // -----------------------------------------------------------------------------------
  228. // Worker thread helper functions.
  229. //
  230. // These functions should only be called from inside the client supplied
  231. // worker function (cmTaskMgrFuncArg_t).
  232. //
  233. // There are two thread-safe methods the worker thread can use to receive
  234. // raw byte messages from the client.
  235. //
  236. // 1. Assign a cmTaskMgrRecv_t function at task installation
  237. // (See cmTaskMgrInstall().) This callback will be invoked from inside
  238. // cmTaskMgrWorkerHandleCommand()
  239. // if a client message is found to be waiting. When this occurs the
  240. // worker helper function will return kRecvTmwRC.
  241. //
  242. // 2. If the task was not assigned a cmTaskMgrRect_t function then
  243. // the worker should notice when
  244. // cmTaskMgrWorkerHandleCommand() returns cmRecvTmwRC. When this occurs
  245. // it should call cmTaskMgrMsgRecv() to copy the message into a
  246. // locally allocated buffer. cmTaskMgrWorkerMsgByteCount() can
  247. // be called to find out the size of the message and therefore
  248. // the minimum size of the copy destination buffer.
  249. typedef enum
  250. {
  251. kOkTmwRC,
  252. kStopTmwRC,
  253. kRecvTmwRC
  254. } cmTmWorkerRC_t;
  255. // The instance function cmTaskMgrFunc_t must poll this
  256. // function to respond to incoming commands and messages.
  257. // The polling rate should be determined by the application to
  258. // trade-off the cost of the poll versus the command execution
  259. // latency. Checking every 20 to 100 milliseconcds is probably
  260. // about right.
  261. // If the function returns 'kStopTmwRC' then the function has received
  262. // a kKillCtlId and should release any resources and return immediately.
  263. // If the function returns 'kRecvTmwRC' and has not registered a
  264. // cmTaskMgrRecv_t function then it should call cmTaskMgrWorkerRecv()
  265. // to pick up an incoming message from the client.
  266. // If the function returns 'kRecvTmwRC' and does have a registered
  267. // cmTaskMgrRecv_t function then a new message was received by the
  268. // cmTaskMGrRecv_t function.
  269. cmTmWorkerRC_t cmTaskMgrWorkerHandleCommand( cmTaskMgrFuncArg_t* a );
  270. // Send a task status update to the client.
  271. //cmTmRC_t cmTaskMgrWorkerSendStatus( cmTaskMgrFuncArg_t* a, cmStatusTmId_t statusId );
  272. // Send a progress update to the client.
  273. cmTmRC_t cmTaskMgrWorkerSendProgress( cmTaskMgrFuncArg_t* a, unsigned prog, const cmChar_t* text );
  274. cmTmRC_t cmTaskMgrWorkerSendProgressV( cmTaskMgrFuncArg_t* a, unsigned prog, const cmChar_t* fmt, va_list vl );
  275. cmTmRC_t cmTaskMgrWorkerSendProgressF( cmTaskMgrFuncArg_t* a, unsigned prog, const cmChar_t* fmt, ... );
  276. // Send an error message to the client. The result code is application
  277. // dependent. These function return 'rc' as does cmErrMsg().
  278. cmTmRC_t cmTaskMgrWorkerError( cmTaskMgrFuncArg_t* a, unsigned rc, const cmChar_t* text );
  279. cmTmRC_t cmTaskMgrWorkerErrorV( cmTaskMgrFuncArg_t* a, unsigned rc, const cmChar_t* fmt, va_list vl );
  280. cmTmRC_t cmTaskMgrWorkerErrorF( cmTaskMgrFuncArg_t* a, unsigned rc, const cmChar_t* fmt, ... );
  281. // Set the internal result buffer for this instance.
  282. // This is a convenient way to assign a final result to a instance
  283. // which can eventually be picked up by cmTaskMgrResult()
  284. // after the worker has officially completed. Alternatively the
  285. // result of the worker computation can be returned using
  286. // cmTaskMgrWorkerMsgSend().
  287. cmTmRC_t cmTaskMgrWorkerSetResult( cmTaskMgrFuncArg_t* a, void* result, unsigned resultByteCnt );
  288. // Get the size of an incoming message sent to this task instance
  289. // from the client. Use cmTaskMgrWorkerMsgRecv() to get
  290. // the msg. contents.
  291. unsigned cmTaskMgrWorkerMsgByteCount( cmTaskMgrFuncArg_t* a );
  292. // Copy a msg from the client into buf[bufByteCnt] and
  293. // return the count of bytes copied into buf[bufByteCnt] or cmInvalidCnt
  294. // if the buf[] was too small. This function is only used when
  295. // the task did notregister a cmTaskMgrRecv_t with cmTaskMgrInstall().
  296. // cmTaskMgrWorkerMsgByteCount() returns the minimum required size of buf[].
  297. unsigned cmTaskMgrWorkerMsgRecv( cmTaskMgrFuncArg_t* a, void* buf, unsigned bufByteCnt );
  298. // Send a generic msg to the client.
  299. cmTmWorkerRC_t cmTaskMgrWorkerMsgSend( cmTaskMgrFuncArg_t* a, const void* buf, unsigned bufByteCnt );
  300. cmTmRC_t cmTaskMgrTest(cmCtx_t* ctx);
  301. //)
  302. #ifdef __cplusplus
  303. }
  304. #endif
  305. #endif