/*------------------------------------------------------------------------ T A S K E R Implementation of a portable task switcher. Mark Koennecke, September 1997 copyleft, 1997. NO WARRANTIES OF ANY KIND WHATSOEVER TAKEN BY ME OR MY EMPLOYER. YOU ARE AT YOUR OWN! Reworked to have a task name, stop by task name and better listing. Mark Koennecke, December 2012 ----------------------------------------------------------------------------*/ #include #include #include #include #include "fortify.h" #include "task.h" #define READY 1 #define WAITING 2 #define YIELDING 3 #define IDUNDEFINED 0L /*--------------------------------------------------------------------------*/ typedef struct __TaskHead { long lID; long groupID; int iStatus; long lWait; char *name; time_t start_time; TaskFunc pRun; SignalFunc pSignal; void *pData; TaskKillFunc pKill; pTaskHead pNext; pTaskHead pPrevious; } TaskHead; typedef struct __TaskMan { int iID; int iStop; pTaskHead pCurrent; /* Think trice before you interfere with this! */ pTaskHead pHead; } TaskMan; /*--------------------------------------------------------------------------- The 7 below solves a subtle bug which occurs when a groupID in user code has been initialized to 0 and starting fails. Then it seems as if this group keeps running. As there will always be some task running at 0. ----------------------------------------------------------------------------*/ static long lIDMama = 7L; #define TASKERID 123399 /*---------------------------------------------------------------------------*/ static pTaskHead MakeTaskHead(char *name, TaskFunc pTask, SignalFunc pSignal, void *pData, TaskKillFunc pKill) { pTaskHead pNew = NULL; pNew = (pTaskHead) malloc(sizeof(TaskHead)); if (!pNew) { return NULL; } memset(pNew, 0, sizeof(TaskHead)); pNew->name = strdup(name); pNew->start_time = time(NULL); pNew->pRun = pTask; pNew->pSignal = pSignal; pNew->pData = pData; pNew->pKill = pKill; lIDMama++; pNew->lID = lIDMama; pNew->iStatus = READY; pNew->groupID = IDUNDEFINED; if(lIDMama < 0){ lIDMama = 7; } return pNew; } /*--------------------------------------------------------------------------*/ static void DeleteTaskHead(pTaskHead self) { assert(self); if (self->pKill) { if (self->pData) { self->pKill(self->pData); } } if(self->name != NULL){ free(self->name); } /* unlink */ if (self->pPrevious != NULL) { self->pPrevious->pNext = self->pNext; } if (self->pNext != NULL) { self->pNext->pPrevious = self->pPrevious; } free(self); } /*--------------------------------------------------------------------------*/ static int DummyTask(void *pData) { return 1; } /*--------------------------------------------------------------------------*/ int TaskerInit(pTaskMan * self) { pTaskMan pNew = NULL; pTaskHead pDummyTask = NULL; *self = NULL; /* a new Task manager */ pNew = (pTaskMan) malloc(sizeof(TaskMan)); if (!pNew) { return 0; } memset(pNew, 0, sizeof(TaskMan)); pNew->iID = TASKERID; /* create a dummy task as start point */ pDummyTask = MakeTaskHead("init",DummyTask, NULL, NULL, NULL); if (!pDummyTask) { free(pNew); return 0; } /* link */ pDummyTask->pNext = NULL; pDummyTask->pPrevious = NULL; pNew->pCurrent = pDummyTask; pNew->pHead = pDummyTask; *self = pNew; return 1; } /*---------------------------------------------------------------------------*/ int TaskerDelete(pTaskMan * pData) { pTaskMan self = *pData; pTaskHead pCurrent, pKill; assert(self); assert(self->iID == TASKERID); pCurrent = self->pHead; while (pCurrent != NULL) { pKill = pCurrent; pCurrent = pCurrent->pNext; DeleteTaskHead(pKill); } free(self); *pData = NULL; return 1; } /*----------------------- temporary for backwards compatability -------------*/ long TaskRegister(pTaskMan self,TaskFunc pTask, SignalFunc pSignal, TaskKillFunc pKill, void *pData, int iPriority) { return TaskRegisterN(self,"Unknown", pTask, pSignal, pKill, pData, iPriority); } /*---------------------------------------------------------------------------*/ long TaskRegisterN(pTaskMan self, char *name, TaskFunc pTask, SignalFunc pSignal, TaskKillFunc pKill, void *pData, int iPriority) { pTaskHead pNew = NULL; assert(self); assert(self->iID == TASKERID); assert(pTask); pNew = MakeTaskHead(name, pTask, pSignal, pData, pKill); if (!pNew) { return -1; } /* link it in */ if (self->pCurrent->pNext) { self->pCurrent->pNext->pPrevious = pNew; } pNew->pPrevious = self->pCurrent; pNew->pNext = self->pCurrent->pNext; self->pCurrent->pNext = pNew; return pNew->lID; } /*-------------------------------------------------------------------------*/ static void IncrTaskPointer(pTaskMan self) { self->pCurrent = self->pCurrent->pNext; if (self->pCurrent == NULL) { self->pCurrent = self->pHead; } } /*-------------------------------------------------------------------------*/ static int TaskExist(pTaskMan self, long lID) { pTaskHead pCur = self->pHead; while (pCur != NULL) { if (pCur->lID == lID) { return 1; } pCur = pCur->pNext; } return 0; } /*--------------------------------------------------------------------------*/ int TaskSchedule(pTaskMan self) { int iRet; pTaskHead pTemp; assert(self); assert(self->iID == TASKERID); /* forever, until stop is called somehow */ while (self->iStop == 0) { if (self->pCurrent->iStatus == READY) { iRet = self->pCurrent->pRun(self->pCurrent->pData); if (iRet != 1) { pTemp = self->pCurrent; IncrTaskPointer(self); DeleteTaskHead(pTemp); } else { IncrTaskPointer(self); } } else { IncrTaskPointer(self); } } return 1; } /*-----------------------------------------------------------------*/ int TaskWait(pTaskMan self, long lID) { int iRet; long lTest; pTaskHead pTemp, pEnd; assert(self); assert(self->iID == TASKERID); /* Cycle until lID is killed. Stop is obeyed as well */ pEnd = self->pCurrent; pEnd->iStatus = WAITING; IncrTaskPointer(self); while (self->iStop == 0) { if ((self->pCurrent != pEnd) && (self->pCurrent->iStatus == READY) && self->pCurrent != NULL) /* omit ourselves! */ { iRet = self->pCurrent->pRun(self->pCurrent->pData); if (iRet != 1) { pTemp = self->pCurrent; lTest = pTemp->lID; IncrTaskPointer(self); DeleteTaskHead(pTemp); if (lTest == lID) { goto ente; } } else { IncrTaskPointer(self); } } else { IncrTaskPointer(self); } if (self->pCurrent == self->pHead) { if (!TaskExist(self, lID)) goto ente; } } ente: /* task ended, we need to continue to pEnd before we are done */ while (self->pCurrent != pEnd) { IncrTaskPointer(self); } pEnd->iStatus = READY; return 1; } /*----------------------------------------------------------------*/ int TaskYield(pTaskMan self) { int iRet; long lTest; pTaskHead pTemp, pEnd; assert(self); assert(self->iID == TASKERID); /* Cycle until back at ourselves */ pEnd = self->pCurrent; pEnd->iStatus = WAITING; IncrTaskPointer(self); while ((self->iStop == 0) && (self->pCurrent != pEnd)) { if ((self->pCurrent != pEnd) && (self->pCurrent->iStatus == READY)) /* omit ourselves! */ { iRet = self->pCurrent->pRun(self->pCurrent->pData); if (iRet != 1) { pTemp = self->pCurrent; lTest = pTemp->lID; IncrTaskPointer(self); DeleteTaskHead(pTemp); } else { IncrTaskPointer(self); } } else { IncrTaskPointer(self); } } pEnd->iStatus = READY; return 1; } /*---------------------------------------------------------------------------*/ int TaskSignal(pTaskMan self, int iSignal, void *pSigData) { pTaskHead pTemp, pEnd; assert(self); assert(self->iID == TASKERID); /* Do one cycle until we are at the caller, then return to him */ pEnd = self->pCurrent; IncrTaskPointer(self); while (self->pCurrent != pEnd) { if (self->pCurrent->pSignal) { self->pCurrent->pSignal(self->pCurrent->pData, iSignal, pSigData); } IncrTaskPointer(self); } /* finally, tell me about the thingie as well */ if (pEnd->pSignal) { pEnd->pSignal(pEnd->pData, iSignal, pSigData); } return 1; } /*--------------------------------------------------------------------------*/ int TaskStop(pTaskMan self) { assert(self); assert(self->iID == TASKERID); self->iStop = 1; return 1; } /*--------------------------------------------------------------------------*/ int TaskContinue(pTaskMan self) { assert(self); assert(self->iID == TASKERID); self->iStop = 0; return 1; } /*--------------------------------------------------------------------------*/ void TaskRemove(pTaskMan self, TaskFunc pTaskRun, void *pData) { int iRet; pTaskHead pCurrent, pNext; if (self == NULL) return; assert(self->iID == TASKERID); pNext = self->pHead->pNext; /* skip dummy task */ while (pNext != NULL) { pCurrent = pNext; pNext = pCurrent->pNext; if (pCurrent->pRun == pTaskRun && pCurrent->pData == pData) { if(pCurrent == self->pCurrent){ /* cannot kill myself */ return; } /* unlink */ if (pCurrent->pPrevious != NULL) { pCurrent->pPrevious->pNext = pCurrent->pNext; } if (pCurrent->pNext != NULL) { pCurrent->pNext->pPrevious = pCurrent->pPrevious; } if(pCurrent->name != NULL){ free(pCurrent->name); } free(pCurrent); } } return; } /*-----------------------------------------------------------------------------*/ int StopTask(pTaskMan self, char *name) { int iRet; pTaskHead pCurrent, pNext; if (self == NULL) return 0; assert(self->iID == TASKERID); pNext = self->pHead->pNext; /* skip dummy task */ while (pNext != NULL) { pCurrent = pNext; pNext = pCurrent->pNext; if (strcmp(pCurrent->name,name) == 0) { if(self->pCurrent == pCurrent){ /** * cannot kill myself */ return 0; } /* unlink */ if (pCurrent->pPrevious != NULL) { pCurrent->pPrevious->pNext = pCurrent->pNext; } if (pCurrent->pNext != NULL) { pCurrent->pNext->pPrevious = pCurrent->pPrevious; } if(pCurrent->name != NULL){ free(pCurrent->name); } free(pCurrent); } } return 1; } /*-----------------------------------------------------------------------------*/ int isTaskRunning(pTaskMan self, char *name) { int iRet; pTaskHead pCurrent, pNext; if (self == NULL) return 0; assert(self->iID == TASKERID); pNext = self->pHead->pNext; /* skip dummy task */ while (pNext != NULL) { pCurrent = pNext; pNext = pCurrent->pNext; if (strcmp(pCurrent->name,name) == 0) { return 1; } } return 0; } /*-----------------------------------------------------------------------------*/ int isTaskIDRunning(pTaskMan self, long lID) { int iRet; pTaskHead pCurrent, pNext; if (self == NULL) return 0; assert(self->iID == TASKERID); pNext = self->pHead->pNext; /* skip dummy task */ while (pNext != NULL) { pCurrent = pNext; pNext = pCurrent->pNext; if (pCurrent->lID == lID) { return 1; } } return 0; } /*-----------------------------------------------------------------------------*/ pTaskHead TaskIteratorStart(pTaskMan self) { if (self == NULL) return NULL; assert(self->iID == TASKERID); return self->pHead->pNext; /* skip dummy task */ } /*-----------------------------------------------------------------------------*/ pTaskHead TaskIteratorNext(pTaskHead it) { if(it != NULL){ return it->pNext; } return NULL; } /*-----------------------------------------------------------------------------*/ char *TaskDescription(pTaskHead it) { char *result; int length; const struct tm *tm; if(it == NULL){ return NULL; } length = strlen(it->name) + 120; result = malloc(length*sizeof(char)); if(result == NULL){ return NULL; } memset(result,0,length*sizeof(char)); strcpy(result,it->name); strcat(result,"|"); length = strlen(result); tm = localtime((const time_t *)&it->start_time); strftime(result+length,100,"%F-%k-%m-%S",tm); length = strlen(result); snprintf(result+length,120-20,"|%ld", it->lID); length = strlen(result); snprintf(result+length,120-40,"|%.0ld ", it->groupID); return result; } /*------------------------------------------------------------------------------*/ long GetTaskID(pTaskHead it) { return it->lID; } /*------------------------------------------------------------------------------*/ long GetGroupID(pTaskHead it) { return it->groupID; } /*------------------------------------------------------------------------------*/ const char * GetTaskName(pTaskHead it) { return (const char*)it->name; } /*------------------------------------------------------------------------------*/ long GetTaskGroupID(pTaskMan self) { lIDMama++; return lIDMama; } /*-------------------------------------------------------------------------------*/ void AddTaskToGroup(pTaskMan self, long taskID, long groupID) { pTaskHead pCurrent, pNext; if (self == NULL) return; assert(self->iID == TASKERID); pNext = self->pHead->pNext; /* skip dummy task */ while (pNext != NULL) { pCurrent = pNext; pNext = pCurrent->pNext; if (pCurrent->lID == taskID) { pCurrent->groupID = groupID; return; } } } /*--------------------------------------------------------------------------------- This simply checks if there are any more tasks with the desired groupID in the list. If none, then all sub tasks have finished. -----------------------------------------------------------------------------------*/ int isTaskGroupRunning(pTaskMan self, long groupID) { pTaskHead pCurrent, pNext; if (self == NULL) return 0; if (groupID == IDUNDEFINED) return 0; assert(self->iID == TASKERID); pNext = self->pHead->pNext; /* skip dummy task */ while (pNext != NULL) { pCurrent = pNext; pNext = pCurrent->pNext; if (pCurrent->groupID != IDUNDEFINED && pCurrent->groupID == groupID) { return 1; } } return 0; } /*------------------------------------------------------------------------------*/ int TaskGroupTask(void *data) { pTaskGroupData self = (pTaskGroupData)data; return isTaskGroupRunning(self->tasker,self->groupID); } /*-------------------------------------------------------------------------------*/ int TaskSignalGroup(pTaskMan self, int iSignal, void *pSigData, long groupID) { pTaskHead pTemp, pEnd; assert(self); assert(self->iID == TASKERID); /* Do one cycle until we are at the caller, then return to him */ pEnd = self->pCurrent; IncrTaskPointer(self); while (self->pCurrent != pEnd) { if (self->pCurrent->pSignal && self->pCurrent->groupID == groupID) { self->pCurrent->pSignal(self->pCurrent->pData, iSignal, pSigData); } IncrTaskPointer(self); } return 1; }