The main event loop manages all the available sources of events. These events can come from any number of different types of sources such as fds (plain files, pipes or sockets) and timeouts. New types of event sources can also be added using g_source_attach().

To allow multiple independent sets of sources to be handled in different threads, each source is associated with a GMainContext. A GMainContext can only be running in a single thread, but sources can be added to it and removed from it from other threads.

Each event source is assigned a priority. Events from high priority sources are always processed before events from lower priority sources.

Idle functions can also be added, and assigned a priority. These will be run whenever no events with a higher priority are ready to be processed.

The GMainLoop data type represents a main event loop. A GMainLoop is created with g_main_loop_new(). After adding the initial event sources, g_main_loop_run() is called. Finally, the processing of an event from one of the sources leads to a call to g_main_loop_quit() to exit the main loop, and g_main_loop_run() returns.

Note that event sources are associated with a particular GMainContext, and will be checked and dispatched for all main loops associated with that GMainContext.

Event source (GSource), event, GMainContext, GMainLoop, idle function:

  • Thread 1 -> n GMainLoop;
  • GMainLoop n -> 1 GMainContext;
  • GMainContext 1 -> n {GSource1, GSource2, GSource3……}.

每个 GmainLoop 都包含一个 GMainContext 成员,而这个 GMainContext 成员可以装各种各样的 GSourceGSource 则是具体的各种 Event 处理逻辑了。这里,可以把 GMainContext 理解为 GSource 的容器。(不过它的用处不只是装 GSource)。

创建 GMainLoop 使用函数 g_main_loop_new(), 它的第一个参数就是需要关联的 GMainContext,如果这个值为空,程序会分配一个默认的 GMainContextGMainLoop

一个 main loop 对象只能被一个线程使用,但一个线程可以有多个 main loop 对象。

GMainContext 可以在多个 GMainLoop 间共享,但要求这些 GMainLoop 都在同一个线程中运行。

GLib – 2.0: The Main Event Loop

值得一读:Main Contexts - GNOME Developer Documentation,下面的部分内容就是从这篇文章当中摘抄出来的。

GMainContext

GMainContext is just a poll() loop.

Each GMainContext() is ‘acquired’ by a thread for each iteration it’s put through. Other threads cannot iterate a GMainContext without acquiring it, which guarantees that a GSource and its FDs will only be polled by one thread at once.

A GMainContext can be swapped between threads across iterations, but this is expensive.

GSource

Each GSource is attached to at most one GMainContext.

GMainLoop

GMainLoop is essentially the following few lines of code:

loop->is_running = TRUE;
while (loop->is_running)
  {
    if (quit_condition)
      loop->is_running = FALSE;
    g_main_context_iteration(context, TRUE);
  }

Hence, GMainLoop is a convenient, thread-safe way of running a GMainContext to process events until a desired exit condition is met, at which point g_main_loop_quit() should be called.

Difference between GMainLoop and GMainContext

It is important not to confuse main contexts with main loops. Main contexts do the bulk of the work: preparing source lists, waiting for events, and dispatching callbacks. A main loop simply iterates a context.

g_source_add_poll()

添加文件描述符到 GSource 这个事件源中,因为 fd 表示的是一类事件嘛。

void g_source_add_poll(GSource *source, GPollFD *fd);

g_source_set_callback()

Default Contexts

Two levels of default context: the thread-default, and the global-default g_main_context_default().

By calling g_main_context_push_thread_default() before starting an I/O operation, the thread-default context is set and the I/O operation can add its sources to that context.