Galaxy Communicator Documentation:

The Timed Task Loop

License / Documentation home / Help and feedback

In order to control the execution of events, MIT provides a timing loop which it uses in both the Hub and servers. We document this loop here. As of 3.0, the functions Gal_AddTimedTask, etc. are no longer recommended; use Gal_AddTask, etc. instead.


What the timed task loop does

In the MIT main loop, the timed task loop schedules and executes server and Hub polling events. When the timed task loop fires a poll, it may be scheduled to fire again if appropriate conditions are met. The loop can be used to schedule other events at the programmer's discretion. The loop runs until Gal_TimedTasksLoopExit is called. We describe the structure of the MIT main loop here. In some cases, the programmer might choose to replace the MIT main loop, and schedule the polls and other events by other means; the tools to accomplish this are described here.

It is now possible to use a version of the timed task API to produce threads instead.


Setting up timed tasks

Note that by default, all these functions begin the tasks when they're created, and this may not be what you want. If it isn't, pass -1 as the value of num_millisecs and use Gal_StartTask.

Gal_TaskPkg *Gal_AddTask(void (*task)(Gal_TaskPkg *), void *refcon, long num_millisecs, int read_blocking_available, void (*cleanup_fn)(void *))
Use this function to add a timed task. When threads are enabled, this function will create a thread and associate a task with it; when threads are not being used, this function adds a task to the list of timed tasks to fire. The task is a function which takes a Gal_TaskPkg * structure as an argument (in fact, the one returned by Gal_AddTask), and returns void. The num_millisecs is how long in milliseconds after the task is added (or, if the loop hasn't been started, how long after the loop is started) the task should be executed. The read_blocking_available argument should indicate whether the task has the capacity to do a blocking read in the threaded case; if read blocking is available, num_millisecs is ignored in the threaded case.

In the normal case, this task will be started as soon as it's created. In the threaded case, this is exceptionally important, because if you need to store the task away anywhere, or do anything with it before it starts, you must create the task and start it later. You can do this by passing -1 as the value of num_millisecs and using Gal_StartTask.

The Gal_TaskPkg * structure argument to the task provides five things:

Finally, the cleanup_fn is run on refcon if refcon is present, whenever the task is not refired or when refcon or cleanup_fn is replaced during refiring (if refiring passes the identical arguments, the function is not run). Immediately before the task is executed, it is removed from the list of timed tasks (or, in the threaded case, it is marked for destruction); so typically the last thing the task will do is call Gal_ReAddTask if it intends the task to be refired.

Gal_TaskPkg *Gal_AddTaskWithSocketIO(void (*task)(Gal_TaskPkg *), void *refcon, long num_millisecs, int read_blocking_available, GAL_SOCKET *read_socket, GAL_SOCKET *write_socket, void (*cleanup_fn)(void *))
Gal_TaskPkg *Gal_AddTaskWithFileIO(void (*task)(Gal_TaskPkg *), void *refcon, long num_millisecs, int read_blocking_available, FILE *read_file, FILE *write_file, void (*cleanup_fn)(void *))
Adds the task with sensitivity to I/O on sockets and file descriptors, respectively. Otherwise, identical to Gal_AddTask(). Note that file I/O polling is not available on Win32.

typedef int (*Gal_TaskConditionFn)(void *caller_data);
Gal_TaskPkg *Gal_AddTaskExtended(void (*task)(Gal_TaskPkg *), void *caller_data, long num_millisecs, int read_blocking_available, GAL_SOCKET *read_socket, GAL_SOCKET *write_socket, GAL_SOCKET *err_socket, FILE *read_file, FILE *write_file, FILE*err_file, Gal_TaskConditionFn condition, void (*cleanup_fn)(void *))
Adds the task with sensitivity to the entire range of elements, including I/O on sockets and file descriptors, the additional possibility of error streams and task conditions. Task conditions are passed the caller_data, and if the task condition returns nonzero, the task will be executed in advance of its normal time expiration. Otherwise, identical to Gal_AddTask(). Note that file I/O polling is not available on Win32.

int Gal_StartTask(Gal_TaskPkg *pkg, int num_millisecs)
Starts the task. If the polling interval in the task pkg is -1, and num_millisecs is > -1, take the appropriate action to start the task (i.e., insert it into the task array in non-threaded mode, or create and start a thread for it in threaded mode). This function can be used to start a task after it's been created.

void Gal_ReAddTask(Gal_TaskPkg *p, void *refcon, long num_millisecs, int read_blocking_available, void (*cleanup_fn)(void *))
This function is identical to Gal_AddTask(), except it is to be used when tasks are supposed to be refired, as described in Gal_AddTask(). In the non-threaded case, it just adds the Gal_TaskPkg * structure back to the list of timed tasks; in the threaded case, the thread is continued. This function is intended to be called from inside the task which p was passed to. If the task is not refired, the task package is freed; the programmer is responsible for managing the memory associated with refcon.

void Gal_ReAddTaskWithSocketIO(Gal_TaskPkg *p, void *refcon, long num_millisecs, int read_blocking_available, GAL_SOCKET *read_socket, GAL_SOCKET *write_socket, void (*cleanup_fn)(void *))
void Gal_ReAddTaskWithFileIO(Gal_TaskPkg *p, void *refcon, long num_millisecs, int read_blocking_available, FILE *read_file, FILE *write_file, void (*cleanup_fn)(void *))
Re-adds the task with sensitivity to I/O on sockets and file descriptors, respectively. Otherwise, identical to Gal_ReAddTask(). Note that file I/O polling is not available on Win32.

void Gal_ReAddTaskExtended(Gal_TaskPkg *p, void *caller_data, long num_millisecs, int read_blocking_available, GAL_SOCKET *read_socket, GAL_SOCKET *write_socket, GAL_SOCKET *err_socket, FILE *read_file, FILE *write_file, FILE *err_file, Gal_TaskConditionFn condition, void (*cleanup_fn)(void *))
Re-adds the task with sensitivity sensitivity to the entire range of elements, including I/O on sockets and file descriptors, the additional possibility of error streams and task conditions. Otherwise, identical to Gal_ReAddTask(). Note that file I/O polling is not available on Win32.

int Gal_TaskPkgRunReasons(Gal_TaskPkg *pkg)
Returns the logical OR of the reasons the package was run. Possible reasons are:
 
value description
GAL_SOCKET_READABLE The task was fired because a read socket had data
GAL_SOCKET_WRITABLE The task was fired because a write socket had data
GAL_SOCKET_ERR_READABLE The task was fired because an error socket had data
GAL_FILE_READABLE The task was fired because a read file pointer had data
GAL_FILE_WRITABLE The task was fired because a write file pointer had data
GAL_FILE_ERR_READABLE The task was fired because an error file pointer had data
GAL_TIMER_EXPIRED The task was fired because it had slept for the required number of milliseconds
GAL_CONDITION_SATISFIED The task was fired because its task condition function returned nonzero
GAL_THREAD_READABLE The task was fired because a blocking read had data in the threaded case

So you can check, for instance, to see if your task fired because of a readable file as follows:

if (Gal_TaskPkgRunReasons(pkg) & GAL_FILE_READABLE) {
  ...
}


voidGal_RemoveTask(Gal_TaskPkg *task_id)
Removes the task task_id.

int Gal_TaskPkgBlocking(Gal_TaskPkg *pkg)
If 1, the task should set its read file descriptors to blocking mode; if 0, non-blocking.

void *Gal_TaskPkgData(Gal_TaskPkg *pkg)
Returns the data associated with the package. This is the same as the value of the refcon argument to Gal_AddTask and Gal_ReAddTask.


Managing timed tasks

These functions manage the invocation and operation of the timed task loop.

void Gal_TimedTasksLoop(void)
This function initiates the timed task loop. It loops until Gal_TimedTasksLoopExit is called.

void Gal_TimedTasksLoopExit(void)
This function halts the timed tasks loop. All tasks in the queue are completed, but no other tasks are queued.

typedef void (*Gal_IdleFunction)(void *client_data);
int Gal_AddIdleFunction(Gal_IdleFunction func, void *client_data)
Adds a function to the list of functions which the timed task loop will execute when there are no tasks to fire. The client_data argument is passed to func when it's called. Unlike tasks, which are removed after they're fired, idle functions remain installed until they're removed with Gal_RemoveIdleFunction().

void Gal_RemoveIdleFunction(Gal_IdleFunction func)
Removes func from the list of functions which the timed task loop executes when there are no tasks to fire.

void Gal_RunIdleFunctions()
Runs the idle functions. Called by the timed task loop, but can also be called explicitly.


Internal functions

int Gal_TimedTasksLoopHandler(struct timeval *tv)
This is the internal function which determines when to execute the timed tasks.


Threads

The Galaxy Communicator library is thread-safe (we believe) on a number of platforms (see the thread notes). One way we've tested this is by running multiple-connection servers in threaded mode. We have made this experimental facility available to the community via the -thread argument described in the generic server arguments. This argument is available when threads are enabled. The following functions can be used to exploit this functionality.

If you want to use the timed task loop in a threaded context, it should work (we haven't tested it extensively). In particular, you can only run the timed task loop from one thread at a time, and you can only shut down the loop from that thread. However, be warned that if you use the -thread argument, the timed task loop will not be started automatically; you will have to start it yourself.

void Gal_EnableTimedTaskThreads()
This function controls whether tasks added via Gal_AddTask() and Gal_ReAddTask() are handled via timed tasks or via threads. Once timed task threads are enabled, they can only disabled by Gal_EndTasks(). Gal_EnableTimedTaskThreads() is the function which is invoked when you provide the -thread argument to servers when threads are enabled. If the user is writing his/her own main() and wants to avoid the timed task loop for server, connection and broker handling but would like threads to handle these facilities, the user can enable this facility with this function.

int Gal_TimedTaskThreadsEnabled()
Returns 1 if timed tasks threads have enabled via Gal_EnableTimedTaskThreads(), 0 otherwise. The user should never need to call this function.

void Gal_TimedTaskLoopThreadWaiter(void)
Waits until all the tasks are completed. This function is the threaded equivalent of Gal_TimedTasksLoop. The main server loop uses it in the threaded case to delay the actions of the main thread so that GalSS_RunServer only returns when the server is finished.

void Gal_EndTasks(int immediate)
This is a more general version of Gal_TimedTasksLoopExit. It causes one of two types of exits: immediate and deferred. An immediate exit will cancel tasks at the most immediate possible exit point, while a deferred exit will cancel tasks which are already "scheduled" to happen. If immediate is non-zero,  this call causes an immediate exit, otherwise a deferred exit. This function halts both threads (if running) and the timed task loop (if running).

void Gal_MaybeEndTask(int immediate, int deferred)
This function introduces an exit point into a long-running task. For instance, if your (threaded) task is looping forever and doing blocking reads, you might choose to call Gal_MaybeEndTask() to check to see if another thread has scheduled an exit via Gal_EndTasks. If it finds an immediate scheduled exit and immediate is nonzero, or if it finds a deferred scheduled exit and deferred is nonzero, the thread will terminate.
 


For backward compatibility

These functions represent an older, simpler way of invoking timed tasks. They still work, but they do not allow you to take advantage of many of the newer features. We have no plans to deprecate them, but we strongly discourage their use.

int Gal_AddTimedTask(void *task, void *refcon, long num_millisecs)
Use this function to add a timed task. The task is a function which takes refcon as its argument and returns void. The num_millisecs is how long in milliseconds after the task is added (or, if the loop hasn't been started, how long after the loop is started) the task should be executed. Once the task is executed, it is removed from the list of timed tasks; so typically the last thing the task will do is call Gal_AddTimedTask again. This function is implemented in terms of the internal function Gal_AddTask.

int Gal_AddTimedTaskWithFileIO(void *task, void *refcon, long num_millisecs, FILE *read_file, FILE *write_file)
Use this function to add a timed task which is sensitive to input and output on file descriptors. If any of the elements in either of the file descriptor sets is ready for reading and writing before num_milliseconds has elapsed, the task will nonetheless be executed. Note that file I/O polling is not available on Win32.

int Gal_AddTimedTaskWithSocketIO(void *task, void *refcon, long num_millisecs, GAL_SOCKET *read_socket, GAL_SOCKET *write_socket)
Use this function to add a timed task which is sensitive to input and output on sockets. If any of the elements in either of the file descriptor sets is ready for reading and writing before num_milliseconds has elapsed, the task will nonetheless be executed.

int Gal_RemoveTimedTask(void *task, void *refcon)
Removes the given timed task. Note that tasks are distinguished by task and refcon, so it's not a good idea to define multiple timed tasks with the same task and refcon.
 


License / Documentation home / Help and feedback
Last updated July 9, 2002