Browse Source

cmTaskMgr.h/c : Many updates and changes to complete the initial development.

master
kpl 10 years ago
parent
commit
53948d977d
2 changed files with 808 additions and 225 deletions
  1. 567
    195
      cmTaskMgr.c
  2. 241
    30
      cmTaskMgr.h

+ 567
- 195
cmTaskMgr.c
File diff suppressed because it is too large
View File


+ 241
- 30
cmTaskMgr.h View File

@@ -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
 

Loading…
Cancel
Save