Modules | Files | Variables

Syncronization and Threading

Syncronization and threading interface. More...

Modules

 Example and test code for syncronization and threads
 

Example programs demonstrate the su syncronization and threading primitives.


Files

file  su_wait.h
 

Syncronization and threading interface.


Variables

int su_root_size_hint
 Contains hint of number of sockets supported by su_root_t.

Detailed Description

Syncronization and threading interface.

The Sofia utility library provides simple OS-independent synchronization interface. The synchronization interface contains primitives for managing events, messages, timers and threads.

Clone Objects

The process may be divided into many tasks via cloning.

Several tasks may run in context of one thread, or each task may be run by its own thread. However, only a single thread can execute code within a task. There can be a 1-to-N mapping from thread to tasks. Thus, software using tasks can be executed by multiple threads in a multithreaded environment and by a single thread in a singlethreaded environment.

The clones are useful for handling tasks that can be executed by a separate threads, but which do not block excessively. When threads are not available or they are not needed, clones can also be run in a single-threaded mode. Running in single-threaded mode is especially useful while debugging.

A clone task is created with function su_clone_start(). Each clone has its own root object (su_root_t), which holds a context pointer (su_root_magic_t *). The context object can be different from that of parent task.

When a clone is started, the clone initialization function is called. The initialization function should do whatever initialization there is to be performed, register I/O events and timers, and then return. If the initialization is successful, the clone task reverts to run the event loop and invoking the event callbacks until its parent stops it by calling su_clone_wait() which invokes the deinit function. The clone task is destroyed when the deinit function returns.

The public API consists of following functions:

Note:
There is only one event loop for each thread which can be shared by multiple clone tasks. Therefore, the clone tasks can not explicitly run or step the event loop, but they are limited to event callbacks. A clone task may not call su_root_break(), su_root_run() or su_root_step().

Tasks and root objects

A task is the basic execution unit for the Sofia event-driven programming model.

According to the model, the program can ask that the event loop invokes a callback function when a certain event occurs. Such events include I/O activity, timers or a message from other task. The event loop is run with function su_root_run() or su_root_step().

Root object gives access to the task control. The root object represents the task to the code running within task. Through the root, the task code can access its context object (magic) and thread-synchronization features like wait objects, timers, and messages.

When a message is sent between tasks, a task reference su_task_r is used to reprent the task address. Reference counting is used to make sure that the task references stay valid.

The public API contains following functions:

New tasks can be created via su_clone_start() function.

Registering Wait Objects

When application expects I/O events, it can create a wait object and register it, a callback function and a context pointer to the su_root_t object using the su_root_register() function.

Whenever the wait object receives an event, the registered callback function is invoked.

When successful, the su_root_register() returns an small non-negative integer representing the registration. The registration can be manipulated with su_root_eventmask() function, for instance, when sending through a socket block, the application can add SU_WAIT_OUT event to the mask.

The registration can be removed using su_root_deregister() function.

Timer Objects

Timers are used to schedule some task to be executed at given time or after a default interval.

The default interval is specified when the timer is created. We call timer activation "setting the timer", and deactivation "resetting the timer" (as in SDL). When the given time has arrived or the default interval has elapsed, the timer expires and it is ready for execution.

The functions used to create, destroy, activate, and manage timers are as follows:

Note:
Timers use poll() to wake up waiting thread. On Linux, the timer granularity is determined by HZ kernel parameter, which decided when the kernel was compiled. With kernel 2.4 the default granularity is 10 milliseconds, and minimum duration of a timer is approximately 20 milliseconds. Naturally, using RTC would give better timing results, but RTC usage above 64 Hz is privileged operation.
On Windows, the granularity is determined by the real-time clock timer. By default, it uses the 18.78 Hz granularity. That timer can be adjusted up to 1000 Hz using Windows multimedia library.

Using Timers

A timer is created by calling su_timer_create():

   timer = su_timer_create(su_root_task(root), 200);

The default duration is given in milliseconds.

Usually, timer wakeup function should be called at regular intervals. In such case, the timer is activated using function su_timer_set_for_ever(). When the timer is activated it is given the wakeup function and pointer to context data:

   su_timer_set_for_ever(timer, timer_wakeup, args);

When the interval has passed, the root event loop calls the wakeup function:

   timer_wakeup(root, timer, args);

If the number of calls to callback function is important, use su_timer_run() instead. The run timer tries to compensate for missed time and invokes the callback function several times if needed. (Because the real-time clock can be adjusted or the program suspended, e.g., while debugged, the callback function can be called thousends of times in a row.) Note that while the timer tries to compensate for delays occurred before and during the callback, it cannot be used as an exact source of timing information.

Timer ceases running when su_timer_reset() is called.

Alternatively, the timer can be set for one-time event invocation. When the timer is set, it is given the wakeup function and pointer to context data. The actual duration can also be specified using su_timer_set_at().

 su_timer_set(timer, timer_wakeup, args);

When the timer expires, the root event loop calls the wakeup function:

   timer_wakeup(root, timer, args);

If the timed event is not needed anymore, the timer can be reset:

   su_timer_reset(timer);

If the timer is expected to be called at regular intervals, it is possible to set ro run continously with su_timer_run(). While such a continously running timer is active it must not be set using su_timer_set() or su_timer_set_at().

When the timer is not needed anymore, the timer object itself should be destroyed:

   su_timer_destroy(timer);

Wait objects

Wait objects are used to signal I/O events to the process.

The events are as follows:

It is possible to combine several events with |, binary or operator.

The wait objects can be managed with functions as follows:

Note:
In Unix, the wait object is struct poll. The structure contains a file descriptor, a mask describing expected events, and a mask containing the occurred events after calling su_wait(), ie. poll().
In Windows, the wait object is a HANDLE (a descriptor of a Windows kernel entity).

Variable Documentation

Contains hint of number of sockets supported by su_root_t.

Hint for number of registered fds in su_root.

 All Data Structures Files Functions Variables Typedefs Enumerator Defines

Sofia-SIP 1.12.11 - Copyright (C) 2006 Nokia Corporation. All rights reserved. Licensed under the terms of the GNU Lesser General Public License.