Просмотр исходного кода

cmTaskMgr.h/c : Initial commit. Not yet debugged.

master
kpl 10 лет назад
Родитель
Сommit
dbf55083fa
2 измененных файлов: 928 добавлений и 0 удалений
  1. 797
    0
      cmTaskMgr.c
  2. 131
    0
      cmTaskMgr.h

+ 797
- 0
cmTaskMgr.c Просмотреть файл

@@ -0,0 +1,797 @@
1
+#include "cmGlobal.h"
2
+#include "cmRpt.h"
3
+#include "cmErr.h"
4
+#include "cmCtx.h"
5
+#include "cmMem.h"
6
+#include "cmMallocDebug.h"
7
+#include "cmThread.h"
8
+#include "cmTaskMgr.h"
9
+
10
+cmTaskMgrH_t cmTaskMgrNullHandle = cmSTATIC_NULL_HANDLE;
11
+
12
+struct cmTmInst_str;
13
+
14
+typedef struct cmTmTask_str
15
+{
16
+  unsigned             taskId;
17
+  cmChar_t*            label; 
18
+  cmTaskMgrFunc_t      func;
19
+  struct cmTmTask_str* link;
20
+} cmTmTask_t;
21
+
22
+typedef struct cmTmInst_str
23
+{
24
+  unsigned             instId;
25
+  struct cmTmTask_str* task;
26
+  void*                funcArg;
27
+  unsigned             progCnt;
28
+  cmStatusTmId_t       status;
29
+  void*                result;
30
+  unsigned             resultByteCnt;
31
+  cmTaskMgrCtlId_t     ctlId;
32
+  struct cmTmInst_str* link;
33
+} cmTmInst_t;
34
+
35
+struct cmTm_str* p;
36
+
37
+typedef struct cmTmThread_str
38
+{
39
+  struct cmTm_str* p;      // 
40
+  cmThreadH_t      thH;    // 
41
+  cmTmInst_t*      inst;   // Ptr to the task instance this thread is executing.
42
+} cmTmThread_t;
43
+
44
+typedef struct cmTm_str
45
+{
46
+  cmErr_t             err;
47
+  cmTmThread_t*       thArray;      // 
48
+  unsigned            threadCnt;        //
49
+  cmTaskMgrStatusCb_t statusCb;     // 
50
+  void*               statusCbArg;  // 
51
+  unsigned            pauseSleepMs;
52
+  cmTs1p1cH_t         inQueH;       // client->mgr
53
+  cmTsMp1cH_t         outQueH;      // mgr->client
54
+  cmTmTask_t*         tasks;        //
55
+  cmTmInst_t*         insts;        //
56
+  unsigned            nextInstId;
57
+} cmTm_t;
58
+
59
+// WARNING: THIS FUNCTION IS CALLED BY BOTH THE WORKER AND THE MASTER THREAD.
60
+cmTmRC_t _cmTmEnqueueStatusMsg0( cmTm_t* p, const cmTaskMgrStatusArg_t* s )
61
+{
62
+  enum { arrayCnt = 3 };
63
+  const void*    msgPtrArray[arrayCnt];
64
+  unsigned       msgSizeArray[arrayCnt];
65
+
66
+  msgPtrArray[0] = s;
67
+  msgPtrArray[1] = s->msg==NULL ? "" : s->msg;
68
+  msgPtrArray[2] = s->result;
69
+
70
+  msgSizeArray[0] = sizeof(*s);
71
+  msgSizeArray[1] = s->msg==NULL ? 1 : strlen(s->msg)+1;
72
+  msgSizeArray[2] = s->resultByteCnt;
73
+
74
+
75
+  if( cmTsMp1cEnqueueSegMsg(p->outQueH, msgPtrArray, msgSizeArray, arrayCnt ) != kOkThRC )
76
+    return kQueueFailTmRC;
77
+  
78
+  return kOkTmRC;
79
+}
80
+
81
+
82
+// This function is called by the worker thread wrapper _cmTmWorkerStatusCb()
83
+// function to enqueue messages being sent back to the client.
84
+cmTmRC_t _cmTmEnqueueStatusMsg1( 
85
+  cmTm_t*         p,
86
+  unsigned        instId,
87
+  cmSelTmId_t     selId,
88
+  cmStatusTmId_t  statusId,
89
+  unsigned        prog,
90
+  const cmChar_t* msg,
91
+  void*           result,
92
+  unsigned        resultByteCnt )
93
+{
94
+  cmTaskMgrStatusArg_t s;
95
+  s.arg           = p->statusCbArg;
96
+  s.instId        = instId;
97
+  s.selId         = selId;
98
+  s.statusId      = statusId;
99
+  s.msg           = msg;
100
+  s.result        = result;
101
+  s.resultByteCnt = resultByteCnt;
102
+  return _cmTmEnqueueStatusMsg0(p,&s);
103
+}
104
+
105
+// Worker threads call this function to enqueue status messages
106
+// for delivery to the task mgr client.
107
+void _cmTmWorkerStatusCb( const cmTaskMgrStatusArg_t* status )
108
+{
109
+  cmTmThread_t* trp = (cmTmThread_t*)status->arg;
110
+
111
+  if( _cmTmEnqueueStatusMsg0( trp->p, status ) != kOkTmRC )
112
+  {
113
+    /// ??????? HOW DO WE HANDLE ERRORS IN THE WORKER THREAD
114
+    /// (set an error code in trp and let the master thread notice it.)
115
+    assert(0);
116
+  }
117
+
118
+}
119
+
120
+
121
+// This is the wrapper for all worker threads.
122
+bool _cmTmWorkerThreadFunc(void* arg)
123
+{
124
+  cmTmThread_t*      trp = (cmTmThread_t*)arg;
125
+  cmTaskMgrFuncArg_t r;
126
+
127
+  r.arg         = trp->inst->funcArg;
128
+  r.instId      = trp->inst->instId;
129
+  r.statusCb    = _cmTmWorkerStatusCb;
130
+  r.statusCbArg = trp;
131
+  r.progCnt     = trp->inst->progCnt;
132
+  r.cmdIdPtr    = &trp->inst->ctlId;
133
+  r.pauseSleepMs= trp->p->pauseSleepMs;
134
+
135
+  // Notify the client that the instance has started.
136
+  _cmTmEnqueueStatusMsg1(trp->p,trp->inst->instId,kStatusTmId,kStartedTmId,0,NULL,NULL,0);
137
+
138
+  // Execute the client provided task function.
139
+  trp->inst->task->func(&r);
140
+
141
+  // Notify the client that the instance has completed or been killed
142
+  if( trp->inst->ctlId == kKillTmId )
143
+    _cmTmEnqueueStatusMsg1(trp->p,trp->inst->instId,kStatusTmId,kKilledTmId,0,NULL,NULL,0);
144
+  else
145
+    _cmTmEnqueueStatusMsg1(trp->p,trp->inst->instId,kStatusTmId,kCompletedTmId,0,NULL,NULL,0);
146
+
147
+  // Force the thread to go into the 'pause' state when it 
148
+  // returns to it's internal loop. The master thread recognizes paused
149
+  // threads as available.
150
+  cmThreadPause(trp->thH,kPauseThFl);
151
+
152
+  return true;
153
+}
154
+
155
+
156
+// This is the master thread function.
157
+bool _cmTmMasterThreadFunc(void* arg)
158
+{
159
+  cmTmThread_t* trp = (cmTmThread_t*)arg;
160
+  cmTm_t*         p = trp->p;
161
+
162
+  while( cmTs1p1cMsgWaiting(p->inQueH) )
163
+  {
164
+    unsigned i;
165
+    cmTmInst_t* ip = NULL;
166
+
167
+    // find an available worker thread
168
+    for(i=1; i<p->threadCnt; ++i)
169
+      if( cmThreadState(p->thArray[i].thH) == kPausedThId )
170
+        break;
171
+
172
+    // if all worker threads are busy ... give up
173
+    if( i==p->threadCnt )
174
+      break;
175
+
176
+    // dequeue a pending task instance pointer from the input queue
177
+    if(cmTs1p1cDequeueMsg(p->inQueH,&ip,sizeof(ip)) != kOkThRC )
178
+    {
179
+      /// ??????? HOW DO WE HANDLE ERRORS IN THE MASTER THREAD
180
+      continue;
181
+    }
182
+
183
+    // assign the instance to the available thread
184
+    p->thArray[i].inst = ip;
185
+    
186
+    // start the thread and wait for it to enter the running state.
187
+    
188
+    if( cmThreadPause(p->thArray[i].thH,kWaitThFl) != kOkThRC )
189
+    {
190
+      /// ??????? HOW DO WE HANDLE ERRORS IN THE MASTER THREAD
191
+    }
192
+
193
+  }
194
+
195
+  return true;
196
+}
197
+
198
+
199
+cmTm_t* _cmTmHandleToPtr( cmTaskMgrH_t h )
200
+{
201
+  cmTm_t* p = (cmTm_t*)h.h;
202
+  assert( p != NULL );
203
+  return p;
204
+}
205
+
206
+cmTmTask_t* _cmTmTaskFromId( cmTm_t* p, unsigned taskId )
207
+{
208
+  cmTmTask_t* tp;
209
+  for(tp=p->tasks; tp!=NULL; tp=tp->link)
210
+    if( tp->taskId == taskId )
211
+      return tp;
212
+  return NULL;
213
+}
214
+
215
+cmTmInst_t* _cmTmInstFromId( cmTm_t* p, unsigned instId )
216
+{
217
+  cmTmInst_t* ip;
218
+  for(ip=p->insts; ip!=NULL; ip=ip->link)
219
+    if( ip->instId == instId )
220
+      return ip;
221
+  return NULL;
222
+}
223
+
224
+
225
+cmTmRC_t _cmTmInstFree( cmTm_t* p, unsigned instId )
226
+{
227
+  cmTmRC_t    rc = kOkTmRC;
228
+  cmTmInst_t* ip = p->insts;  
229
+  cmTmInst_t* pp = NULL;
230
+
231
+  for(; ip!=NULL; ip=ip->link)
232
+  {
233
+    if( ip->instId == instId )
234
+    {
235
+      if( pp == NULL )
236
+        p->insts = ip->link;
237
+      else
238
+        pp->link = ip->link;
239
+
240
+      cmMemFree(ip->result);
241
+      cmMemFree(ip);
242
+      break;
243
+    }
244
+    pp = ip;
245
+  }
246
+
247
+  return rc;
248
+}
249
+
250
+
251
+cmTmRC_t _cmTmDestroy( cmTm_t* p )
252
+{
253
+  cmTmRC_t rc = kOkTmRC;
254
+  unsigned i;
255
+
256
+  // stop and destroy all the threads
257
+  for(i=0; i<p->threadCnt; ++i)
258
+  {
259
+    if( cmThreadDestroy(&p->thArray[i].thH) != kOkThRC )
260
+    {
261
+      rc = cmErrMsg(&p->err,kThreadFailTmRC,"Thread index %i destroy failed.",i);
262
+      goto errLabel;
263
+    }
264
+  }
265
+
266
+  cmMemFree(p->thArray);
267
+
268
+  // release the input queue
269
+  if( cmTs1p1cDestroy(&p->inQueH) != kOkThRC )
270
+  {
271
+    rc = cmErrMsg(&p->err,kQueueFailTmRC,"The input queue destroy failed.");
272
+    goto errLabel;
273
+  }
274
+
275
+  // draining the output queue
276
+  while( cmTsMp1cMsgWaiting(p->outQueH) )
277
+    if(cmTsMp1cDequeueMsg(p->outQueH,NULL,0) != kOkThRC )
278
+      cmErrMsg(&p->err,kQueueFailTmRC,"The output queue failed while draingin.");
279
+
280
+  // release the output queue
281
+  if( cmTsMp1cDestroy(&p->outQueH) != kOkThRC )
282
+  {
283
+    rc = cmErrMsg(&p->err,kQueueFailTmRC,"The input queue destroy failed.");
284
+    goto errLabel;
285
+  }
286
+
287
+
288
+  // release instance list
289
+  while( p->insts != NULL )
290
+    _cmTmInstFree(p,p->insts->instId);
291
+
292
+  // release the task list
293
+  cmTmTask_t* tp = p->tasks;
294
+  while( tp != NULL )
295
+  {
296
+    cmTmTask_t* np = tp->link;
297
+    cmMemFree(tp->label);
298
+    cmMemFree(tp);
299
+    tp = np;
300
+  }
301
+
302
+  cmMemFree(p);
303
+ errLabel:
304
+  return rc;
305
+}
306
+
307
+cmRC_t _cmTmMasterOutQueueCb(void* arg, unsigned msgByteCnt, const void* msgDataPtr );
308
+
309
+cmTmRC_t cmTaskMgrCreate(  
310
+  cmCtx_t*             ctx, 
311
+  cmTaskMgrH_t*        hp, 
312
+  cmTaskMgrStatusCb_t  statusCb, 
313
+  void*                statusCbArg, 
314
+  unsigned             threadCnt,
315
+  unsigned             queueByteCnt,
316
+  unsigned             pauseSleepMs)
317
+{
318
+  cmTmRC_t rc = kOkTmRC;
319
+  unsigned i;
320
+
321
+  if((rc = cmTaskMgrDestroy(hp)) != kOkTmRC )
322
+    return rc;
323
+
324
+  cmTm_t* p = cmMemAllocZ(cmTm_t,1);
325
+ 
326
+  cmErrSetup(&p->err,&ctx->rpt,"Task Mgr.");
327
+
328
+  threadCnt += 1;
329
+  p->thArray      = cmMemAllocZ(cmTmThread_t,threadCnt);
330
+  p->threadCnt    = threadCnt;
331
+  p->statusCb     = statusCb;
332
+  p->statusCbArg  = statusCbArg;
333
+  p->pauseSleepMs = pauseSleepMs;
334
+
335
+  // create the threads
336
+  for(i=0; i<threadCnt; ++i)
337
+  {
338
+    if( cmThreadCreate(&p->thArray[i].thH, i==0 ? _cmTmMasterThreadFunc : _cmTmWorkerThreadFunc,  p->thArray+i, &ctx->rpt ) != kOkThRC )
339
+    {
340
+      rc = cmErrMsg(&p->err,kThreadFailTmRC,"Thread index %i create failed.",i);
341
+      goto errLabel;
342
+    }
343
+
344
+    p->thArray[i].p = p;
345
+
346
+  }
347
+
348
+  // create the input queue
349
+  if(cmTs1p1cCreate( &p->inQueH, queueByteCnt, NULL, NULL, p->err.rpt ) != kOkThRC )
350
+  {
351
+    rc = cmErrMsg(&p->err,kQueueFailTmRC,"The input queue creation failed.");
352
+    goto errLabel;
353
+  }
354
+
355
+  // create the output queue
356
+  if( cmTsMp1cCreate( &p->outQueH, queueByteCnt, _cmTmMasterOutQueueCb, p, p->err.rpt ) != kOkThRC )
357
+  {
358
+    rc = cmErrMsg(&p->err,kQueueFailTmRC,"The output queue creation failed.");
359
+    goto errLabel;
360
+  }
361
+
362
+ errLabel:
363
+  return rc;  
364
+}
365
+
366
+cmTmRC_t cmTaskMgrDestroy( cmTaskMgrH_t* hp )
367
+{
368
+  cmTmRC_t rc = kOkTmRC;
369
+  if( hp!=NULL || cmTaskMgrIsValid(*hp)==false )
370
+    return rc;
371
+
372
+  cmTm_t* p = _cmTmHandleToPtr(*hp);
373
+
374
+  if((rc = _cmTmDestroy(p)) != kOkTmRC )
375
+    return rc;
376
+
377
+  hp->h = NULL;
378
+
379
+  return rc;
380
+}
381
+
382
+bool     cmTaskMgrIsValid(   cmTaskMgrH_t h )
383
+{ return h.h != NULL; }
384
+
385
+
386
+// This function is called by cmTaskMgrIdle() to dispatch
387
+// status updates to the client.
388
+cmRC_t _cmTmMasterOutQueueCb(void* arg, unsigned msgByteCnt, const void* msgDataPtr )
389
+{
390
+  cmTm_t* p = (cmTm_t*)arg;
391
+  cmTaskMgrStatusArg_t s;
392
+
393
+  // This is probably not nesessary since changing the memory  
394
+  // pointed to by msgDataPtr should be safe even though it is marked as const.
395
+  memcpy(&s,&msgDataPtr,sizeof(s));
396
+
397
+  // The 'msg' and 'result' data have been serialized after the status record.
398
+  // The 'msg' is guaranteed to at least contain a terminating zero.
399
+  s.msg = ((char*)msgDataPtr) + sizeof(s);
400
+
401
+  // if the 'resultByteCnt' > 0 then there is a result record
402
+  if( s.resultByteCnt > 0 )
403
+    s.result = ((char*)msgDataPtr) + sizeof(s) + strlen(s.msg) + 1;
404
+  else
405
+    s.result = NULL;
406
+
407
+  s.arg = p->statusCbArg;
408
+
409
+  p->statusCb( &s );
410
+  return cmOkRC;
411
+}
412
+
413
+
414
+cmTmRC_t cmTaskMgrOnIdle( cmTaskMgrH_t h )
415
+{
416
+  cmTmRC_t rc = kOkTmRC;
417
+  cmTm_t*  p  = _cmTmHandleToPtr(h);
418
+
419
+  while( cmTsMp1cMsgWaiting(p->outQueH) )
420
+  {
421
+    // calling this function calls: _cmTmMasterOutQueueCb()
422
+    if(cmTsMp1cDequeueMsg(p->outQueH,NULL,0) != kOkThRC )
423
+    {
424
+      rc = cmErrMsg(&p->err,kQueueFailTmRC,"The output queue failed during a dequeue.");
425
+      goto errLabel;
426
+    }
427
+
428
+    
429
+  }
430
+
431
+ errLabel:
432
+  return rc;
433
+}
434
+
435
+bool     cmTaskMgrIsEnabled( cmTaskMgrH_t h )
436
+{
437
+  cmTm_t*  p = _cmTmHandleToPtr(h);
438
+
439
+  if( cmThreadState(p->thArray[0].thH) != kPausedThId )
440
+    return false;
441
+
442
+  return true;
443
+}
444
+
445
+cmTmRC_t cmTaskMgrEnable(    cmTaskMgrH_t h, bool enableFl )
446
+{
447
+  cmTmRC_t rc    = kOkTmRC;
448
+  cmTm_t*  p     = _cmTmHandleToPtr(h);
449
+  unsigned flags = (enableFl ? 0 : kPauseThFl) | kWaitThFl;
450
+
451
+  if( cmThreadPause(p->thArray[0].thH, flags ) != kOkThRC )
452
+    rc = cmErrMsg(&p->err,kThreadFailTmRC,"The master thread failed to %s.",enableFl ? "enable" : "disable" );
453
+
454
+  return rc;
455
+}
456
+
457
+cmTmRC_t cmTaskMgrInstall( cmTaskMgrH_t h, unsigned taskId, const cmChar_t* label, cmTaskMgrFunc_t func )
458
+{
459
+  cmTmRC_t    rc = kOkTmRC;
460
+  cmTm_t*     p  = _cmTmHandleToPtr(h);
461
+  cmTmTask_t* tp = cmMemAllocZ(cmTmTask_t,1);
462
+
463
+  if( _cmTmTaskFromId(p,taskId) != NULL )
464
+  {
465
+    rc = cmErrMsg(&p->err,kInvalidArgTmRC,"The task id %i is already in use.",taskId);
466
+    goto errLabel;
467
+  }
468
+
469
+  tp->taskId = taskId;
470
+  tp->func   = func;
471
+  tp->label  = cmMemAllocStr(label);
472
+  
473
+  tp->link   = p->tasks;
474
+  p->tasks   = tp;
475
+  
476
+ errLabel:
477
+  return rc;
478
+}
479
+
480
+cmTmRC_t cmTaskMgrCall( 
481
+  cmTaskMgrH_t h, 
482
+  unsigned     taskId, 
483
+  void*        funcArg, 
484
+  unsigned     progCnt, 
485
+  unsigned*    retInstIdPtr )
486
+{
487
+  cmTmRC_t    rc = kOkTmRC;
488
+  cmTm_t*     p  = _cmTmHandleToPtr(h);
489
+  cmTmTask_t* tp = NULL;
490
+  cmTmInst_t* ip = NULL;
491
+
492
+  if((tp = _cmTmTaskFromId(p,taskId)) != NULL )
493
+  {
494
+    rc = cmErrMsg(&p->err,kInvalidArgTmRC,"Task not found for task id=%i.",taskId);
495
+    goto errLabel;
496
+  }
497
+
498
+  ip = cmMemAllocZ(cmTmInst_t,1);
499
+
500
+  ip->instId         = p->nextInstId++;
501
+  ip->task           = tp;
502
+  ip->funcArg        = funcArg;
503
+  ip->progCnt        = progCnt;
504
+  ip->status         = kQueuedTmId;
505
+
506
+  if( p->insts == NULL )
507
+    p->insts = ip;
508
+  else
509
+  {
510
+    cmTmInst_t* pp = p->insts;
511
+    while( pp != NULL )
512
+      if( pp->link == NULL )
513
+        pp->link = ip;
514
+  }
515
+
516
+
517
+  // enque the instance ptr in the input queue 
518
+  if( cmTs1p1cEnqueueMsg(p->inQueH,&ip,sizeof(ip)) != kOkThRC )
519
+  {
520
+    rc = cmErrMsg(&p->err,kQueueFailTmRC,"New task instance command enqueue failed.");    
521
+    goto errLabel;
522
+  }
523
+
524
+  // notify the client that the instance was enqueued
525
+  // ???????????????
526
+  // (is this ok??? - we are inserting into p->outQueH from the client thread)
527
+  // it would be safe to simply callback directly
528
+  // ???????????????
529
+  if( _cmTmEnqueueStatusMsg1(p,ip->instId,kStatusTmId,kQueuedTmId,0,NULL,NULL,0) != kOkTmRC )
530
+  {
531
+    cmErrMsg(&p->err,kQueueFailTmRC,"The 'queued' status update message failed to enqueue.");
532
+    goto errLabel;
533
+  }
534
+
535
+  
536
+ errLabel:
537
+  return rc;
538
+}
539
+
540
+cmTmRC_t cmTaskMgrTaskCtl( cmTaskMgrH_t h, unsigned instId, cmTaskMgrCtlId_t ctlId )
541
+{
542
+  cmTmRC_t       rc     = kOkTmRC;
543
+  cmTm_t*        p      = _cmTmHandleToPtr(h);
544
+  cmTmInst_t*    ip     = NULL;
545
+
546
+  if((ip = _cmTmInstFromId(p,instId)) == NULL )
547
+  {
548
+    cmErrMsg(&p->err,kInvalidArgTmRC,"The task instance associated with id %i could not be found.",instId);
549
+    goto errLabel;
550
+  }
551
+
552
+  // once the ctl id is set to kKillTmId don't allow it to change
553
+  if( ip->ctlId != kKillTmId )
554
+    ip->ctlId = ctlId;
555
+
556
+ errLabel:
557
+  return rc;
558
+}
559
+
560
+cmStatusTmId_t cmTaskMgrStatus( cmTaskMgrH_t h, unsigned instId )
561
+{
562
+  cmTm_t*        p      = _cmTmHandleToPtr(h);
563
+  cmTmInst_t*    ip     = NULL;
564
+  cmStatusTmId_t status = kInvalidTmId;
565
+
566
+  if((ip = _cmTmInstFromId(p,instId)) == NULL )
567
+  {
568
+    cmErrMsg(&p->err,kInvalidArgTmRC,"The task instance associated with id %i could not be found.",instId);
569
+    goto errLabel;
570
+  }
571
+  
572
+  status = ip->status;
573
+
574
+ errLabel:
575
+  return status;
576
+}
577
+  
578
+const void* cmTaskMgrResult( cmTaskMgrH_t h, unsigned instId )
579
+{
580
+  cmTm_t*     p  = _cmTmHandleToPtr(h);
581
+  cmTmInst_t* ip = NULL;
582
+
583
+  if((ip = _cmTmInstFromId(p,instId)) == NULL )
584
+  {
585
+    cmErrMsg(&p->err,kInvalidArgTmRC,"The task instance associated with id %i could not be found.",instId);
586
+    return NULL;
587
+  }
588
+
589
+  return ip->result;
590
+
591
+}
592
+
593
+unsigned    cmTaskMgrResultByteCount( cmTaskMgrH_t h, unsigned instId )
594
+{
595
+  cmTm_t*     p  = _cmTmHandleToPtr(h);
596
+  cmTmInst_t* ip = NULL;
597
+
598
+  if((ip = _cmTmInstFromId(p,instId)) == NULL )
599
+  {
600
+    cmErrMsg(&p->err,kInvalidArgTmRC,"The task instance associated with id %i could not be found.",instId);
601
+    return 0;
602
+  }
603
+
604
+  return ip->resultByteCnt;
605
+}
606
+
607
+cmTmRC_t    cmTaskMgrResultDelete(    cmTaskMgrH_t h, unsigned instId )
608
+{
609
+  cmTmRC_t rc = kOkTmRC;
610
+  cmTm_t*  p  = _cmTmHandleToPtr(h);
611
+
612
+  if((rc = _cmTmInstFree(p,instId)) != kOkTmRC )
613
+    rc = cmErrMsg(&p->err,kOpFailTmRC,"The instace delete failed on instance id %i.",instId);
614
+
615
+  return rc;
616
+}
617
+
618
+
619
+cmTaskMgrCtlId_t cmTaskMgrHandleCommand( cmTaskMgrFuncArg_t* a )
620
+{
621
+
622
+  while( *(a->cmdIdPtr) == kPauseTmId )
623
+  {
624
+    // ????????
625
+    // maybe we should send a status code to notify the client that the instance has paused.
626
+    ///
627
+    cmSleepMs(a->pauseSleepMs);
628
+  }
629
+
630
+  return *(a->cmdIdPtr);
631
+}
632
+
633
+//-----------------------------------------------------------------------------
634
+
635
+enum { kMaxTestInstCnt = 3 };
636
+
637
+typedef struct cmTmTestInst_str
638
+{
639
+  unsigned instId;
640
+} cmTmTestInst_t;
641
+
642
+typedef struct cmTmTestApp_str
643
+{
644
+  cmErr_t*       err;
645
+  cmTmTestInst_t insts[kMaxTestInstCnt];
646
+} cmTmTestApp_t;
647
+
648
+void _cmTmTestReportStatus( cmRpt_t* rpt, const cmTaskMgrStatusArg_t* s )
649
+{
650
+  cmRptPrintf(rpt,"%i ",s->instId );
651
+
652
+  switch( s->selId )
653
+  {
654
+    case kStatusTmId:
655
+      {
656
+        const cmChar_t* label = "<none>"; 
657
+        switch( s->statusId )
658
+        {
659
+          case kInvalidTmId:       label="<Invalid>"; break;
660
+          case kQueuedTmId:        label="Queued";  break;
661
+          case kQueuedPausedTmId:  label="Queued-Paused."; break;
662
+          case kStartedTmId:       label="Started"; break;
663
+          case kCompletedTmId:     label="Completed"; break;
664
+          case kKilledTmId:        label="Killed"; break;
665
+          default:
666
+            { assert(0); }        
667
+        }
668
+        cmRptPrintf(rpt,"status '%s'",label);
669
+      }
670
+      break;
671
+
672
+    case kProgTmId:
673
+      cmRptPrintf(rpt,"prog %i",s->prog);
674
+      break;
675
+
676
+    case kErrorTmId:
677
+      cmRptPrintf(rpt,"error %s",cmStringNullGuard(s->msg));
678
+      break;
679
+
680
+    default:
681
+      { assert(0); }
682
+  }
683
+
684
+  cmRptPrintf(rpt,"\n");
685
+
686
+}
687
+
688
+void _cmTmTestStatusCb( const cmTaskMgrStatusArg_t* s  )
689
+{
690
+   // s.arg set from cmTaskMgrCreate( ..., statusCbArg, ...);
691
+  cmTmTestApp_t* app = (cmTmTestApp_t*)s->arg;
692
+  unsigned i;
693
+
694
+  // locate the instance record assoc'd with this callback
695
+  for(i=0; i<kMaxTestInstCnt; ++i)
696
+    if( app->insts[i].instId ==  s->instId )
697
+      break;
698
+
699
+  if( i==kMaxTestInstCnt )
700
+    cmRptPrintf(app->err->rpt,"instId %i not found.\n",s->instId);
701
+
702
+  _cmTmTestReportStatus(app->err->rpt,s);
703
+}
704
+
705
+
706
+void _cmTmTestFunc(cmTaskMgrFuncArg_t* arg )
707
+{
708
+  cmTaskMgrStatusArg_t s;
709
+  memset(&s,0,sizeof(s));
710
+  s.arg           = arg->statusCbArg;
711
+  s.instId        = arg->instId;
712
+  s.selId         = kProgTmId;
713
+  s.statusId      = kStartedTmId;
714
+  s.prog          = 0;
715
+  s.msg           = NULL;
716
+  s.result        = NULL;
717
+  s.resultByteCnt = 0;
718
+
719
+  for(; s.prog<arg->progCnt; ++s.prog)
720
+  {
721
+    if( cmTaskMgrHandleCommand(arg) == kKillTmId )
722
+      break;
723
+
724
+    cmSleepMs(1000); 
725
+    arg->statusCb(&s);
726
+  }
727
+  
728
+}
729
+
730
+
731
+cmTmRC_t cmTaskMgrTest(cmCtx_t* ctx)
732
+{
733
+  cmTmRC_t        rc           = kOkTmRC;
734
+  cmTaskMgrH_t    tmH          = cmTaskMgrNullHandle;
735
+  unsigned        threadCnt    = 2;
736
+  unsigned        queueByteCnt = 1024;
737
+  unsigned        pauseSleepMs = 50;
738
+  unsigned        nextInstId   = 0;
739
+  unsigned        taskId       = 0;
740
+  const cmChar_t* taskLabel    = "Task Label";
741
+  cmTmTestApp_t   app;
742
+  char            c;
743
+
744
+  memset(&app,0,sizeof(app));
745
+  app.err = &ctx->err;
746
+
747
+  // create the task mgr
748
+  if( cmTaskMgrCreate( ctx,&tmH,_cmTmTestStatusCb,&app,threadCnt,queueByteCnt,pauseSleepMs) != kOkTmRC )
749
+  {
750
+    rc = cmErrMsg(&ctx->err,kTestFailTmRC,"Task mgr create failed.");
751
+    goto errLabel;
752
+  }
753
+
754
+  // install a task
755
+  if( cmTaskMgrInstall(tmH, taskId, taskLabel, _cmTmTestFunc ) != kOkTmRC )
756
+  {
757
+    rc = cmErrMsg(&ctx->err,kTestFailTmRC,"Task mgr task install failed.");
758
+    goto errLabel;    
759
+  }
760
+
761
+
762
+  // go into interactive mode
763
+  while((c = getchar()) != 'q')
764
+  {
765
+    switch(c)
766
+    {
767
+      case 'e':
768
+        {
769
+          // toggle the enable state of the task mgr.
770
+          bool fl = cmTaskMgrIsEnabled(tmH);
771
+          if( cmTaskMgrEnable(tmH,!fl) != kOkTmRC )
772
+            rc = cmErrMsg(&ctx->err,kTestFailTmRC,"Test enable failed.");
773
+        }
774
+        break;
775
+
776
+      case 'c':
777
+          if( nextInstId < kMaxTestInstCnt )
778
+          {
779
+            void* funcArg = app.insts + nextInstId;
780
+            unsigned progCnt = 5;
781
+            if( cmTaskMgrCall( tmH, taskId, funcArg, progCnt, &app.insts[nextInstId].instId ) != kOkTmRC )
782
+              rc = cmErrMsg(&ctx->err,kTestFailTmRC,"Test call failed.");            
783
+            else
784
+              ++nextInstId;
785
+          }
786
+    }
787
+  }
788
+
789
+  
790
+
791
+ errLabel:
792
+  // destroy the task mgr
793
+  if( cmTaskMgrDestroy(&tmH) != kOkTmRC )
794
+    rc = cmErrMsg(&ctx->err,kTestFailTmRC,"Task mgr destroy failed.");
795
+
796
+  return rc;
797
+}

+ 131
- 0
cmTaskMgr.h Просмотреть файл

@@ -0,0 +1,131 @@
1
+#ifndef cmTaskMgr_h
2
+#define cmTaskMgr_h
3
+
4
+#ifdef __cplusplus
5
+extern "C" {
6
+#endif
7
+
8
+  enum
9
+  {
10
+    kOkTmRC,
11
+    kThreadFailTmRC,
12
+    kInvalidArgTmRC,
13
+    kOpFailTmRC,
14
+    kQueueFailTmRC,
15
+    kTestFailTmRC
16
+  };
17
+
18
+  typedef cmRC_t cmTmRC_t;
19
+
20
+  typedef cmHandle_t cmTaskMgrH_t;
21
+
22
+  extern cmTaskMgrH_t cmTaskMgrNullHangle;
23
+
24
+  typedef enum
25
+  {
26
+    kInvalidTmId,
27
+    kQueuedTmId,       // The task is waiting in the queue.
28
+    kQueuedPausedTmId, // The task is waiting in the queue but is deferred.
29
+    kStartedTmId,      // The task is running.
30
+    kCompletedTmId,    // The task successfully completed.
31
+    kKilledTmId        // The task was killed by the client.
32
+  } cmStatusTmId_t;
33
+
34
+  typedef enum
35
+  {
36
+    kStatusTmId,       // Task status updates. These are automatically sent by the system when the task instance changes state.
37
+    kProgTmId,         // Task progress update. The user function should increment the 'prog' toward 'progCnt'.
38
+    kErrorTmId         // 
39
+  } cmSelTmId_t;
40
+
41
+  typedef enum 
42
+  { 
43
+    kStartTmId, 
44
+    kPauseTmId, 
45
+    kKillTmId 
46
+  } cmTaskMgrCtlId_t;
47
+
48
+
49
+  typedef struct cmTaskMgrStatusArg_str
50
+  {
51
+    void*           arg;
52
+    unsigned        instId;
53
+    cmSelTmId_t     selId;
54
+    cmStatusTmId_t  statusId;
55
+    unsigned        prog;
56
+    const cmChar_t* msg;
57
+    void*           result;
58
+    unsigned        resultByteCnt;
59
+  } cmTaskMgrStatusArg_t;
60
+
61
+  typedef void (*cmTaskMgrStatusCb_t)( const cmTaskMgrStatusArg_t* status  );
62
+
63
+  typedef struct cmTaskMgrFuncArg_str
64
+  {
65
+    void*               arg;           // 'funcArg' provided by cmTaskMgrCall();
66
+    unsigned            argByteCnt;    // 'funcArgByteCnt' provided by cmTaskMgrCall()
67
+    unsigned            instId;        // Task instance id.
68
+    cmTaskMgrStatusCb_t statusCb;      // Status update function provided by cmTaskMgrCreate().
69
+    void*               statusCbArg;   // Status update function arg. provided by cmTaskMgrCreate().
70
+    unsigned            progCnt;       // Maximum expected value of cmTaskMgrStatusArg_t.prog during execution of this task instance.
71
+    cmTaskMgrCtlId_t*   cmdIdPtr;      // Command id used to inform the running task that it should pause or exit.
72
+    unsigned            pauseSleepMs;  // Length of time to sleep if the task receives a pause command.
73
+  } cmTaskMgrFuncArg_t;
74
+
75
+  typedef void (*cmTaskMgrFunc_t)(cmTaskMgrFuncArg_t* arg );
76
+
77
+  cmTmRC_t cmTaskMgrCreate(  
78
+    cmCtx_t*             ctx, 
79
+    cmTaskMgrH_t*        hp, 
80
+    cmTaskMgrStatusCb_t  statusCb, 
81
+    void*                statusCbArg, 
82
+    unsigned             threadCnt,
83
+    unsigned             queueByteCnt,
84
+    unsigned             pauseSleepMs );
85
+
86
+  cmTmRC_t cmTaskMgrDestroy( cmTaskMgrH_t* hp );
87
+
88
+  bool     cmTaskMgrIsValid(   cmTaskMgrH_t h );
89
+
90
+  // Called by the client to give the task mgr an opportunity to execute
91
+  // period functions from within the client thread.  Note that 'statusCb()'
92
+  // (as passed to cmTaskMgrCreate()) is only called within this function
93
+  // This guarantees that all communication with the client occurs in the 
94
+  // clients thread.
95
+  cmTmRC_t cmTaskMgrOnIdle( cmTaskMgrH_t h );
96
+
97
+  // Pause/unpause the task mgr.
98
+  bool     cmTaskMgrIsEnabled( cmTaskMgrH_t h );
99
+  cmTmRC_t cmTaskMgrEnable(    cmTaskMgrH_t h, bool enableFl );
100
+
101
+  // Install a task function and associate it with a label and unique id.
102
+  cmTmRC_t cmTaskMgrInstall( cmTaskMgrH_t h, unsigned taskId, const cmChar_t* label, cmTaskMgrFunc_t func ); 
103
+
104
+  // Queue a task.
105
+  cmTmRC_t cmTaskMgrCall( 
106
+    cmTaskMgrH_t h, 
107
+    unsigned     taskId, 
108
+    void*        funcArg, 
109
+    unsigned     progCnt, 
110
+    unsigned*    retInstIdPtr ); 
111
+
112
+  // Start,pause, or kill a task.  
113
+  // If a queued task is paused then it will remain at the front of the queue
114
+  // and tasks behdind it in the queue will be executed.
115
+  cmTmRC_t cmTaskMgrTaskCtl( cmTaskMgrH_t h, unsigned instId, cmTaskMgrCtlId_t ctlId );
116
+
117
+  // Get the status of a task.
118
+  cmStatusTmId_t cmTaskMgrStatus( cmTaskMgrH_t h, unsigned instId );
119
+
120
+  
121
+  const void* cmTaskMgrResult( cmTaskMgrH_t h, unsigned instId );
122
+  unsigned    cmTaskMgrResultByteCount( cmTaskMgrH_t h, unsigned instId );
123
+  cmTmRC_t    cmTaskMgrResultDelete(    cmTaskMgrH_t h, unsigned instId );
124
+
125
+  cmTmRC_t cmTaskMgrTest(cmCtx_t* ctx);
126
+
127
+#ifdef __cplusplus
128
+}
129
+#endif
130
+
131
+#endif

Загрузка…
Отмена
Сохранить