5. Examples

Browsing
Home: | Fair Threads in C
Previous chapter: | API
Next chapter: | Related Work
Examples
    5.1 Hello World!
    5.2 Blocking I/O
    5.3 Producer/Consumer
    5.4 Automata
Chapters
    1. Introduction
    2. Rationale
    3. API Overview
    4. API
    5. Examples
    6. Related Work
    7. Conclusion
    8. Man Pages
5.1 Hello World!
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
5.2 Blocking I/O
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;
    }
5.3 Producer/Consumer
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 event `new_input` is 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, if `new_input` is 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;
    }
5.4 Automata
### Preemption by an Event Here is the example of a one-state automaton, named `killer`, that preempts a thread when an event is present. The thread and the event are accessible with the macro `ARGS`.
    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_create` is that no new pthread is actually created by `ft_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 automaton `switch_aut` has 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.

This page has been generated by Scribe.
Last update Tue Jun 4 10:09:20 2002