Besides any discussion on whether the OP's question makes sense or not, a possible solution could be the following:
Have a signal handler installed for each thread to be scheduled. This handler is triggered on say SIGUSR1
and internally does nothing more than to invoked a call to pause()
.
The thread functions all start with a call to pause()
, which suspends all threads immediatly after creation.
Create all threads to be schedules using pthread_create()
. Stores the pthreads created into an array pthreads
.
Assign the first pthread to be run (from pthread
) to pthread_first
.
To start scheduling call pthread_kill(pthread_first, SIGUSR2)
to resume the thread to be run first (by makeing the pause()
its blocking on to return). Make pthread_current
become pthread_first
.
To actually perform scheduling an additional thread (perhaps the main thread) loops infinitly and calls sleep(SCHEDULING_INTERVALL)
and then calls pthread_kill(pthread_current, SIGUSR1)
to suspend the current thread (by invoking its signal handler and with this running into pause()
). Then call pthread_kill(pthread_next, SIGUSR2)
to resume the next thread (by makeing the pause()
its blocking on to return). Make pthreat_current
become pthread_next
, and pthread_next
become another entry from the array pthread
filled during thread creation.
But be aware:
As the threads interupted by signals and suspended by pause()
might have been caught in the middle of doing some work on shared resources and stay their until resumed the chances of stepping on each other toes are quiet high.
To all others: yes, beat me ;-)
Update:
Equivalent example:
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#define THREADSMAX (3)
#define SCHEDULING_INTERVALL (5) /* seconds */
void
sigusr_handler(int signo)
{
if (SIGUSR1 == signo)
{
pause();
}
}
void *
thread_function(void * pv)
{
intptr_t iThread = (intptr_t) pv;
pause();
{
int i = 0;
for (;;)
{
printf("%d: %d\n", (int) iThread, i++);
sleep(1);
}
}
pthread_exit(NULL);
}
int
main(int argc, char ** argv)
{
struct sigaction signal_action;
memset(&signal_action, 0, sizeof(signal_action));
signal_action.sa_handler = sigusr_handler;
sigemptyset(&signal_action.sa_mask);
sigaction(SIGUSR1, &signal_action, NULL);
sigaction(SIGUSR2, &signal_action, NULL);
{
pthread_t threads[THREADSMAX] =
{ 0 };
intptr_t iThread = 0;
/* create threads */
for (; iThread < THREADSMAX; ++iThread)
{
int iResult = pthread_create(&threads[iThread], NULL, thread_function,
(void *) iThread);
if (iResult)
{
errno = iResult;
perror("pthread_created()");
exit(1);
}
}
sleep(1); /* Unreliable workaround: Try to make sure all threads have started and block in "pause()". See comments on how this might be fixed nicely ... */
/* scheduling loop */
for (iThread = 0;; ++iThread)
{
if (THREADSMAX == iThread)
{
iThread = 0;
}
/* Resume current thread */
{
int iResult = pthread_kill(threads[iThread], SIGUSR2);
if (iResult)
{
errno = iResult;
perror("pthread_kill(..., SIGUSR2)");
exit(2);
}
}
sleep(SCHEDULING_INTERVALL);
/* Suspend current thread */
{
int iResult = pthread_kill(threads[iThread], SIGUSR1);
if (iResult)
{
errno = iResult;
perror("pthread_kill(..., SIGUSR1)");
exit(3);
}
}
}
}
return 0;
}
Expected output:
0: 0
0: 1
0: 2
0: 3
0: 4
1: 0
1: 1
1: 2
1: 3
1: 4
2: 0
2: 1
2: 2
2: 3
2: 4
0: 5
0: 6
0: 7
0: 8
0: 9
1: 5
1: 6
1: 7
1: 8
1: 9
2: 5
2: 6
...