/** * This is another approach at using the scriptcontext system. It introduces * the concept of a communication task which is executed against a device serializer, * accessed via a given ScriptContext. Clients can: * - Create CommTasks * - Figure out their state * - Retrieve reply data * - wait for a ComTask to finish. * * The purpose sctcomtask will keep a cache of pending and finished communications. * Old runs will be deleted periodically. Nevertheless the cache can be listed in order * to figure out what happened. * * The intended use is for C-code or scripts to interact in a serial manner with * the asynchronous communication system implemented in devser and ascon. As a * standalone implementation this would share tons of code with scriptcontext, thus * this has been implemented as an add on module to scriptcontext. * * copyright: see file COPYRIGHT * * Mark Koennecke, December 2012 */ #include #include "sctcomtask.h" #include "scriptcontext.h" #include "sicsobj.h" #include "sicsutil.h" #include "sicshipadaba.h" static int mamaID = 0L; extern char *ParText(Hdb * cmdNode, char *name, int nPar, char *defaultValue); /*----------------------------------------------------------------*/ typedef struct{ char *sendData; char *replyData; int replyDataLength; int ID; double startTime; double endTime; ComTaskState state; }ComTask, *pComTask; /*----------------------------------------------------------------*/ struct ComTaskManager{ DevSer *devser; unsigned int halfQueueLength; ComTask *TaskQueue; unsigned int iPtr; }; /*-----------------------------------------------------------------*/ static void KillComTaskData(ComTask *queue, int length) { int i; for(i = 0; i < length; i++){ if(queue[i].sendData != NULL){ free(queue[i].sendData); queue[i].sendData = NULL; } if(queue[i].replyData != NULL){ free(queue[i].replyData); queue[i].replyData = NULL; } } } /*-----------------------------------------------------------------*/ static char *ComTaskHandler(void *data, char *replyData, int commError) { pComTask task = (pComTask)data; assert(task != NULL); if(replyData == NULL){ task->state = eRunning; return task->sendData; } else { if(task->replyData != NULL){ free(task->replyData); } task->replyData = strdup(replyData); task->replyDataLength = strlen(replyData); task->endTime = DoubleTime(); task->state = eFinished; return NULL; } } /*-----------------------------------------------------------------*/ static int ComTaskMatch(void *data1, void *data2) { pComTask task1 = (pComTask)data1; pComTask task2 = (pComTask)data2; assert(task1 && task2); if(task1->ID == task2->ID){ return 1; } else { return 0; } } /*----------------------------------------------------------------*/ static char *stateName[] = { "waiting", "running", "finished", "unknown" }; static char* StateToText(ComTaskState state) { return stateName[(int)state]; } /*----------------------------------------------------------------*/ static char* ComTaskInfo(void *data) { char *info = NULL; char buf[] = {"NA"}; char *reply; int length = 256; pComTask task = (pComTask)data; assert(data != NULL); if(task->sendData != NULL){ length += strlen(task->sendData); } if(task->replyData != NULL){ length += strlen(task->replyData); } info = calloc(length,sizeof(char)); if(info == NULL){ return info; } if(task->replyData == NULL){ reply = buf; } else { reply = task->replyData; } snprintf(info,length,"%s:%s:%lf:%s:%lf", StateToText(task->state), task->sendData,task->startTime, reply, task->endTime); return info; } /*----------------------------------------------------------------*/ int StartComTask(ComTaskManager *manager, char *priority, char *sendData) { int ID, i; unsigned int ptr; DevPrio prio; assert(manager != NULL && priority != NULL); ID = mamaID; mamaID++; if(mamaID < 0){ mamaID = 0; } if(manager->iPtr >= 2*manager->halfQueueLength){ KillComTaskData(manager->TaskQueue,manager->halfQueueLength); memmove(manager->TaskQueue,&manager->TaskQueue[manager->halfQueueLength], manager->halfQueueLength*sizeof(ComTask)); /** * The pointers need to be cleared, not freed, in order to * prevent double freeing when reusing them. */ for(i = manager->halfQueueLength; i < 2*manager->halfQueueLength; i++){ manager->TaskQueue[i].sendData = NULL; manager->TaskQueue[i].replyData = NULL; } manager->iPtr = manager->halfQueueLength; } ptr = manager->iPtr; manager->iPtr++; manager->TaskQueue[ptr].ID = ID; manager->TaskQueue[ptr].startTime = DoubleTime(); manager->TaskQueue[ptr].endTime = 0.; manager->TaskQueue[ptr].state = eWaiting; if(manager->TaskQueue[ptr].sendData != NULL){ free(manager->TaskQueue[ptr].sendData); } manager->TaskQueue[ptr].sendData = strdup(sendData); if(manager->TaskQueue[ptr].replyData != NULL){ free(manager->TaskQueue[ptr].replyData); } manager->TaskQueue[ptr].replyData = NULL; prio = DevText2Prio(priority); DevQueue(manager->devser, &manager->TaskQueue[ptr], prio, ComTaskHandler, ComTaskMatch, NULL, ComTaskInfo); return ID; } /*-----------------------------------------------------------------*/ static int StartComTaskCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { char *priority, *sendData; long lID; ComTaskManager *manni = NULL; if(nPar < 2) { SCWrite(con,"ERROR: need priorty and send data as parameters",eError); return 0; } priority = ParText(cmdNode,"priority",nPar,"read"); sendData = ParText(cmdNode,"send",nPar,"None"); manni = (ComTaskManager *)ccmd->pPrivate; lID = StartComTask(manni, priority, sendData); SCPrintf(con,eValue,"comtaskid = %ld", lID); return 1; } /*-----------------------------------------------------------------*/ static int ComTaskCompare(const void *data1, const void *data2) { pComTask task1 = (pComTask)data1; pComTask task2 = (pComTask)data2; assert(task1 && task2); if(task1->ID < task2->ID){ return -1; } else if(task1->ID > task2->ID){ return 1; } else { return 0; } } /*------------------------------------------------------------------*/ ComTaskState GetComTaskState(ComTaskManager *manager, int comTaskID) { ComTask key; ComTask *task = NULL; key.ID = comTaskID; task = bsearch(&key,manager->TaskQueue,manager->iPtr, sizeof(ComTask), ComTaskCompare); if(task == NULL){ return eUnknown; } else { return task->state; } } /*-----------------------------------------------------------------*/ static int GetComTaskStateCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { int lID; ComTaskManager *manni = NULL; ComTaskState state; if(nPar < 1) { SCWrite(con,"ERROR: need task ID parameter",eError); return 0; } lID = par[0]->value.v.intValue; manni = (ComTaskManager *)ccmd->pPrivate; state = GetComTaskState(manni,lID); SCPrintf(con,eValue,"%d = %s", lID, StateToText(state)); return 1; } /*------------------------------------------------------------------*/ const char *GetComTaskReply(ComTaskManager *manager, int comTaskID, int *length) { ComTask key; ComTask *task = NULL; key.ID = comTaskID; task = bsearch(&key,manager->TaskQueue,manager->iPtr, sizeof(ComTask), ComTaskCompare); if(task == NULL || task->state != eFinished){ return NULL; } else { return (const char *)task->replyData; } } /*-----------------------------------------------------------------*/ static int GetComTaskReplyCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { long lID; int length; ComTaskManager *manni = NULL; const char *reply; if(nPar < 1) { SCWrite(con,"ERROR: need task ID parameter",eError); return 0; } lID = par[0]->value.v.intValue; manni = (ComTaskManager *)ccmd->pPrivate; reply = GetComTaskReply(manni,lID,&length); if(reply == NULL){ SCPrintf(con,eError,"No reply for %ld found", lID); return 0; } else { SCPrintf(con,eValue,"%ld = %s", lID, reply); } return 1; } /*------------------------------------------------------------*/ void ComTaskWait(ComTaskManager *manager, int comTaskID) { ComTask key; ComTask *task = NULL; key.ID = comTaskID; task = bsearch(&key,manager->TaskQueue,manager->iPtr, sizeof(ComTask), ComTaskCompare); if(task == NULL){ return; } /** * Hmmmmhhhh. It might be better to start a SICS task here and * wait for it terminate. But this would essentially also call * TaskYield or, worse, Taskwait. */ while(task->state != eFinished){ TaskYield(pServ->pTasker); } } /*-----------------------------------------------------------------*/ static int ComTaskWaitCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { int lID; ComTaskManager *manni = NULL; if(nPar < 1) { SCWrite(con,"ERROR: need task ID parameter",eError); return 0; } lID = par[0]->value.v.intValue; manni = (ComTaskManager *)ccmd->pPrivate; ComTaskWait(manni,lID); SCWrite(con,"Done",eValue); return 1; } /*-----------------------------------------------------------------*/ static int ComTaskListAllCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { ComTaskManager *manni = NULL; pDynString list = NULL; int i; char *info; manni = (ComTaskManager *)ccmd->pPrivate; list = CreateDynString(256,256); if(list == NULL){ SCWrite(con,"ERROR: out of memory listing ComTasks", eError); return 0; } for(i = 0; i < manni->iPtr; i++){ info = ComTaskInfo((void *)&manni->TaskQueue[i]); DynStringConcat(list,info); DynStringConcatChar(list,'\n'); free(info); } SCWrite(con,GetCharArray(list),eValue); DeleteDynString(list); return 1; } /*--------------------------------------------------------------------*/ static int ComTaskListPendingCmd(pSICSOBJ ccmd, SConnection * con, Hdb * cmdNode, Hdb * par[], int nPar) { ComTaskManager *manni = NULL; pDynString list = NULL; int i; char *info; manni = (ComTaskManager *)ccmd->pPrivate; list = CreateDynString(256,256); if(list == NULL){ SCWrite(con,"ERROR: out of memory listing ComTasks", eError); return 0; } for(i = 0; i < manni->iPtr; i++){ if(manni->TaskQueue[i].state != eFinished){ info = ComTaskInfo((void *)&manni->TaskQueue[i]); DynStringConcat(list,info); DynStringConcatChar(list,'\n'); free(info); } } SCWrite(con,GetCharArray(list),eValue); DeleteDynString(list); return 1; } /*-----------------------------------------------------------------*/ static ComTaskManager *MakeComTaskManager(int length) { ComTaskManager *manni = NULL; manni = calloc(1, sizeof(ComTaskManager)); if(manni != NULL){ manni->halfQueueLength=length; manni->TaskQueue = calloc(length*2,sizeof(ComTask)); if(manni->TaskQueue == NULL){ free(manni); return NULL; } } return manni; } /*-----------------------------------------------------------------*/ static void KillComTaskManager(void *data) { ComTaskManager *manni = (ComTaskManager *)data; if(manni == NULL){ return; } if(manni->TaskQueue != NULL){ free(manni->TaskQueue); } KillComTaskData(manni->TaskQueue,manni->halfQueueLength*2); free(manni); } /*-----------------------------------------------------------------*/ int SctMakeComTask(SConnection * con, SicsInterp * sics, void *object, int argc, char *argv[]) { SICSOBJ *ccmd; pHdb cmd = NULL; ComTaskManager *manni = NULL; int length = 64; if (argc < 3) { SCWrite(con,"ERROR: need at least comtaskmanager name and sctcontroller as arguments", eError); return 0; } ccmd = (SICSOBJ *)FindCommandData(sics,argv[2],"SctController"); if(ccmd == NULL){ SCPrintf(con,eError,"ERROR: SctController %s not found",argv[2]); return 0; } if(argc > 3) { length = atoi(argv[3]); } manni = MakeComTaskManager(length); if(manni == NULL){ SCWrite(con,"ERROR: out of memory allocating ComTaskmanager",eError); return 0; } manni->devser = SctGetDevser(ccmd); assert(manni->devser != NULL); ccmd = MakeSICSOBJv(argv[1], "SctComTask", HIPNONE, usSpy); assert(ccmd); ccmd->pPrivate = manni; ccmd->KillPrivate = KillComTaskManager; AddCommand(pServ->pSics, argv[1], InterInvokeSICSOBJ, KillSICSOBJ, ccmd); RegisterSICSOBJKillCmd(ccmd, argv[1]); SetDescriptorKey(ccmd->pDes, "creationCommand", "0"); cmd = AddSICSHdbPar(ccmd->objectNode, "start", usUser, MakeSICSFunc(StartComTaskCmd)); AddSICSHdbPar(cmd, "priority", usUser, MakeHdbText("")); AddSICSHdbPar(cmd, "send", usUser, MakeHdbText("None")); cmd = AddSICSHdbPar(ccmd->objectNode, "state", usUser, MakeSICSFunc(GetComTaskStateCmd)); AddSICSHdbPar(cmd, "id", usUser, MakeHdbInt(0)); cmd = AddSICSHdbPar(ccmd->objectNode, "reply", usUser, MakeSICSFunc(GetComTaskReplyCmd)); AddSICSHdbPar(cmd, "id", usUser, MakeHdbInt(0)); cmd = AddSICSHdbPar(ccmd->objectNode, "wait", usUser, MakeSICSFunc(ComTaskWaitCmd)); AddSICSHdbPar(cmd, "id", usUser, MakeHdbInt(0)); cmd = AddSICSHdbPar(ccmd->objectNode, "listall", usSpy, MakeSICSFunc(ComTaskListAllCmd)); cmd = AddSICSHdbPar(ccmd->objectNode, "listpend", usSpy, MakeSICSFunc(ComTaskListPendingCmd)); return 1; } /*-------------------------------------------------------------------------------*/