double runKernel(double externalTime) { double nextHit, timeElapsed; Task *task, *temp, *oldrunning, *newrunning; UserTask *usertask; InterruptHandler *hdl; DataNode* dn; // If no energy, then we can not run if (rtsys->energyLevel <= 0) { //printf("Energy is out at time: %f\n", rtsys->time); return INF; } timeElapsed = externalTime - rtsys->prevHit; // time since last invocation rtsys->prevHit = externalTime; // update previous invocation time nextHit = 0.0; //printf("runkernel at %f\n", rtsys->time); #ifdef KERNEL_MATLAB /* Write rtsys pointer to global workspace */ *((long *)mxGetPr(rtsys->rtsysptr)) = (long)rtsys; #endif while (nextHit < EPS) { // Count down execution time for current task (usertask or handler) // and check if it has finished its execution task = rtsys->running; if (task != NULL) { // Count down execution time task->execTime -= timeElapsed * rtsys->cpuScaling; if (task->execTime < EPS) { // Execute next segment task->segment++; if (task->isUserTask()) { usertask = (UserTask*) task; // Update budget and lastStart variable at segment change usertask->budget -= (rtsys->time - usertask->lastStart); usertask->lastStart = rtsys->time; } // Execute next segment of the code function #ifndef KERNEL_MATLAB task->execTime = task->codeFcn(task->segment, task->data); if (rtsys->error) { printf("Error in ==> task '%s', segment %d\n", task->name, task->segment); return 0.0; } #else if (task->codeFcnMATLAB == NULL) { task->execTime = task->codeFcn(task->segment, task->data); } else { task->execTime = executeCode(task->codeFcnMATLAB, task->segment, task); } if (rtsys->error) { printf("Error in ==> task '%s', segment %d\n", task->name, task->segment); return 0.0; } #endif if (task->execTime < 0.0) { // Negative execution time = task finished task->execTime = 0.0; task->segment = 0; if (task->myList == rtsys->readyQ) { // Remove task from readyQ task->remove(); } if (!(task->isUserTask())) { hdl = (InterruptHandler*) task; if (hdl->type == TIMER) { if (hdl->timer->isPeriodic) { // if periodic timer put back in timeQ hdl->timer->time += hdl->timer->period; hdl->moveToList(rtsys->timeQ); } else { // Remove timer and free up handler dn = getNode(hdl->timer->name, rtsys->timerList); rtsys->timerList->deleteNode(dn); delete hdl->timer; hdl->timer = NULL; hdl->type = UNUSED; } } if (hdl->type == EXTERNAL) { if (hdl->pending > 0) { // new external interrupt occured before handler finished hdl->pending--; hdl->moveToList(rtsys->readyQ); } } } else { // the finished task is a usertask usertask = (UserTask*) task; // Execute finish-hook usertask->finish_hook(usertask); usertask->state = IDLE; // Release next job if any usertask->nbrJobs--; if (usertask->nbrJobs > 0) { // next pending release dn = (DataNode*) usertask->pending->getFirst(); double* release = (double*) dn->data; usertask->release = *release; usertask->absDeadline = *release + usertask->deadline; usertask->moveToList(rtsys->timeQ); usertask->pending->deleteNode(dn); delete release; // Execute release-hook usertask->release_hook(usertask); usertask->state = SLEEPING; } } } } } // end: counting down execution time of running task // Check time queue for possible releases task = (Task*) rtsys->timeQ->getFirst(); while (task != NULL) { if ((task->wakeupTime() - rtsys->time) < EPS) { // Task to be released temp = task; task = (Task*) task->getNext(); temp->moveToList(rtsys->readyQ); if (temp->isUserTask()) { usertask = (UserTask*) temp; usertask->state = READY; } } else { break; } } // end: checking timeQ for releases // Determine task with highest priority and make it running task newrunning = (Task*) rtsys->readyQ->getFirst(); oldrunning = rtsys->running; if (newrunning != NULL) { // Check for suspend- and resume-hooks if (oldrunning != NULL) { // Is oldrunning being suspended? if (oldrunning->isUserTask()) { if (newrunning != oldrunning && ((UserTask*) oldrunning)->state == RUNNING) { usertask = (UserTask*) oldrunning; usertask->state = SUSPENDED; usertask->suspend_hook(usertask); } } } // invocation of hooks may have triggered kernelHandler newrunning = (Task*) rtsys->readyQ->getFirst(); // Is newrunning being resumed? if (newrunning->isUserTask()) { if ( (((UserTask*) newrunning)->state == READY) || (((UserTask*) newrunning)->state == SUSPENDED) ) { // newrunning is being resumed or started usertask = (UserTask*) newrunning; usertask->state = RUNNING; if (usertask->segment == 0) { usertask->start_hook(usertask); } else { usertask->resume_hook(usertask); } } } // invocation of hooks may have triggered kernelHandler rtsys->running = (Task*) rtsys->readyQ->getFirst(); } else { // No tasks in readyQ rtsys->running = NULL; } // end: task dispatching // Determine next invocation of kernel nextHit = getNextInvocation(); timeElapsed = 0.0; } // end: loop while nextHit < EPS return nextHit; }
double runKernel(double externalTime) { Task *task, *temp, *newrunning; UserTask *usertask; InterruptHandler *handler; DataNode* dn; // If no energy, then we can not run if (rtsys->energyLevel <= 0) { debugPrintf("'%s': Energy is out at time: %f\n", rtsys->blockName, rtsys->time); return TT_MAX_TIMESTEP; } double timeElapsed = externalTime - rtsys->prevHit; // time since last invocation rtsys->prevHit = externalTime; // update previous invocation time debugPrintf("'%s': runkernel at %.16f\n", rtsys->blockName, rtsys->time); #ifdef KERNEL_MATLAB // Write rtsys pointer to global workspace so that MATLAB kernel // primitives can access it *((long *)mxGetPr(rtsys->rtsysptr)) = (long)rtsys; #endif double timestep = 0.0; int niter = 0; while (timestep < TT_TIME_RESOLUTION) { if (++niter == TT_MAX_ITER) { mexPrintf("??? Fatal kernel error: maximum number of iterations reached!\n"); rtsys->error = 1; return 0.0; } // For each CPU, count down execution time for the current task for (int i=0; i<rtsys->nbrOfCPUs; i++) { debugPrintf("running core %d\n", i); rtsys->currentCPU = i; rtsys->running = rtsys->runnings[i]; task = rtsys->running; if (task != NULL) { if (task->state == RUNNING) { task->state = READY; } double duration = timeElapsed * rtsys->cpuScaling; // Decrease remaining execution time for current segment and increase total CPU time task->execTime -= duration; task->CPUTime += duration; // If user task, call runkernel hook (to e.g. update budgets) if (task->isUserTask()) { usertask = (UserTask*)task; usertask->runkernel_hook(usertask,duration); } // Check if task has finished current segment or not yet started if (task->execTime / rtsys->cpuScaling < TT_TIME_RESOLUTION) { // Execute next segment task->segment = task->nextSegment; task->nextSegment++; // default, can later be changed by ttSetNextSegment #ifndef KERNEL_MATLAB debugPrintf("'%s': executing code segment %d of task '%s'\n", rtsys->blockName, task->segment, task->name); task->execTime = task->codeFcn(task->segment, task->data); if (rtsys->error) { TT_RUNKERNEL_ERROR(errbuf); mexPrintf("In task ==> '%s', code segment %d\n", task->name, task->segment); return 0.0; } #else if (task->codeFcnMATLAB == NULL) { task->execTime = task->codeFcn(task->segment, task->data); } else { mxArray *lhs[2]; mxArray *rhs[2]; debugPrintf("'%s': executing code function '%s'\n", rtsys->blockName, task->codeFcnMATLAB); *mxGetPr(rtsys->segArray) = (double)task->segment; rhs[0] = rtsys->segArray; if (task->dataMATLAB) { rhs[1] = task->dataMATLAB; } else { rhs[1] = mxCreateDoubleMatrix(0, 0, mxREAL); } mexSetTrapFlag(1); // return control to the MEX file after an error lhs[0] = NULL; // needed not to crash Matlab after an error lhs[1] = NULL; // needed not to crash Matlab after an error if (mexCallMATLAB(2, lhs, 2, rhs, task->codeFcnMATLAB) != 0) { rtsys->error = true; return 0.0; } if (mxGetClassID(lhs[0]) == mxUNKNOWN_CLASS) { snprintf(errbuf, MAXERRBUF, "Execution time not assigned in code function '%s'", task->codeFcnMATLAB); TT_RUNKERNEL_ERROR(errbuf); rtsys->error = true; return 0.0; } if (!mxIsDoubleScalar(lhs[0])) { snprintf(errbuf, MAXERRBUF, "Illegal execution time returned by code function '%s'", task->codeFcnMATLAB); TT_RUNKERNEL_ERROR(errbuf); rtsys->error = true; return 0.0; } if (mxGetClassID(lhs[1]) == mxUNKNOWN_CLASS) { snprintf(errbuf, MAXERRBUF, "Data not assigned in code function '%s'", task->codeFcnMATLAB); TT_RUNKERNEL_ERROR(errbuf); rtsys->error = true; return 0.0; } //if ( task->dataMATLAB ) { if ( task->dataMATLAB != lhs[1] ) { mxDestroyArray(task->dataMATLAB); //task->dataMATLAB = mxDuplicateArray(lhs[1]); task->dataMATLAB = lhs[1]; mexMakeArrayPersistent(task->dataMATLAB); } task->execTime = *mxGetPr(lhs[0]); //mxDestroyArray(rhs[1]); mxDestroyArray(lhs[0]); //mxDestroyArray(lhs[1]); } #endif if (task->execTime < 0.0) { // Negative execution time = task is finished debugPrintf("'%s': task '%s' finished\n", rtsys->blockName, task->name); task->execTime = 0.0; task->segment = 0; task->nextSegment = 1; // Remove task from readyQ and set running to NULL if (task->state == READY) { task->remove(); task->state = SLEEPING; } else { snprintf(errbuf, MAXERRBUF, "Finished task '%s' not in ReadyQ.", task->name); TT_RUNKERNEL_ERROR(errbuf); rtsys->error = true; return 0.0; } rtsys->runnings[i] = NULL; rtsys->running = NULL; if (task->isUserTask()) { // Execute finish-hook usertask = (UserTask*)task; usertask->finish_hook(usertask); rtsys->runningUserTasks[i] = NULL; } task->nbrInvocations--; if (task->nbrInvocations > 0) { // There are queued invocations, release the next one dn = (DataNode*) task->pending->getFirst(); TaskInvocation *ti = (TaskInvocation *)dn->data; if (task->isUserTask()) { usertask = (UserTask*)task; usertask->arrival = ti->timestamp; usertask->release = rtsys->time; usertask->release_hook(usertask); // could affect task prio } debugPrintf("'%s': releasing task '%s'\n", rtsys->blockName, task->name); task->moveToList(rtsys->readyQs[task->affinity]); // re-insert task into readyQ task->state = READY; if (task->isHandler()) { handler = (InterruptHandler*)task; strncpy(handler->invoker, ti->invoker, MAXCHARS); handler->timestamp = ti->timestamp; } task->pending->deleteNode(dn); delete ti; } } } } } // Check time queue for possible releases and expired timers task = (Task*) rtsys->timeQ->getFirst(); while (task != NULL) { if ((task->wakeupTime() - rtsys->time) >= TT_TIME_RESOLUTION) { break; // timeQ is sorted by time, no use to go further } // Task to be released temp = task; task = (Task*) task->getNext(); if (temp->isTimer()) { Timer *timer = (Timer*)temp; debugPrintf("'%s': timer '%s' expired at %f\n", rtsys->blockName, timer->name, rtsys->time); invoke_task(timer->task, timer->name); if (timer->isPeriodic) { // if periodic timer put back in timeQ timer->time += timer->period; timer->moveToList(rtsys->timeQ); } else { timer->remove(); // remove timer from timeQ if (!timer->isOverrunTimer) { // delete the timer dn = getNode(timer->name, rtsys->timerList); rtsys->timerList->deleteNode(dn); delete timer; } } } else if (temp->isUserTask()) { usertask = (UserTask*)temp; debugPrintf("'%s': releasing task '%s'\n", rtsys->blockName, usertask->name); usertask->moveToList(rtsys->readyQs[usertask->affinity]); usertask->state = READY; } else if (temp->isHandler()) { mexPrintf("??? Fatal kernel error: interrupt handler in TimeQ!\n"); rtsys->error = 1; return 0.0; } } // end: checking timeQ for releases // For each core, determine the task with highest priority and make it running task for (int i=0; i<rtsys->nbrOfCPUs; i++) { debugPrintf("scheduling core %d\n", i); rtsys->currentCPU = i; newrunning = (Task*) rtsys->readyQs[i]->getFirst(); // If old running has been preempted, execute suspend_hook if (rtsys->runnings[i] != NULL && rtsys->runnings[i] != newrunning) { if (rtsys->runnings[i]->isUserTask()) { usertask = (UserTask*)rtsys->runnings[i]; usertask->suspend_hook(usertask); } } // If new running != old running, execute start_hook or resume_hook if (newrunning != NULL && newrunning != rtsys->runnings[i]) { if (newrunning->isUserTask()) { usertask = (UserTask*)newrunning; if (usertask->segment == 0) { usertask->segment = 1; usertask->start_hook(usertask); } else { usertask->resume_hook(usertask); } rtsys->runningUserTasks[i] = usertask; } } rtsys->runnings[i] = (Task*) rtsys->readyQs[i]->getFirst(); // hooks may have released handlers if (rtsys->runnings[i] != NULL) { rtsys->runnings[i]->state = RUNNING; } } // Determine next invocation of kernel double compTime; timestep = TT_MAX_TIMESTEP; // Next release from timeQ (user task or timer) if (rtsys->timeQ->getFirst() != NULL) { Task* t = (Task*) rtsys->timeQ->getFirst(); timestep = t->wakeupTime() - rtsys->time; } // Remaining execution time of running tasks for (int i=0; i<rtsys->nbrOfCPUs; i++) { if (rtsys->runnings[i] != NULL) { compTime = rtsys->runnings[i]->execTime / rtsys->cpuScaling; timestep = (timestep < compTime) ? timestep : compTime; } } timeElapsed = 0.0; } // end: loop while timestep < TT_TIME_RESOLUTION return timestep; }