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