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();
}