Main Loop (Generic Event Management)

Main Loop (Generic Event Management) — Convenient way to be notified when certain types of event occur.

Synopsis




            GskMainLoopChange;
            GskMainLoopEvent;
            GskMainLoopWaitInfo;
            GskSource;
            GskMainLoopContextList;
enum        GskMainLoopEventType;
            GskMainLoopClass;
            GskMainLoop;
void        (*GskMainLoopWaitPidFunc)       (GskMainLoopWaitInfo *info,
                                             gpointer user_data);
gboolean    (*GskMainLoopIdleFunc)          (gpointer user_data);
gboolean    (*GskMainLoopSignalFunc)        (int sig_no,
                                             gpointer user_data);
gboolean    (*GskMainLoopTimeoutFunc)       (gpointer user_data);
gboolean    (*GskMainLoopIOFunc)            (int fd,
                                             GIOCondition condition,
                                             gpointer user_data);
enum        GskMainLoopCreateFlags;
GskMainLoop* gsk_main_loop_new              (GskMainLoopCreateFlags create_flags);
GskMainLoop* gsk_main_loop_default          (void);
guint       gsk_main_loop_run               (GskMainLoop *main_loop,
                                             gint timeout,
                                             guint *t_waited_out);
GskSource*  gsk_main_loop_add_idle          (GskMainLoop *main_loop,
                                             GskMainLoopIdleFunc source_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);
GskSource*  gsk_main_loop_add_signal        (GskMainLoop *main_loop,
                                             int signal_number,
                                             GskMainLoopSignalFunc signal_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);
GskSource*  gsk_main_loop_add_waitpid       (GskMainLoop *main_loop,
                                             int process_id,
                                             GskMainLoopWaitPidFunc waitpid_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);
GskSource*  gsk_main_loop_add_io            (GskMainLoop *main_loop,
                                             int fd,
                                             guint events,
                                             GskMainLoopIOFunc io_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);
void        gsk_source_adjust_io            (GskSource *source,
                                             guint events);
void        gsk_source_remove_io_events     (GskSource *source,
                                             guint events);
void        gsk_source_add_io_events        (GskSource *source,
                                             guint events);
GskSource*  gsk_main_loop_add_timer         (GskMainLoop *main_loop,
                                             GskMainLoopTimeoutFunc timer_func,
                                             gpointer timer_data,
                                             GDestroyNotify timer_destroy,
                                             gint64 millis_expire,
                                             gint64 milli_period);
GskSource*  gsk_main_loop_add_timer_absolute
                                            (GskMainLoop *main_loop,
                                             GskMainLoopTimeoutFunc timer_func,
                                             gpointer timer_data,
                                             GDestroyNotify timer_destroy,
                                             int unixtime,
                                             int unixtime_micro);
void        gsk_source_adjust_timer         (GskSource *timer_source,
                                             gint64 millis_expire,
                                             gint64 milli_period);
void        gsk_source_remove               (GskSource *source);
void        gsk_main_loop_add_context       (GskMainLoop *main_loop,
                                             GMainContext *context);
void        gsk_main_loop_quit              (GskMainLoop *main_loop);
gboolean    gsk_main_loop_should_continue   (GskMainLoop *main_loop);
GskMainLoop* gsk_source_peek_main_loop      (GskSource *source);
void        gsk_main_loop_destroy_all_sources
                                            (GskMainLoop *main_loop);
gboolean    gsk_main_loop_do_waitpid        (int pid,
                                             GskMainLoopWaitInfo *wait_info);

Object Hierarchy


  GObject
   +----GskMainLoop
         +----GskMainLoopPollBase
         +----GskMainLoopKqueue

Description

A main loop is an object which can trap events and call user functions when those events happen.

The events that our main loop handles are fixed: file-descriptors being readied, signals being delivered, timeouts, idle functions and child-process termination are the currently known events.

GskMainLoop itself is abstract, you must use a system-specific derived class to do polling. Use gsk_main_loop_default() to get a main loop which is appropriate to your system.

Details

GskMainLoopChange

typedef struct {
  GskMainLoopEventType type;
  union
  {
    struct {
      guint number;
      gboolean add;
    } signal;
    struct {
      guint fd;
      GIOCondition old_events;
      GIOCondition events;
    } io;
    struct {
      gint pid;
      gboolean add;
      gboolean did_exit;
    } process;
  } data;
} GskMainLoopChange;

This structure is passed to the system-dependent polling mechanism to indicate a change in events we want notification of.

GskMainLoopEventType type; what type of change is to occur.

GskMainLoopEvent

typedef struct {
  GskMainLoopEventType type;
  union
  {
    guint signal;
    struct {
      guint fd;
      GIOCondition events;
    } io;
    GskMainLoopWaitInfo process_wait_info;
  } data;
} GskMainLoopEvent;

An event passed back from the system-dependent polling mechanism.

GskMainLoopEventType type; what type of event occurred.
GskMainLoopWaitInfo process_wait_info;

GskMainLoopWaitInfo

typedef struct {
  int               pid; 
  gboolean          exited;         /* exit(2) or killed by signal? */
  union {
    int             signal;         /* !exited */
    int             exit_status;    /*  exited */
  } d;           
  gboolean          dumped_core;
} GskMainLoopWaitInfo;

Information about a process's termination.

int pid; the process-id which terminated.
gboolean exited; whether the process exited, versus being killed by a signal, including crashes, which exit with SIGSEGV, SIGABRT, etc.
gboolean dumped_core; whether the process dumped core.

GskSource

typedef struct _GskSource GskSource;

An opaque object representing a trap of an event.


GskMainLoopContextList

typedef struct _GskMainLoopContextList GskMainLoopContextList;

Private.


enum GskMainLoopEventType

typedef enum
{
  GSK_MAIN_LOOP_EVENT_IO,
  GSK_MAIN_LOOP_EVENT_SIGNAL,
  GSK_MAIN_LOOP_EVENT_PROCESS
} GskMainLoopEventType;

Types of event that can be dealt with by this main-loop.

GSK_MAIN_LOOP_EVENT_IO An input/output event. This is really just a file-descriptor event.
GSK_MAIN_LOOP_EVENT_SIGNAL
GSK_MAIN_LOOP_EVENT_PROCESS

GskMainLoopClass

typedef struct {
  GObjectClass object_class;
  gboolean (*setup)  (GskMainLoop       *main_loop);
  void     (*change) (GskMainLoop       *main_loop,
                      GskMainLoopChange *change);
  guint    (*poll)   (GskMainLoop       *main_loop,
                      guint              max_events_out,
                      GskMainLoopEvent  *events,
                      gint               timeout);
} GskMainLoopClass;

The virtual function which must be implemented for each type of main-loop.

GObjectClass object_class; the base class from which the main-loop is derived.
setup () function to call to initialize the main-loop.
change () function invoked to indicate that a change in events we are interested in has occurred.
poll () function invoked to check which events have occurred.

GskMainLoop

typedef struct {
  gint		 exit_status;

  GskMainLoopEvent *event_array_cache;
  unsigned       max_events;

  /* a list of GMainContext's */
  GskMainLoopContextList *first_context;
  GskMainLoopContextList *last_context;
} GskMainLoop;

A main-loop. This holds information about all its sources.

gint exit_status; Stored exit status which the user may return from main().
GskMainLoopEvent *event_array_cache;
GskMainLoopContextList *first_context; first GMainContext in the list.
GskMainLoopContextList *last_context; last GMainContext in the list.

GskMainLoopWaitPidFunc ()

void        (*GskMainLoopWaitPidFunc)       (GskMainLoopWaitInfo *info,
                                             gpointer user_data);

A function which will be invoked when a process terminates.

info : information about the process and why it terminated.
user_data : data registered to gsk_main_loop_add_waitpid().

GskMainLoopIdleFunc ()

gboolean    (*GskMainLoopIdleFunc)          (gpointer user_data);

Function to be called repetitively (that is, with no blocking or waiting; however, other events will continue to be processed). It will stop when gsk_main_loop_remove() is run on its source, or if it returns FALSE.

user_data : data registered to gsk_main_loop_add_idle().
Returns : whether to continue running.

GskMainLoopSignalFunc ()

gboolean    (*GskMainLoopSignalFunc)        (int sig_no,
                                             gpointer user_data);

Function to be called whenever a UNIX signal of a particular number is raised.

It will be untrapped when gsk_main_loop_remove() is run on its source, or if it returns FALSE.

sig_no : the number of the signal that was raised.
user_data : data registered to gsk_main_loop_add_signal().
Returns : whether to continue trapping the unix signal.

GskMainLoopTimeoutFunc ()

gboolean    (*GskMainLoopTimeoutFunc)       (gpointer user_data);

Function to invoke whenever a timeout expires.

It will be untrapped when gsk_main_loop_remove() is run on its source, or if it returns FALSE, or after it runs if it was registered as a one-shot (with milli_period==-1).

user_data : data registered to gsk_main_loop_add_timer() or gsk_main_loop_add_timer_absolute().
Returns : whether to keep running this timer. (Will be ignored if it is a one-shot timer or if the source has been destroyed)

GskMainLoopIOFunc ()

gboolean    (*GskMainLoopIOFunc)            (int fd,
                                             GIOCondition condition,
                                             gpointer user_data);

Function to run whenever some subset of a set of requested events are noticed.

fd : the file-descriptor that the events occured on.
condition : the events that triggered the callback.
user_data : data registered to gsk_main_loop_add_io().
Returns : whether to keep these source.

enum GskMainLoopCreateFlags

typedef enum
{
  GSK_MAIN_LOOP_NEEDS_THREADS = (1 << 0)
} GskMainLoopCreateFlags;

Indicate user requirements for the main-loop being constructed.

GSK_MAIN_LOOP_NEEDS_THREADS Some main loops (like GskMainLoopKqueue) don't cooperate with threads at all! This flag precludes choosing main-loops which are have this property. If you don't need threads, this flag does not hurt, except that kqueue() is a pretty good mechanism, so you probably want to get it right for a high-volume single-threaded server.

gsk_main_loop_new ()

GskMainLoop* gsk_main_loop_new              (GskMainLoopCreateFlags create_flags);

Make a new main loop.

create_flags : Stipulations on the nature of the main-loop.
Returns : a new main loop.

gsk_main_loop_default ()

GskMainLoop* gsk_main_loop_default          (void);

Get the main-loop which is associated with the current thread.

Returns : a pointer to the main-loop. This function does not increase the ref-count on the main-loop, so you do not need to call g_object_unref() on the return value.

gsk_main_loop_run ()

guint       gsk_main_loop_run               (GskMainLoop *main_loop,
                                             gint timeout,
                                             guint *t_waited_out);

Run the main loop once, for a specified number of milliseconds.

main_loop : the main loop to run.
timeout : the maximum number of milliseconds to run, or -1 for no maximum.
t_waited_out : the number of milliseconds out used, if non-NULL.
Returns : the number of sources processed.

gsk_main_loop_add_idle ()

GskSource*  gsk_main_loop_add_idle          (GskMainLoop *main_loop,
                                             GskMainLoopIdleFunc source_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);

This adds an idle function to the main loop. An idle function is a function which gets called every time the main loop is run. Furthermore, while there are idle functions, the main loop will never block.

One popular use of idle functions is to defer an operation, usually because either something is not in a good state to call immediately, or because there may be many requests that should be handled at one time.

main_loop : the loop to add the idle function to.
source_func : the function to call.
user_data : parameter to be passed to source_func
destroy : to be called when the source is destroyed.
Returns : a GskSource which can be removed (or ignored).

gsk_main_loop_add_signal ()

GskSource*  gsk_main_loop_add_signal        (GskMainLoop *main_loop,
                                             int signal_number,
                                             GskMainLoopSignalFunc signal_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);

Add a signal handler to the main loop.

Please note that unlike a normal unix signal handler (as provided by signal(2) or sigaction(2)), this handler will be run synchronously, so you can call non-reentrant methods.

Also, because unix signal delivery is unreliable, if the signal is raised a few times in rapid succession, you may miss some callbacks.

It is ok to connect multiple times to a single signal simulataneously.

main_loop : the loop to add the unix signal handler function to.
signal_number : the number of the signal handler, like SIGINT.
signal_func : the function to run synchronously when a unix signal is raised.
user_data : data to be passed to signal_func.
destroy : to be called when the source is destroyed.
Returns : a GskSource which can be removed (or ignored).

gsk_main_loop_add_waitpid ()

GskSource*  gsk_main_loop_add_waitpid       (GskMainLoop *main_loop,
                                             int process_id,
                                             GskMainLoopWaitPidFunc waitpid_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);

Add a handler to trap process termination.

Only one handler is allowed per child process.

The handler will be invoked synchronously.

main_loop : the loop to add the child-process termination handler function to.
process_id : the child's process-id to wait for.
waitpid_func : function to call when the process terminates in some way or another.
user_data : data to be passed to waitpid_func
destroy : to be called when the source is destroyed.
Returns : GskSource which can be removed (or ignored).

gsk_main_loop_add_io ()

GskSource*  gsk_main_loop_add_io            (GskMainLoop *main_loop,
                                             int fd,
                                             guint events,
                                             GskMainLoopIOFunc io_func,
                                             gpointer user_data,
                                             GDestroyNotify destroy);

Add a handler to input or output on a file-descriptor (a socket or a pipe, usually).

Only one handler trap is allowed per file-descriptor.

The handler will be re-invoked until the event subsides. For example, if you read only part of the data when a input event is raised, the io_func will be invoked again at every iteration of the main-loop until there is no data available.

main_loop : the loop to add the i/o watch to.
fd : the file-descriptor to watch for events.
events : initial I/O events to watch for.
io_func : a function to call when the currently requested events occur.
user_data : data to be passed to io_func
destroy : to be called when the source is destroyed.
Returns : a GskSource which can be removed or altered.

gsk_source_adjust_io ()

void        gsk_source_adjust_io            (GskSource *source,
                                             guint events);

This changes the types of events being watched by the main-loop.

Note: each new file-descriptor needs a new GskSource. You must reuse this GskSource for a new file-descriptor even if it happens to have the same numeric value as a file-descriptor you closed. (The reason why: GSK automatically coagulates multiple adjust_io calls. This is fine with all main-loops. However, kqueue(2) on BSD, and possibly others, automatically unregister all interest in an event if the file-descriptor closes. Hence, if the file-descriptor is re-opened and re-used with the same GskSource, GSK will not be able to determine that anything has changed, and will not issue a new GskMainLoopChange. This will break main-loops that are sensitive to exact which file-descriptor (not just the number) was registered.)

source : the I/O source which now wants to watch different events.
events : the new events to watch for the I/O source.

gsk_source_remove_io_events ()

void        gsk_source_remove_io_events     (GskSource *source,
                                             guint events);

Cause this source to stop being notified if any of the events in the events parameter are set.

source : the input/output source whose events-of-interest set should be reduced.
events : new events which should stop causing source to wake-up.

gsk_source_add_io_events ()

void        gsk_source_add_io_events        (GskSource *source,
                                             guint events);

Cause this source to be notified if any of the events in the events parameter are set, in addition to the events which already caused this source to be woken up.

source : the input/output source whose events-of-interest set should be expanded.
events : new events which should cause source to wake-up.

gsk_main_loop_add_timer ()

GskSource*  gsk_main_loop_add_timer         (GskMainLoop *main_loop,
                                             GskMainLoopTimeoutFunc timer_func,
                                             gpointer timer_data,
                                             GDestroyNotify timer_destroy,
                                             gint64 millis_expire,
                                             gint64 milli_period);

Add a timeout function to the main-loop. This is a function that will be called after a fixed amount of time passes, and then may be called at a regular interval thereafter.

main_loop : the main-loop which should keep track and run the timeout.
timer_func : function to call when the requested amount of time elapses.
timer_data : data to pass to timer_func.
timer_destroy : optional function to call to destroy the timer_data.
millis_expire : number of milliseconds to wait before running timer_func.
milli_period : period between subsequent invocation of the timeout. This may be -1 to indicate that the timeout is a one-shot.
Returns : GskSource which can be removed or altered.

gsk_main_loop_add_timer_absolute ()

GskSource*  gsk_main_loop_add_timer_absolute
                                            (GskMainLoop *main_loop,
                                             GskMainLoopTimeoutFunc timer_func,
                                             gpointer timer_data,
                                             GDestroyNotify timer_destroy,
                                             int unixtime,
                                             int unixtime_micro);

Add a timeout function to the main-loop. The timer_func will be called as soon as we detect that the specified time has passed.

The time to wait until is (unixtime + unixtime_micro * 10^{-6}) seconds after New Years, Jan 1, 1970 GMT.

main_loop : the main-loop which should keep track and run the timeout.
timer_func : function to call when the requested amount of time elapses.
timer_data : data to pass to timer_func.
timer_destroy : optional function to call to destroy the timer_data.
unixtime : number of seconds since Jan 1, 1970 GMT that will have passed when the timer should expire.
unixtime_micro : fractional part of unixtime, in microseconds.
Returns : a GskSource which can be removed or altered.

gsk_source_adjust_timer ()

void        gsk_source_adjust_timer         (GskSource *timer_source,
                                             gint64 millis_expire,
                                             gint64 milli_period);

Adjust the timeout and period for an already existing timer source. (You may only call this on timer sources.)

timer_source : the timeout source returned by gsk_main_loop_add_timer() or gsk_main_loop_add_timer_absolute().
millis_expire : the number of milliseconds from now that the timer should run.
milli_period : the period between subsequent runs of the timer, or -1 to indicate that the timer is a one-shot.

gsk_source_remove ()

void        gsk_source_remove               (GskSource *source);

Destroy a main loop's source.

If the source is currently running, it's destroy method will not be called until the source's callback returns. (This way, important data won't be deleted unexpectedly in the middle of the user's callback.)

source : the source to remove and destroy.

gsk_main_loop_add_context ()

void        gsk_main_loop_add_context       (GskMainLoop *main_loop,
                                             GMainContext *context);

Indicate that a particular GskMainLoop will take care of invoking the necessary methods of context.

main_loop : main-loop which will take responsibility for context.
context : a GMainContext that should be handled by gsk_main_loop_run().

gsk_main_loop_quit ()

void        gsk_main_loop_quit              (GskMainLoop *main_loop);

Set the main-loop flag that indicates that it should really stop running.

If you are executing a GskMainLoop using gsk_main_loop_run(), then you should probably check gsk_main_loop_should_continue() at every iteration to ensure that you should not have quit by now.

main_loop : the main-loop which is being asked to quit.

gsk_main_loop_should_continue ()

gboolean    gsk_main_loop_should_continue   (GskMainLoop *main_loop);

Query whether the main-loop should keep running or not.

main_loop : the main-loop to query.
Returns : whether to keep running this main-loop.

gsk_source_peek_main_loop ()

GskMainLoop* gsk_source_peek_main_loop      (GskSource *source);

Get the main-loop where the source was created.

source : the source to query.
Returns : the main-loop associated with the source.

gsk_main_loop_destroy_all_sources ()

void        gsk_main_loop_destroy_all_sources
                                            (GskMainLoop *main_loop);

main_loop :

gsk_main_loop_do_waitpid ()

gboolean    gsk_main_loop_do_waitpid        (int pid,
                                             GskMainLoopWaitInfo *wait_info);

Do a waitpid system call on the process and munge the data into wait_info for the caller to use.

pid : the process id to wait for.
wait_info : place to collect termination status of the process.
Returns : whether the waitpid succeeded.