Glibc Main Event Loop
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
成员可以装各种各样的 GSource
,GSource
则是具体的各种 Event 处理逻辑了。这里,可以把 GMainContext
理解为 GSource
的容器。(不过它的用处不只是装 GSource
)。
创建 GMainLoop
使用函数 g_main_loop_new()
, 它的第一个参数就是需要关联的 GMainContext
,如果这个值为空,程序会分配一个默认的 GMainContext
给 GMainLoop
。
一个 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.