libcm is a C development framework with an emphasis on audio signal processing applications.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

cmTaskMgr.c 19KB

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