| 
 
 
 
 | 
The following code is a complete example, made of two threads
run in the same scheduler.
#include "fthread.h"
#include <stdio.h>
void h (void *id)
{
   while (1) {
      fprintf (stderr,"Hello ");
      ft_thread_cooperate ();
   }
}
void w (void *id)
{
   while (1) {
      fprintf (stderr,"World!\n");
      ft_thread_cooperate ();
   }
}
int main (void)
{
  ft_scheduler_t sched = ft_scheduler_create ();
  ft_thread_create (sched,h,NULL,NULL);
  ft_thread_create (sched,w,NULL,NULL);
  ft_scheduler_start (sched);
  ft_exit ();
  return 0;
}
The program outputs Hello World! cyclically. Note the call
of ft_exit to prevent the program to terminate before
executing the two threads. Execution of linked fair threads is
round-robin and deterministic: messages Hello and World! are always printed in this order. 
 Here is the typical way to produce executable code:
gcc -D_REENTRANT -o test test.c -lfthread -lpthread 
 
The following function ft_thread_read implements a non-blocking read
I/O, using the standard blocking read function. The calling
thread first unlinks from the scheduler, then performs the read, and
finally re-links to the scheduler:
ssize_t ft_thread_read (int fd,void *buf,size_t count)
{ 
   ft_scheduler_t sched = ft_thread_scheduler ();
   ssize_t res;
   
   ft_thread_unlink ();
   
   res = read (fd,buf,count);
   
   ft_thread_link (sched);
   return res;
}
 
One implements a producer/consumer example. There are 2 files, in and out, and
a pool of threads that take data from  in, process them, and then put results in
out. A scheduler and an event are associated to each file; the event is generated
to indicate that a new value is produded in the associated file.
file in = NULL, out = NULL;
ft_scheduler_t in_sched, out_sched;
ft_event_t new_input, new_output;
 
Processing Values
In order to process a value v, the calling thread first
unlinks from in_sched. After processing, it links to out_sched in order to put the result in out, and finally, it
re-links to in_sched.  The procedure for processing a value is
the following (for simplicity, values are of type int):void process_value (int v)
{
  ft_thread_unlink ();
  < process v >
  ft_thread_link (out_sched);
  put (v,&out);
  ft_thread_generate (new_output);
  ft_thread_unlink ();
  ft_thread_link (in_sched);
}
The function run by the processing threads is:void process (void *args)
{
  while (1) {
    if (size(in) > 0) {
      process_value (get (&in));
    } else {
      ft_thread_await (new_input);
      if (size (in) == 0) ft_thread_cooperate ()
    }
  }
}
The eventnew_inputis used to prevent polling when no value
is available from in. However, to test it as present does not
necessary implies that a value is available: it could have been consumed
by another thread. Thus, a call to ft_thread_cooperate is needed to avoid an infinite loop
during the same instant, ifnew_inputis tested as present
while no value is actually available.
Main Function
Two threads are added to the system: one for producing
new values, and the other for consuming results.  The main
function is the following:int main (void)
{
   int i;
   ft_thread_t thread_array [MAX_THREADS]
   in_sched  = ft_scheduler_create ();
   out_sched = ft_scheduler_create ();
   
   new_input  = ft_event_create (in_sched);
   new_output = ft_event_create (out_sched);     
   for (i=0; i<MAX_THREADS; i++) {
      thread_array[i] = ft_thread_create (in_sched,process,NULL,NULL);
   }
  
   ft_thread_create (in_sched,produce,NULL,NULL);
   ft_thread_create (out_sched,consume,NULL,NULL);
   ft_scheduler_start (in_sched);
   ft_scheduler_start (out_sched);  
   ft_exit ();
   return 0;
}
 
Preemption by an Event
Here is the example of a one-state automaton, namedkiller,
that preempts a thread when an event is present. The thread and the event are accessible
with the macroARGS.DEFINE_AUTOMATON (killer)
{
   void **args = ARGS;
   ft_event_t event   = args[0]
   ft_thread_t thread = args[1]
   
   BEGIN_AUTOMATON
     
     STATE_AWAIT (0,event)
     {
        ft_scheduler_stop (thread);
     }
  
   END_AUTOMATON
}
A fair thread is created by:ft_thread_t a = ft_automaton_create (sched,killer,NULL,args);
The difference with a standard thread created by ft_thread_createis that no new pthread is
actually created byft_automaton_create. The automaton is simply run by the scheduler's pthread.
Thus, no pthread context switch is needed and execution is more efficient.
Two Threads Run in Turn
The following automaton switches control between two threads, according to the presence of an event.
The automatonswitch_authas three states. The first state resumes the first thread to run 
(initially, both threads are suspended).
The switching event is awaited in the second state, and then the threads are switched. The third state
is similar to the second, except that the threads are exchanged.DEFINE_AUTOMATON (switch_aut)
{
   void **args = ARGS;
   
   ft_event_t   event   = args[0]
   ft_thread_t  thread1 = args[1]
   ft_thread_t  thread2 = args[2]
   
  BEGIN_AUTOMATON
     
     STATE (0)
     {
        ft_scheduler_resume (thread1);
     }
     STATE_AWAIT (1,event)
     {
        ft_scheduler_suspend (thread1);
        ft_scheduler_resume  (thread2);
        GOTO(2);
     }
     STATE_AWAIT (2,event)
     {
        ft_scheduler_suspend (thread2);
        ft_scheduler_resume  (thread1);
        GOTO(1);
     }
     
  END_AUTOMATON
}
If a standard thread were used instead of an automaton, one supplementary pthread
would be needed to perform the same task. |