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

Reply via email to