Simple coroutine library / objects in plain C Snapshot
Classes | Typedefs | Enumerations | Functions
CTHREAD

a coroutine thread. More...

Classes

struct  tagCTHREAD

Typedefs

typedef void(* CTHREAD_PROC )(VALUES *)
typedef struct tagCTHREAD CTHREAD

Enumerations

enum  CTHREAD_STATE { CTHREAD_STATE_INIT, CTHREAD_STATE_RUNNING, CTHREAD_STATE_SUSPENDED, CTHREAD_STATE_EXIT }

Functions

int CTHREAD_libinit ()
 library initialisation
CTHREADCTHREAD_init (STACKS *stacks, CTHREAD_PROC proc)
 initialises a new co-routine thread.
int CTHREAD_free (CTHREAD *thread)
int CTHREAD_start (CTHREAD *thread, VALUES **rvalue, const char *format,...)
 start a new thread - the thread procedure is invoked.
int CTHREAD_resume (CTHREAD *thread, VALUES **rvalue, const char *format,...)
 resume a suspended thread
int CTHREAD_yield (VALUES **rvalue, const char *format,...)
 a running thread temporarily suspends execution
int CTHREAD_join (CTHREAD *thread, VALUES **rvalue)
 waits till the thread has finished Calls CTHREAD_resume repeatedly unntil the thread finishes.
int CTHREAD_set_return_value (const char *format,...)
 a running thread sets it return values
uint32_t CTHREAD_get_tid ()
 get thread id
M_INLINE CTHREAD_STATE CTHREAD_state (CTHREAD *thread)
 get thread state

Detailed Description

a coroutine thread.

A very limited user mode threading package that allows constructions of co-routines. Coroutine scheduling is done by mean of mkcontet/setcontext/getcontext functions (glibc)

User mode threads are best when used in a very limited scenario, complex usage scenarios often lead to intricate situations that are hard to debug. Areas where user mode threading shines


Typedef Documentation

typedef struct tagCTHREAD CTHREAD
typedef void(* CTHREAD_PROC)(VALUES *)

Definition at line 27 of file cthread.h.


Enumeration Type Documentation

Enumerator:
CTHREAD_STATE_INIT 
CTHREAD_STATE_RUNNING 
CTHREAD_STATE_SUSPENDED 
CTHREAD_STATE_EXIT 

Definition at line 29 of file cthread.h.


Function Documentation

int CTHREAD_free ( CTHREAD thread)

deallocates a thread

Precondition: the thread must be in CTHREAD_STATE_EXIT state

Definition at line 296 of file cthread.c.

{
  if (thread->state != CTHREAD_STATE_EXIT) {
    return -1;
  }
  free(thread);
  return 0;
}
uint32_t CTHREAD_get_tid ( )

get thread id

Precondition: the thread is in any state other than CTHREAD_STATE_INIT

Definition at line 305 of file cthread.c.

{
  CTHREAD *thread;
 
  thread = GET_TLS();
  if (!thread) {
    return (uint32_t) -1;
  }
  return thread->thread_id;
}
CTHREAD* CTHREAD_init ( STACKS stacks,
CTHREAD_PROC  proc 
)

initialises a new co-routine thread.

Postcondition: The new thread is initialised and enters CTHREAD_STATE_INIT state.

Parameters:
stacksthe thread is obtained from the pool of stacks (argument stack).
procThe thread procecdure is argument proc.

Definition at line 48 of file cthread.c.

{
  void *stack;
  CTHREAD *ret;
  int stage = 0;

  ret = (CTHREAD *) malloc( sizeof(CTHREAD) );
  if (!ret) {
    return 0;
  }
  stage = 1;

  if (VALUES_init(&ret->thread_to_caller_value)) {
    goto err;
  } 
  stage = 2;

  if (VALUES_init(&ret->caller_to_thread_value)) {
    goto err;
  } 
  stage = 3;

  stack = STACKS_get( stacks, &ret->stack_entry );
  if (!stack) {
    goto err;
  }
  stage = 4;

  ret->proc = proc;

  ret->prev_thread = 0;

  ret->thread_id = -1;   
  ret->state = CTHREAD_STATE_INIT;

  ret->caller_to_thread_value_set = ret->thread_to_caller_value_set = 0;
 
  if (getcontext( &ret->context_coroutine )) {
    if (ret->state == CTHREAD_STATE_INIT) {
      goto err;
    }
    return 0;
  }

  //fprintf( stderr,"thread stack start %p\n",stack);

  ret->context_coroutine.uc_stack.ss_sp = stack;
  ret->context_coroutine.uc_stack.ss_size = STACKS_get_stack_size( stacks );


  return ret;

err:
  if (stage > 3) {
    STACKS_release(ret->stack_entry);
  }
  if (stage > 2) {
    VALUES_free(&ret->caller_to_thread_value);
  }
   if (stage > 1) {
    VALUES_free(&ret->thread_to_caller_value);
  }
  if (stage > 0) {
    free(ret);
  }
  return 0;
}
int CTHREAD_join ( CTHREAD thread,
VALUES **  rvalue 
)

waits till the thread has finished Calls CTHREAD_resume repeatedly unntil the thread finishes.

Definition at line 189 of file cthread.c.

{  
   if( thread->state == CTHREAD_STATE_INIT ||
       thread->state == CTHREAD_STATE_RUNNING) {
     return -1;
   }
   while(thread->state != CTHREAD_STATE_EXIT) {
     CTHREAD_resume( thread, rvalue, 0 );
   }
   return 0;
}
int CTHREAD_libinit ( )

library initialisation

Definition at line 22 of file cthread.c.

{
#ifdef NO_TLS
  pthread_key_create( &tls_key, 0);
#endif
  return 0;
}
int CTHREAD_resume ( CTHREAD thread,
VALUES **  rvalue,
const char *  format,
  ... 
)

resume a suspended thread

Resumes a suspended thread, the function will return when the resumed thread calls CTHREAD_yield or when it exits.

Passing parameters to the thread; a variable length set of arguments can be sent to the thread, and the thread will be able to read them. Parameter format is the format specifier, subsequent variable length argument values are then sent to the thread.

Retrieving parameters from the thread: We can access values returned by the thread, once CTHREAD_resume returns. The values passed by the thread are stored in rvalue parameter.

Precondition: the thread is in CTHREAD_STATE_SUSPENDED state Postcondition: the thread is in CTHREAD_STATE_RUNNING state

Definition at line 201 of file cthread.c.

{
  if (thread->state != CTHREAD_STATE_SUSPENDED) {
    return -1;
  }

  getcontext( &thread->context_caller );
  if (thread->state == CTHREAD_STATE_RUNNING) {
    // got here when the running thread called yield for the first time, or exited without calling yield.
    thread->state = CTHREAD_STATE_SUSPENDED;
    if (rvalue) {
     if (thread->thread_to_caller_value_set) {
       *rvalue = &thread->thread_to_caller_value; 
     } else {
       *rvalue = 0;
     }
    }
    return 0;
  }
  if (thread->state == CTHREAD_STATE_EXIT) {
    // got here from thread that has exited.
    if (rvalue) {
     if (thread->thread_to_caller_value_set) {
       *rvalue = &thread->thread_to_caller_value; 
     } else {
       *rvalue = 0;
     }
    }
    return 0;
  }

  thread->caller_to_thread_value_set = 0;
  if (format) {
    va_list vlist;
    
    va_start( vlist, format );
    
    if (VALUES_printv( &thread->caller_to_thread_value, format, vlist ) ) {
      return -1;
    }
    thread->caller_to_thread_value_set = 1;
  } 

  SET_TLS( thread );
  
  return setcontext( &thread->context_coroutine );
}
int CTHREAD_set_return_value ( const char *  format,
  ... 
)

a running thread sets it return values

Definition at line 316 of file cthread.c.

{
  CTHREAD *thread;
  va_list vlist;
  
  thread = GET_TLS();
  if (!thread) {
   return (uint32_t) -1;
  }

  va_start( vlist, format );
  
  thread->thread_to_caller_value_set = 0;
  if (VALUES_printv( &thread->thread_to_caller_value, format, vlist )) {
    thread->thread_to_caller_value_set = 0;
    return -1;
  }

  thread->thread_to_caller_value_set = 1;
  return 0;
}
int CTHREAD_start ( CTHREAD thread,
VALUES **  rvalue,
const char *  format,
  ... 
)

start a new thread - the thread procedure is invoked.

The initialised thread is started; The function returns if the running thread either exited or entered suspended state (i.e. the thread called CTHREAD_yield)

Values passed from the thread to the caller are stored in rvalue parameter. If this parameter is 0 then the return values are ignored.

Values passed from the caller to the thread -

Precondition: the thread is in either CTHREAD_STATE_INIT or CTHREAD_STATE_EXIT state. Postcondition: the thread is in CTHREAD_STATE_RUNNING state.

Definition at line 139 of file cthread.c.

{
  if (thread->state != CTHREAD_STATE_INIT) {
    return -1;
  }
  getcontext( &thread->context_caller );
  if (thread->state == CTHREAD_STATE_RUNNING) {
    // got here when the running thread called yield for the first time, or exited without calling yield.
    thread->state = CTHREAD_STATE_SUSPENDED;
    if (rvalue) {
     if (thread->thread_to_caller_value_set) {
      *rvalue = &thread->thread_to_caller_value; 
     } else {
       *rvalue = 0;
     }
    }
    return 0;
  }
  if (thread->state == CTHREAD_STATE_EXIT) {
    // got here from thread that has exited.
    if (rvalue) {
     if (thread->thread_to_caller_value_set) {
       *rvalue = &thread->thread_to_caller_value; 
     } else {
       *rvalue = 0;
     }
    }
    return 0;
  }

  thread->caller_to_thread_value_set = 0;
  if (format) {
    va_list vlist;
    
    va_start( vlist, format );
    
    if (VALUES_printv( &thread->caller_to_thread_value, format, vlist ) ) {
      return -1;
    }

    thread->caller_to_thread_value_set = 1;
  } 
  

  return do_start( thread );
}
M_INLINE CTHREAD_STATE CTHREAD_state ( CTHREAD thread)

get thread state

Definition at line 147 of file cthread.h.

{
  return thread->state;
}
int CTHREAD_yield ( VALUES **  rvalue,
const char *  format,
  ... 
)

a running thread temporarily suspends execution

Precondition: the thread is in CTHREAD_STATE_RUNNING state Postcondition: the thread is in CTHREAD_STATE_SUSPENDED state

Definition at line 250 of file cthread.c.

{
  CTHREAD *thread;
  
  thread = GET_TLS();

  if (!thread) {
    return -1;
  }

  if (thread->state != CTHREAD_STATE_RUNNING) {
      return -1;
  }

  getcontext( &thread->context_coroutine );
  if (thread->state != CTHREAD_STATE_RUNNING) {
    // got here when this thread was resumed.
    thread->state = CTHREAD_STATE_RUNNING;
    if (rvalue) {
     if (thread->caller_to_thread_value_set) {
       *rvalue = &thread->caller_to_thread_value; 
     } else {
       *rvalue = 0;
     }
    }
    return 0;
  }
 
  thread->thread_to_caller_value_set = 0;
  if (format) {
    va_list vlist;
    
    va_start( vlist, format );
    
    if (VALUES_printv( &thread->thread_to_caller_value, format, vlist ) ) {
      return -1;
    }
    thread->thread_to_caller_value_set = 1;
  }

  SET_TLS( thread->prev_thread );
 
  return setcontext( &thread->context_caller );
}