Copyright (C) Kevin Larke 2009-2020
This file is part of libcm.
libcm is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
libcm is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
See the GNU General Public License distributed with the libcm
package or look here:
// enum { kOkTmRC, kThreadFailTmRC, kInvalidArgTmRC, kOpFailTmRC, kQueueFailTmRC, kAssertFailTmRC, kTestFailTmRC }; typedef cmRC_t cmTmRC_t; typedef cmHandle_t cmTaskMgrH_t; extern cmTaskMgrH_t cmTaskMgrNullHandle; typedef enum { kInvalidTmId, kQueuedTmId, // The task is waiting in the queue. kStartedTmId, // The task is running. kPausedTmId, // The task is paused. kDeactivatedTmId, // The task was temporarily deactivated by the system kCompletedTmId, // The task successfully completed. kKilledTmId // The task was killed by the client. } cmStatusTmId_t; typedef enum { kStatusTmId, // Task status updates. These are automatically sent by the system when the task instance changes state. kProgTmId, // Task progress update. The user function should increment the 'prog' toward 'progCnt'. kErrorTmId, // Error message ('cmTaskMgrStatusArg_t.prog' has error result code) kMsgTmId // Msg from a task instance in cmTaskMgrStatusArg_t.msg[msgByteCnt]. } cmSelTmId_t; typedef enum { kStartTmId, kPauseTmId, kKillTmId } cmTaskMgrCtlId_t; typedef struct cmTaskMgrStatusArg_str { void* arg; // Client status arg. as passed to cmTaskMgrCreate(). unsigned instId; // Task instance id of the task which generated the callback. cmSelTmId_t selId; // See cmSelTmId_t. cmStatusTmId_t statusId; // See cmStatusTmId_t. unsigned prog; // selId==kProgTmId (0&lt=prog&lt=cmTaskMgrFuncArg_t.progCnt) selId=kErrorTmid prog=error result code. const cmChar_t* text; // Used by kErrorTmId. const void* msg; // Used by kMsgTmId. msg[msgByteCnt] unsigned msgByteCnt; // Count of bytes in msg[]. } cmTaskMgrStatusArg_t; typedef void (*cmTaskMgrStatusCb_t)( const cmTaskMgrStatusArg_t* status ); typedef struct cmTaskMgrFuncArg_str { void* reserved; void* arg; // 'funcArg' provided by cmTaskMgrCall(); unsigned argByteCnt; // 'funcArgByteCnt' provided by cmTaskMgrCall() unsigned instId; // Task instance id. cmTaskMgrStatusCb_t statusCb; // Status update function provided by cmTaskMgrCreate(). void* statusCbArg; // Status update function arg. provided by cmTaskMgrCreate(). unsigned progCnt; // Maximum expected value of cmTaskMgrStatusArg_t.prog during execution of this task instance. unsigned pauseSleepMs; // Length of time to sleep if the task receives a pause command. } cmTaskMgrFuncArg_t; // Task process function. typedef void (*cmTaskMgrFunc_t)(cmTaskMgrFuncArg_t* arg ); // Task message receive function. typedef void (*cmTaskMgrRecv_t)(cmTaskMgrFuncArg_t* arg, const void* msg, unsigned msgByteCnt ); // Allocate the task manager. cmTmRC_t cmTaskMgrCreate( cmCtx_t* ctx, cmTaskMgrH_t* hp, cmTaskMgrStatusCb_t statusCb, // Task status callbacks. void* statusCbArg, // Status callback arg unsigned maxActiveTaskCnt, // Max. number of active tasks (see Usage notes above.) unsigned queueByteCnt, // Size of task client-&gttaskMgr and taskMgr-&gtclient msg queues. unsigned pauseSleepMs ); // Scheduler sleep time. (20-50ms) // Calling cmTaskMgrDestroy() will send a 'kill' control message // to any existing worker threads. The threads will shutdown // gracefully but the task they were computing will not be completed. cmTmRC_t cmTaskMgrDestroy( cmTaskMgrH_t* hp ); enum { kKillQueuedTmFl = 0x01, // Kill any queued (otherwise queued tasks will be started) kTimeOutKillTmFl = 0x02 // KIll any instance not completed before returning }; // Wait for the current task instances to complete. // Set 'timeOutMs' to the number of milliseconds to wait for the current tasks to complete. // Set kKillQueueTmFl to kill any queued tasks otherwise queued tasks will run as usual. // Set kRefuseCallTmFl to refuse to queue any new tasks following the return from this function. // Set kTimeOutKillTmFl to kill any tasks that are still running after timeOutMs has expired // otherwise these tasks will still be running when the function returns. cmTmRC_t cmTaskMgrClose( cmTaskMgrH_t h, unsigned flags, unsigned timeOutMs ); // Return the current number of active tasks. unsigned cmTaskMgrActiveTaskCount( cmTaskMgrH_t h ); // Return true if the task manager handle is valid. bool cmTaskMgrIsValid( cmTaskMgrH_t h ); // Given a statusId return a the associated label. const cmChar_t* cmTaskMgrStatusIdToLabel( cmStatusTmId_t statusId ); // Called by the client to give the task mgr an opportunity to execute // period functions from within the client thread. Note that 'statusCb()' // (as passed to cmTaskMgrCreate()) is only called within this function // This guarantees that all communication with the client occurs in the // clients thread. cmTmRC_t cmTaskMgrOnIdle( cmTaskMgrH_t h ); // Pause/unpause the task mgr. // This function pauses/unpauses each worker thread and then the master thread. // If waitFl is set then the function will not return until each of // the threads has entered the requested state. If 'waitFl' is false // The function will wait for the worker threads to pause but will // only signal the master thread to pause before returning. bool cmTaskMgrIsEnabled( cmTaskMgrH_t h ); cmTmRC_t cmTaskMgrEnable( cmTaskMgrH_t h, bool enableFl ); // Install a task function and associate it with a label and unique id. cmTmRC_t cmTaskMgrInstall( cmTaskMgrH_t h, unsigned taskId, // Unique task id. const cmChar_t* label, // (optional) Task label cmTaskMgrFunc_t func, // Task worker function. cmTaskMgrRecv_t recv); // (optional) Task message receive function. // Queue a new task instance. // The 'queued' status callback occurs from inside this call. // If this function completes successfully then the client is // guaranteed to get both a 'kQueuedTmId' status update and // and a 'kCompletedTmId' update. Any per task instance cleanup // can therefore be triggered by the 'kCompleteTmId' status callback. cmTmRC_t cmTaskMgrCall( cmTaskMgrH_t h, unsigned taskId, // Task id of a task previously registered by cmTaskMgrInstall(). void* funcArg, // The value assigned to cmTaskMgrFuncArg_t.arg for this instance. unsigned progCnt, // Max. expected progress value to be eventually reported by this task instances 'kProgTmId' progress updates. unsigned queueByteCnt, // Size of the client-&gttask message buffer. const cmChar_t* label, // (optional) Instance label. unsigned* retInstIdPtr ); // (optional) Unique id assigned to this instance. // Start,pause, or kill a task instance. // // If a queued task is paused then it will remain at the front // of the queue and tasks behind it in the queue may be executed. // See the usage note above regarding the interaction between // pausing/unpausing, activating/deactivating and the // maxActiveTaskCnt parameter. // // Note that killing a task causes it to terminate quickly // but this does not imply that the task ends in an uncontrolled // manner. Even killed tasks properly release any resource they // may hold prior to ending. For long running tasks which do not // have a natural stopping point prior to the end of the client // process using the kill signal to end the task is both convenient // and efficient. // // Once a task is paused it is safe to directly interact with // the data space it has access to (e.g. cmTaskMgrFuncArg_t.funcArg). // This is true because the client has control over when the task // may start again via the cmStartTmId signal. Note that this is // not the case for deactivated signals. Deactivated signals may // be restarted at any time. Note however that it is possible to // pause a deactivated task in which case it's data space may // be accessed as soon as the client is notified that the task // has switched to the paused state. cmTmRC_t cmTaskMgrCtl( cmTaskMgrH_t h, unsigned instId, cmTaskMgrCtlId_t ctlId ); // Get the status of a task. cmStatusTmId_t cmTaskMgrStatus( cmTaskMgrH_t h, unsigned instId ); // Send a thread-safe msg to a task instance. cmTmRC_t cmTaskMgrSendMsg( cmTaskMgrH_t h, unsigned instId, const void* msg, unsigned msgByteCnt ); const cmChar_t* cmTaskMgrTaskIdToLabel( cmTaskMgrH_t h, unsigned taskId ); const cmChar_t* cmTaskMgrInstIdToLabel( cmTaskMgrH_t h, unsigned instId ); // The following functions are only valid when the task has completed and // has a status of either kCompletedTmId. const void* cmTaskMgrResult( cmTaskMgrH_t h, unsigned instId ); unsigned cmTaskMgrResultByteCount( cmTaskMgrH_t h, unsigned instId ); void* cmTaskMgrFuncArg( cmTaskMgrH_t h, unsigned instId ); cmTmRC_t cmTaskMgrInstDelete( cmTaskMgrH_t h, unsigned instId ); // ----------------------------------------------------------------------------------- // Worker thread helper functions. // // These functions should only be called from inside the client supplied // worker function (cmTaskMgrFuncArg_t). // // There are two thread-safe methods the worker thread can use to receive // raw byte messages from the client. // // 1. Assign a cmTaskMgrRecv_t function at task installation // (See cmTaskMgrInstall().) This callback will be invoked from inside // cmTaskMgrWorkerHandleCommand() // if a client message is found to be waiting. When this occurs the // worker helper function will return kRecvTmwRC. // // 2. If the task was not assigned a cmTaskMgrRect_t function then // the worker should notice when // cmTaskMgrWorkerHandleCommand() returns cmRecvTmwRC. When this occurs // it should call cmTaskMgrMsgRecv() to copy the message into a // locally allocated buffer. cmTaskMgrWorkerMsgByteCount() can // be called to find out the size of the message and therefore // the minimum size of the copy destination buffer. typedef enum { kOkTmwRC, kStopTmwRC, kRecvTmwRC } cmTmWorkerRC_t; // The instance function cmTaskMgrFunc_t must poll this // function to respond to incoming commands and messages. // The polling rate should be determined by the application to // trade-off the cost of the poll versus the command execution // latency. Checking every 20 to 100 milliseconcds is probably // about right. // If the function returns 'kStopTmwRC' then the function has received // a kKillCtlId and should release any resources and return immediately. // If the function returns 'kRecvTmwRC' and has not registered a // cmTaskMgrRecv_t function then it should call cmTaskMgrWorkerRecv() // to pick up an incoming message from the client. // If the function returns 'kRecvTmwRC' and does have a registered // cmTaskMgrRecv_t function then a new message was received by the // cmTaskMGrRecv_t function. cmTmWorkerRC_t cmTaskMgrWorkerHandleCommand( cmTaskMgrFuncArg_t* a ); // Send a task status update to the client. //cmTmRC_t cmTaskMgrWorkerSendStatus( cmTaskMgrFuncArg_t* a, cmStatusTmId_t statusId ); // Send a progress update to the client. cmTmRC_t cmTaskMgrWorkerSendProgress( cmTaskMgrFuncArg_t* a, unsigned prog, const cmChar_t* text ); cmTmRC_t cmTaskMgrWorkerSendProgressV( cmTaskMgrFuncArg_t* a, unsigned prog, const cmChar_t* fmt, va_list vl ); cmTmRC_t cmTaskMgrWorkerSendProgressF( cmTaskMgrFuncArg_t* a, unsigned prog, const cmChar_t* fmt, ... ); // Send an error message to the client. The result code is application // dependent. These function return 'rc' as does cmErrMsg(). cmTmRC_t cmTaskMgrWorkerError( cmTaskMgrFuncArg_t* a, unsigned rc, const cmChar_t* text ); cmTmRC_t cmTaskMgrWorkerErrorV( cmTaskMgrFuncArg_t* a, unsigned rc, const cmChar_t* fmt, va_list vl ); cmTmRC_t cmTaskMgrWorkerErrorF( cmTaskMgrFuncArg_t* a, unsigned rc, const cmChar_t* fmt, ... ); // Set the internal result buffer for this instance. // This is a convenient way to assign a final result to a instance // which can eventually be picked up by cmTaskMgrResult() // after the worker has officially completed. Alternatively the // result of the worker computation can be returned using // cmTaskMgrWorkerMsgSend(). cmTmRC_t cmTaskMgrWorkerSetResult( cmTaskMgrFuncArg_t* a, void* result, unsigned resultByteCnt ); // Get the size of an incoming message sent to this task instance // from the client. Use cmTaskMgrWorkerMsgRecv() to get // the msg. contents. unsigned cmTaskMgrWorkerMsgByteCount( cmTaskMgrFuncArg_t* a ); // Copy a msg from the client into buf[bufByteCnt] and // return the count of bytes copied into buf[bufByteCnt] or cmInvalidCnt // if the buf[] was too small. This function is only used when // the task did notregister a cmTaskMgrRecv_t with cmTaskMgrInstall(). // cmTaskMgrWorkerMsgByteCount() returns the minimum required size of buf[]. unsigned cmTaskMgrWorkerMsgRecv( cmTaskMgrFuncArg_t* a, void* buf, unsigned bufByteCnt ); // Send a generic msg to the client. cmTmWorkerRC_t cmTaskMgrWorkerMsgSend( cmTaskMgrFuncArg_t* a, const void* buf, unsigned bufByteCnt ); cmTmRC_t cmTaskMgrTest(cmCtx_t* ctx);