Explorar el Código

cmTaskMgr.h/c : Added dynamic thread allocation and task balancing.

master
kpl hace 11 años
padre
commit
fe29450106
Se han modificado 2 ficheros con 210 adiciones y 72 borrados
  1. 205
    67
      cmTaskMgr.c
  2. 5
    5
      cmTaskMgr.h

+ 205
- 67
cmTaskMgr.c Ver fichero

@@ -5,6 +5,7 @@
5 5
 #include "cmMem.h"
6 6
 #include "cmMallocDebug.h"
7 7
 #include "cmThread.h"
8
+#include "cmTime.h"
8 9
 #include "cmTaskMgr.h"
9 10
 
10 11
 cmTaskMgrH_t cmTaskMgrNullHandle = cmSTATIC_NULL_HANDLE;
@@ -37,19 +38,25 @@ struct cmTm_str* p;
37 38
 
38 39
 typedef struct cmTmThread_str
39 40
 {
40
-  struct cmTm_str* p;      // 
41
-  cmThreadH_t      thH;    // 
42
-  cmTmInst_t*      inst;   // Ptr to the task instance this thread is executing.
41
+  struct cmTm_str*       p;     // 
42
+  cmThreadH_t            thH;   // 
43
+  cmTmInst_t*            inst;  // Ptr to the task instance this thread is executing.
44
+  double                 durSecs;
45
+  cmTimeSpec_t           t0;
46
+  bool                   deactivateFl;
47
+  struct cmTmThread_str* link;
43 48
 } cmTmThread_t;
44 49
 
45 50
 typedef struct cmTm_str
46 51
 {
47 52
   cmErr_t             err;
48
-  cmTmThread_t*       thArray;      // 
49
-  unsigned            threadCnt;    //
53
+  cmThreadH_t         mstrThH;      // 
54
+  cmTmThread_t*       threads;      //
55
+  unsigned            maxActiveThreadCnt; //
56
+  unsigned            threadRecdCnt;
50 57
   cmTaskMgrStatusCb_t statusCb;     // 
51 58
   void*               statusCbArg;  // 
52
-  unsigned            pauseSleepMs;
59
+  unsigned            pauseSleepMs; // 
53 60
   cmTs1p1cH_t         inQueH;       // client->mgr
54 61
   cmTsMp1cH_t         outQueH;      // mgr->client
55 62
   cmTmTask_t*         tasks;        //
@@ -151,7 +158,7 @@ bool _cmTmWorkerThreadFunc(void* arg)
151 158
   
152 159
   // if the task was paused or killed while it was queued then
153 160
   // cmTaskMgrHandleCommand() will do the right thing
154
-  if( cmTaskMgrHandleCommand(&r) != kKillTmId )
161
+  if( cmTaskMgrWorkerHandleCommand(&r) != kKillTmId )
155 162
   { 
156 163
     trp->inst->status = kStartedTmId;
157 164
 
@@ -171,56 +178,185 @@ bool _cmTmWorkerThreadFunc(void* arg)
171 178
   _cmTmEnqueueStatusMsg1(trp->p,trp->inst->instId,kStatusTmId,trp->inst->status,0,NULL,NULL,0);
172 179
 
173 180
   
174
-  trp->inst = NULL;
175
-
176 181
   // Force the thread to go into the 'pause' state when it 
177 182
   // returns to it's internal loop. The master thread recognizes paused
178
-  // threads as available.
183
+  // threads as available for reuse.
179 184
   cmThreadPause(trp->thH,kPauseThFl);
180 185
 
181 186
   return true;
182 187
 }
183 188
 
189
+void _cmTmMasterRptError( cmTm_t* p, unsigned rc, const cmChar_t* msg )
190
+{
191
+  assert(0); 
192
+}
193
+
194
+int _cmTmSortThreadByDur( const void* t0, const void* t1 )
195
+{  
196
+  double d = ((cmTmThread_t*)t0)->durSecs - ((cmTmThread_t*)t1)->durSecs; 
197
+
198
+  return d== 0 ? 0 : (d<0 ? -1 : 1);
199
+}
200
+
184 201
 
185 202
 // This is the master thread function.
186 203
 bool _cmTmMasterThreadFunc(void* arg)
187 204
 {
188
-  cmTmThread_t* trp = (cmTmThread_t*)arg;
189
-  cmTm_t*         p = trp->p;
205
+  cmTm_t*       p         = (cmTm_t*)arg;
206
+  unsigned      activeCnt = 0;
207
+  cmTmThread_t* trp       = p->threads;
208
+
209
+  if( p->threadRecdCnt > 0 )
210
+  {
211
+    cmTmThread_t* thArray[p->threadRecdCnt];
212
+    unsigned      deactivatedCnt = 0;
213
+
214
+    // for each thread record
215
+    for(trp=p->threads; trp!=NULL; trp=trp->link)
216
+    {
217
+      cmThStateId_t thState;
218
+
219
+      // if this thread is active ...
220
+      if( (thState = cmThreadState(trp->thH)) != kPausedThId )
221
+      {
222
+        // update the task lifetime duration
223
+        cmTimeSpec_t t1;
224
+        cmTimeGet(&t1);
225
+        trp->durSecs += (double)cmTimeElapsedMicros(&trp->t0,&t1) / 1000000.0;
226
+        trp->t0 = t1;
227
+        
228
+        assert(trp->inst!=NULL);
229
+
230
+        // if the task assoc'd with this thread is running
231
+        if( trp->inst->status == kStartedTmId )
232
+        {
233
+          thArray[activeCnt] = trp;
234
+          ++activeCnt;
235
+        }
236
+
237
+        // count the number of deactivated threads
238
+        if( trp->deactivateFl )
239
+          ++deactivatedCnt;
240
+      }
241
+    }
242
+
243
+    // The first 'activeCnt' elements of thArray[] now point to
244
+    // cmTmThread_t records of the active tasks.
245
+
246
+    // if more tasks are active than should be - then deactive the youngest
247
+    if( activeCnt > p->maxActiveThreadCnt )
248
+    {
249
+      // sort the active tasks in increasing order of lifetime
250
+      qsort(&thArray[0],activeCnt,sizeof(thArray[0]),_cmTmSortThreadByDur);
251
+
252
+      // determine the number of threads that need to be paused
253
+      int n = activeCnt - p->maxActiveThreadCnt;
254
+      int i;
255
+
256
+      // pause the active threads with the lowest lifetime
257
+      for(i=0; i<n ; ++i)
258
+        if( thArray[i]->deactivateFl == false )
259
+        {
260
+          thArray[i]->deactivateFl = true;
261
+          ++deactivatedCnt;
262
+        }
263
+    }
264
+
265
+    // if there are deactivated tasks and the max thread count has not been reached
266
+    // then re-activate some of the deactivated tasks.
267
+    if( activeCnt < p->maxActiveThreadCnt && deactivatedCnt > 0 )
268
+    {
269
+      // sort the active tasks in increasing order of lifetime
270
+      qsort(&thArray[0],activeCnt,sizeof(thArray[0]),_cmTmSortThreadByDur);
271
+
272
+      int n = cmMin(p->maxActiveThreadCnt - activeCnt, deactivatedCnt );
273
+      int i;
274
+
275
+      // re-activate the oldest deactivated tasks
276
+      for(i=activeCnt-1; i>=0 && n>0; --i)
277
+        if( thArray[i]->deactivateFl )
278
+        {
279
+          thArray[i]->deactivateFl = false;
280
+          --n;
281
+          ++activeCnt;
282
+        }
283
+
284
+    }
285
+  }
190 286
   
287
+  
288
+  // if a queued task exists 
191 289
   while( cmTs1p1cMsgWaiting(p->inQueH) )
192 290
   {
193
-    unsigned i;
194
-    cmTmInst_t* ip = NULL;
291
+    cmTmInst_t*   ip        = NULL;
292
+    cmTmThread_t* atrp      = NULL;
293
+
294
+    activeCnt = 0;
195 295
 
196
-    // find an available worker thread
197
-    for(i=1; i<p->threadCnt; ++i)
198
-      if( cmThreadState(p->thArray[i].thH) == kPausedThId )
296
+    // Find a worker thread that is in the 'paused' state.
297
+    // This is the definitive indication that the thread
298
+    // does not have an assigned instance
299
+    for(trp=p->threads; trp!=NULL; trp=trp->link)
300
+    {
301
+      if( cmThreadState(trp->thH) == kPausedThId )
302
+        atrp = trp;
303
+      else
304
+        ++activeCnt;
305
+    }
306
+
307
+    // If the maximum number of active threads already exists then we cannot start a new task    
308
+    if( activeCnt >= p->maxActiveThreadCnt )
309
+      break;
310
+
311
+    // If all the existing worker threads are busy
312
+    // but the maximum number of threads has not yet been allocated ...
313
+    if( atrp==NULL && p->threadRecdCnt < p->maxActiveThreadCnt)
314
+    {
315
+      // ... then create a new worker thread recd
316
+      atrp = cmMemAllocZ(cmTmThread_t,1);
317
+
318
+      // ... create the new worker thread
319
+      if( cmThreadCreate(&atrp->thH,_cmTmWorkerThreadFunc,atrp,p->err.rpt) != kOkThRC )
320
+      {
321
+        cmMemFree(atrp);
322
+        atrp = NULL;
323
+        _cmTmMasterRptError(p,kThreadFailTmRC,"Worker thread create failed.");
199 324
         break;
325
+      }
326
+      else
327
+      {
328
+        // ... setup the new thread record
329
+        atrp->p    = p;
330
+        atrp->link = p->threads;
331
+        p->threads = atrp;
200 332
 
201
-    // if all worker threads are busy ... give up
202
-    if( i==p->threadCnt )
333
+        p->threadRecdCnt += 1;
334
+      }
335
+    }
336
+
337
+    // if there are no available threads then give up 
338
+    if( atrp == NULL )
203 339
       break;
204 340
 
205 341
     // dequeue a pending task instance pointer from the input queue
206 342
     if(cmTs1p1cDequeueMsg(p->inQueH,&ip,sizeof(ip)) != kOkThRC )
207 343
     {
208
-      /// ??????? HOW DO WE HANDLE ERRORS IN THE MASTER THREAD
209
-      continue;
344
+      _cmTmMasterRptError(p,kQueueFailTmRC,"Dequeue failed on incoming task instance queue.");
345
+      break;
210 346
     }
211 347
 
212
-    // assign the instance to the available thread
213
-    p->thArray[i].inst = ip;
214
-    
215
-    // start the thread and wait for it to enter the running state.
216
-    
217
-    if( cmThreadPause(p->thArray[i].thH,0) != kOkThRC )
218
-    {
219
-      /// ??????? HOW DO WE HANDLE ERRORS IN THE MASTER THREAD
220
-    }
348
+    // setup the thread record associated with the new task
349
+    atrp->inst         = ip;
350
+    atrp->durSecs      = 0;
351
+    atrp->deactivateFl = false;
352
+
353
+    // start the worker thread 
354
+    if( cmThreadPause(atrp->thH,0) != kOkThRC )
355
+      _cmTmMasterRptError(p,kThreadFailTmRC,"Worker thread start failed.");
221 356
 
222 357
   }
223 358
 
359
+
224 360
   cmSleepMs(p->pauseSleepMs);
225 361
 
226 362
   return true;
@@ -283,17 +419,26 @@ cmTmRC_t _cmTmDestroy( cmTm_t* p )
283 419
   cmTmRC_t rc = kOkTmRC;
284 420
   unsigned i;
285 421
 
286
-  // stop and destroy all the threads
287
-  for(i=0; i<p->threadCnt; ++i)
422
+  // stop and destroy the master thread
423
+  if( cmThreadDestroy(&p->mstrThH) != kOkThRC )
288 424
   {
289
-    if( cmThreadDestroy(&p->thArray[i].thH) != kOkThRC )
425
+    rc = cmErrMsg(&p->err,kThreadFailTmRC,"Master thread destroy failed.");
426
+    goto errLabel;
427
+  }
428
+
429
+  // stop and destroy all the worker threads
430
+  for(i=0; p->threads != NULL; ++i )
431
+  {
432
+    if( cmThreadDestroy(&p->threads->thH) != kOkThRC )
290 433
     {
291
-      rc = cmErrMsg(&p->err,kThreadFailTmRC,"Thread index %i destroy failed.",i);
292
-      goto errLabel;
434
+      rc = cmErrMsg(&p->err,kThreadFailTmRC,"Thread destruction failed for the worker thread at index %i.",i);
435
+      goto errLabel;      
293 436
     }
294
-  }
295 437
 
296
-  cmMemFree(p->thArray);
438
+    cmTmThread_t* trp = p->threads;
439
+    p->threads = p->threads->link;
440
+    cmMemFree(trp);
441
+  }
297 442
 
298 443
   // release the input queue
299 444
   if( cmTs1p1cDestroy(&p->inQueH) != kOkThRC )
@@ -341,12 +486,11 @@ cmTmRC_t cmTaskMgrCreate(
341 486
   cmTaskMgrH_t*        hp, 
342 487
   cmTaskMgrStatusCb_t  statusCb, 
343 488
   void*                statusCbArg, 
344
-  unsigned             threadCnt,
489
+  unsigned             maxActiveThreadCnt,
345 490
   unsigned             queueByteCnt,
346 491
   unsigned             pauseSleepMs)
347 492
 {
348 493
   cmTmRC_t rc = kOkTmRC;
349
-  unsigned i;
350 494
 
351 495
   if((rc = cmTaskMgrDestroy(hp)) != kOkTmRC )
352 496
     return rc;
@@ -355,24 +499,16 @@ cmTmRC_t cmTaskMgrCreate(
355 499
  
356 500
   cmErrSetup(&p->err,&ctx->rpt,"Task Mgr.");
357 501
 
358
-  threadCnt += 1;
359
-  p->thArray      = cmMemAllocZ(cmTmThread_t,threadCnt);
360
-  p->threadCnt    = threadCnt;
502
+  p->maxActiveThreadCnt = maxActiveThreadCnt;
361 503
   p->statusCb     = statusCb;
362 504
   p->statusCbArg  = statusCbArg;
363 505
   p->pauseSleepMs = pauseSleepMs;
364 506
 
365
-  // create the threads
366
-  for(i=0; i<threadCnt; ++i)
507
+  // create the master thread
508
+  if( cmThreadCreate(&p->mstrThH, _cmTmMasterThreadFunc,p,&ctx->rpt) != kOkThRC )
367 509
   {
368
-    if( cmThreadCreate(&p->thArray[i].thH, i==0 ? _cmTmMasterThreadFunc : _cmTmWorkerThreadFunc,  p->thArray+i, &ctx->rpt ) != kOkThRC )
369
-    {
370
-      rc = cmErrMsg(&p->err,kThreadFailTmRC,"Thread index %i create failed.",i);
371
-      goto errLabel;
372
-    }
373
-
374
-    p->thArray[i].p = p;
375
-
510
+    rc = cmErrMsg(&p->err,kThreadFailTmRC,"Thread index %i create failed.");
511
+    goto errLabel;
376 512
   }
377 513
 
378 514
   // create the input queue
@@ -480,7 +616,7 @@ bool     cmTaskMgrIsEnabled( cmTaskMgrH_t h )
480 616
 {
481 617
   cmTm_t*  p = _cmTmHandleToPtr(h);
482 618
 
483
-  return cmThreadState(p->thArray[0].thH) != kPausedThId;
619
+  return cmThreadState(p->mstrThH) != kPausedThId;
484 620
 }
485 621
 
486 622
 cmTmRC_t cmTaskMgrEnable(    cmTaskMgrH_t h, bool enableFl )
@@ -489,7 +625,7 @@ cmTmRC_t cmTaskMgrEnable(    cmTaskMgrH_t h, bool enableFl )
489 625
   cmTm_t*  p     = _cmTmHandleToPtr(h);
490 626
   unsigned flags = (enableFl ? 0 : kPauseThFl) | kWaitThFl;
491 627
 
492
-  if( cmThreadPause(p->thArray[0].thH, flags ) != kOkThRC )
628
+  if( cmThreadPause(p->mstrThH, flags ) != kOkThRC )
493 629
     rc = cmErrMsg(&p->err,kThreadFailTmRC,"The master thread failed to %s.",enableFl ? "enable" : "disable" );
494 630
 
495 631
   return rc;
@@ -696,7 +832,7 @@ cmTmRC_t    cmTaskMgrInstDelete(    cmTaskMgrH_t h, unsigned instId )
696 832
 }
697 833
 
698 834
 
699
-cmTaskMgrCtlId_t _cmTaskMgrHelper( cmTaskMgrFuncArg_t* a, unsigned prog, cmStatusTmId_t statusId )
835
+cmTaskMgrCtlId_t _cmTaskMgrWorkerHelper( cmTaskMgrFuncArg_t* a, unsigned prog, cmStatusTmId_t statusId )
700 836
 {
701 837
   cmTaskMgrStatusArg_t s;
702 838
 
@@ -711,32 +847,32 @@ cmTaskMgrCtlId_t _cmTaskMgrHelper( cmTaskMgrFuncArg_t* a, unsigned prog, cmStatu
711 847
 
712 848
   a->statusCb(&s);
713 849
 
714
-  return cmTaskMgrHandleCommand(a);
850
+  return cmTaskMgrWorkerHandleCommand(a);
715 851
 }
716 852
 
717
-cmTaskMgrCtlId_t cmTaskMgrHandleCommand( cmTaskMgrFuncArg_t* a )
853
+cmTaskMgrCtlId_t cmTaskMgrWorkerHandleCommand( cmTaskMgrFuncArg_t* a )
718 854
 {
719 855
   cmTmThread_t* trp = a->reserved;
720 856
 
721
-  while( trp->inst->ctlId == kPauseTmId )
857
+  while( trp->inst->ctlId == kPauseTmId || trp->deactivateFl == true )
722 858
   {
723 859
     // change the instance status to 'paused'.
724 860
     trp->inst->status = kPausedTmId;
725 861
 
726 862
     // notify the client of the change in state
727
-    cmTaskMgrSendStatus(a,kPausedTmId);
863
+    cmTaskMgrWorkerSendStatus(a,kPausedTmId);
728 864
 
729 865
     // sleep the thread for pauseSleepMs milliseconds
730 866
     cmSleepMs(a->pauseSleepMs);
731 867
 
732 868
     // if the task was unpaused while we slept
733
-    if( trp->inst->ctlId == kStartTmId )
869
+    if( trp->inst->ctlId == kStartTmId && trp->deactivateFl == false )
734 870
     {
735 871
       // change the instance status to 'started'.
736 872
       trp->inst->status = kStartedTmId;
737 873
 
738 874
       // notify the client of the change in state
739
-      cmTaskMgrSendStatus(a,kStartedTmId);
875
+      cmTaskMgrWorkerSendStatus(a,kStartedTmId);
740 876
     }
741 877
   }
742 878
 
@@ -746,11 +882,11 @@ cmTaskMgrCtlId_t cmTaskMgrHandleCommand( cmTaskMgrFuncArg_t* a )
746 882
   return trp->inst->ctlId;
747 883
 }
748 884
 
749
-cmTaskMgrCtlId_t cmTaskMgrSendStatus( cmTaskMgrFuncArg_t* a, cmStatusTmId_t statusId )
750
-{ return _cmTaskMgrHelper(a,0,statusId); }
885
+cmTaskMgrCtlId_t cmTaskMgrWorkerSendStatus( cmTaskMgrFuncArg_t* a, cmStatusTmId_t statusId )
886
+{ return _cmTaskMgrWorkerHelper(a,0,statusId); }
751 887
 
752
-cmTaskMgrCtlId_t cmTaskMgrSendProgress( cmTaskMgrFuncArg_t* a, unsigned prog )
753
-{ return _cmTaskMgrHelper(a,prog,kInvalidTmId);  }
888
+cmTaskMgrCtlId_t cmTaskMgrWorkerSendProgress( cmTaskMgrFuncArg_t* a, unsigned prog )
889
+{ return _cmTaskMgrWorkerHelper(a,prog,kInvalidTmId);  }
754 890
 
755 891
 
756 892
 //-----------------------------------------------------------------------------
@@ -829,17 +965,19 @@ void _cmTmTestStatusCb( const cmTaskMgrStatusArg_t* s  )
829 965
 // Test worker function.
830 966
 void _cmTmTestFunc(cmTaskMgrFuncArg_t* arg )
831 967
 {
968
+  if( cmTaskMgrWorkerHandleCommand(arg) == kKillTmId )
969
+    return;
832 970
 
833 971
   unsigned prog = 0;
834 972
 
835 973
   for(; prog<arg->progCnt; ++prog)
836 974
   {
837
-    if( cmTaskMgrHandleCommand(arg) == kKillTmId )
975
+    if( cmTaskMgrWorkerHandleCommand(arg) == kKillTmId )
838 976
       break;
839 977
 
840 978
     cmSleepMs(1000); 
841 979
 
842
-    if( cmTaskMgrSendProgress(arg,prog) == kKillTmId )
980
+    if( cmTaskMgrWorkerSendProgress(arg,prog) == kKillTmId )
843 981
       break;
844 982
   }
845 983
   

+ 5
- 5
cmTaskMgr.h Ver fichero

@@ -36,7 +36,7 @@ extern "C" {
36 36
   {
37 37
     kStatusTmId,       // Task status updates. These are automatically sent by the system when the task instance changes state.
38 38
     kProgTmId,         // Task progress update. The user function should increment the 'prog' toward 'progCnt'.
39
-    kErrorTmId         // 
39
+    kErrorTmId         // Error message
40 40
   } cmSelTmId_t;
41 41
 
42 42
   typedef enum 
@@ -81,7 +81,7 @@ extern "C" {
81 81
     cmTaskMgrH_t*        hp, 
82 82
     cmTaskMgrStatusCb_t  statusCb, 
83 83
     void*                statusCbArg, 
84
-    unsigned             threadCnt,
84
+    unsigned             maxActiveThreadCnt,
85 85
     unsigned             queueByteCnt,
86 86
     unsigned             pauseSleepMs );
87 87
 
@@ -137,9 +137,9 @@ extern "C" {
137 137
 
138 138
   // -----------------------------------------------------------------------------------
139 139
   // Worker thread helper functions.
140
-  cmTaskMgrCtlId_t cmTaskMgrHandleCommand( cmTaskMgrFuncArg_t* a );
141
-  cmTaskMgrCtlId_t cmTaskMgrSendStatus( cmTaskMgrFuncArg_t* a, cmStatusTmId_t statusId );
142
-  cmTaskMgrCtlId_t cmTaskMgrSendProgress( cmTaskMgrFuncArg_t* a, unsigned prog );
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 );
143 143
 
144 144
   cmTmRC_t cmTaskMgrTest(cmCtx_t* ctx);
145 145
 

Loading…
Cancelar
Guardar