A while back I was exploring the use of an idle task outside of the pth library. In order to do this I had to create a function within the file pth_sched.c:
void pth_events_update(void) { // FIXME: messes up load average. pth_time_t useTime; pth_time_set(&useTime, PTH_TIME_NOW); pth_sched_eventmanager(&useTime, TRUE); } As noted in the comment above, the load average calculation gets messed up when this function gets called. How do I fix that? Attached is the sample program that shows how this is used. There is also an "#if 0" part of the code that shows what happens when this function is not available.
/* * Quick test for our pth_events_update() hack in scheduler. Basically * we want a starvable idle task that will yield when any other * task is ready or is new, even before it processes anything. We * currently can't have this because even the lowest priority task * will automatically rise in priority, via the scheduler. We also want * to do some cleanup once we detect that we will do a context switch. * This may be useful as part of dynamically adjusting the number of * active tasks. * * In source file pth_sched.c: * void * pth_events_update(void) * { * // FIXME: messes up load average. * pth_time_t useTime; * pth_time_set(&useTime, PTH_TIME_NOW); * pth_sched_eventmanager(&useTime, TRUE); * } * * Also add prototype in pth.h.in source file, or pth.h generated file: * extern void pth_events_update(void); * * * 22-Jul-2007 * Maurice Havelday * SimDevices.com */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <pth.h> #define CHECK_RN_QS (PTH_CTRL_GETTHREADS_READY | PTH_CTRL_GETTHREADS_NEW) static void *dummy(void *); static void *task1(void *); static void *task2(void *); static void *bliptask(void *); static void *faux_idle(void *); static void *broken_idle(void *); typedef struct tagTaskRoster { char *taskName; pth_t taskId; int taskStackSize; unsigned int flags; int taskPrio; void *(*taskEntry)(void *); } TaskRoster; TaskRoster roster[] = { { "task 1", 0, 32*1024, PTH_ATTR_JOINABLE, PTH_PRIO_STD, task1 }, { "task 2", 0, 32*1024, PTH_ATTR_JOINABLE, PTH_PRIO_STD, task2 }, { "bliptask", 0, 32*1024, PTH_ATTR_JOINABLE, PTH_PRIO_STD, bliptask }, #if 0 { "broken_idle", 0, 32*1024, PTH_ATTR_JOINABLE, PTH_PRIO_MIN, broken_idle }, #else { "faux_idle", 0, 32*1024, PTH_ATTR_JOINABLE, PTH_PRIO_MIN, faux_idle }, #endif { NULL, 0, 0, 0, 0, dummy } }; static void * dummy(void *stuff) { if (stuff) printf("\nDummy called with stuff!\n"); else printf("\nDummy called without stuff!\n"); for(;;) pth_sleep(60); return NULL; } static void * task1(void *stuff) { time_t now; int maxCount = 5; printf("\ntask 1 running\n"); for(;;) { pth_sleep(4); now = time(NULL); printf("\ntask 1 was woken up on %s", ctime(&now)); if (--maxCount <= 0) break; } } static void * task2(void *stuff) { time_t now; int maxCount = 6; printf("\ntask 2 running\n"); for(;;) { pth_sleep(5); now = time(NULL); printf("\ntask 2 was woken up on %s", ctime(&now)); if (--maxCount <= 0) break; } } static void * bliptask(void *stuff) { time_t now; printf("\nbliptask running\n"); pth_sleep(15); now = time(NULL); printf("\nbliptask was woken up on %s, now leaving...\n ", ctime(&now)); } // Want idle task without increase in priority, i.e., a task // that is allowed to starve. static void * faux_idle(void *stuff) { long numReady; time_t now; int flagYielded = 1; long idleSequentialCount = 0; printf("\nfaux_idle running\n"); for(;;) { // We don't want to call pth_yield() directly because we will // be unable to do cleanup if we do yield. On the other hand, // the call to check the ready and new queues will not // work as intended because the queues don't get updated until // the scheduler is able to check events. Thus, we need our // pth_events_update() hack in the pth library before checking // the number of tasks in the ready and new queues. pth_events_update(); numReady = pth_ctrl(CHECK_RN_QS, NULL); // if something is now ready or new, clean up and yield control if (numReady) { // Prep to yield printf("\nfaux_idle yielding\n"); pth_ctrl(PTH_CTRL_DUMPSTATE, stdout, NULL); flagYielded++; idleSequentialCount = 0; pth_yield(NULL); continue; // allow to starve } idleSequentialCount++; if (flagYielded) { now = time(NULL); printf("\nfaux_idle yielded %d times, and was woken up on %s\n", flagYielded, ctime(&now)); pth_ctrl(PTH_CTRL_DUMPSTATE, stdout, NULL); flagYielded = 0; } else if ( !(idleSequentialCount % 1000) ) { printf("."); fflush(stdout); } } } static void * broken_idle(void *stuff) { time_t now; int brokenCount = 0; printf("broken_idle running\n"); for(;;) { now = time(NULL); printf("\nbroken_idle was woken up on %s\n", ctime(&now)); pth_ctrl(PTH_CTRL_DUMPSTATE, stdout, NULL); for(;;) { // queues not updated with this call, so almost // guaranteed to halt multithreading if (pth_ctrl(CHECK_RN_QS, NULL) > 0) { brokenCount = 0; pth_yield(NULL); break; } else { brokenCount++; if ( !(brokenCount % 1000) ) { printf("!"); fflush(stdout); } } } } } int main(int argc, char *argv[]) { register int index; TaskRoster *tp; time_t now; pth_attr_t useAttr; if ( !pth_init() ) { perror("Failed initialization for threading library"); exit(-1); } for(index = 0; roster[index].taskName != NULL; index++) { tp = &roster[index]; useAttr = pth_attr_new(); if ( !useAttr ) { perror("Unable to spawn tasks!"); exit(-1); } pth_attr_set(useAttr, PTH_ATTR_NAME, tp->taskName); if (tp->flags & PTH_ATTR_JOINABLE) pth_attr_set(useAttr, PTH_ATTR_JOINABLE, TRUE); pth_attr_set(useAttr, PTH_ATTR_STACK_SIZE, tp->taskStackSize); pth_attr_set(useAttr, PTH_ATTR_PRIO, tp->taskPrio); tp->taskId = pth_spawn(useAttr, tp->taskEntry, NULL); pth_attr_destroy(useAttr); pth_yield(NULL); } for(;;) { pth_sleep(10); now = time(NULL); printf("\nMain loop was woken up on %s", ctime(&now)); printf("Press control-c or your kill key to stop.\n"); } // should never get here pth_kill(); }