GLib和GTK+應用的主事件循環管理着全部事件源。這些事件的來源有不少種好比文件描述符(文件、管道或套接字)或超時。新類型的事件源能夠經過g_source_attach()函數添加。
爲了讓多組獨立事件源可以在不一樣的線程中被處理,每一個事件源都會關聯一個GMainContext。一個線程只能運行一個GMainContext,可是在其餘線程中可以對事件源進行添加和刪除操做。
每一個事件源都被賦予了優先級。默認的優先級是G_PRIORITY_DEFAULT(0)。值越小優先級越高,優先級高的事件源優先處理。
Idle函數在沒有更高優先級的事件被處理的時候纔會執行。
GMainLoop數據類型表明了一個主事件循環。經過g_main_loop_new()來建立GMainLoop對象。在添加完初始事件源後執行g_main_loop_run(),主循環將持續不斷的檢查每一個事件源產生的新事件,而後分發它們,直處處理來自某個事件源的事件的時候觸發了g_main_loop_quit()調用退出主循環爲止。
GMainLoop實例可以被遞歸建立。在GTK+應用中常常使用這種方式來顯示模態對話框。注意若是一個事件源被添加到一個GMainContext,那麼它將被全部關聯這個GMainContext的主線程檢查和分發。
GTK+對這些函數作了些封裝,例如gtk_main、gtk_mian_quit和gtk_events_pending。數據結構
GMainLoop一個不經常使用的特性就是可以建立一個新的事件源類型,而後當作內置事件源的擴展來使用。一個新的事件源類型一般用來處理GDK事件。經過繼承GSource結構來建立一個新的事件源類型。繼承產生的新事件源類型表示GSource結構做爲新事件源類型的第一個元素而後其餘元素緊跟其後。使用g_source_new函數來建立新的事件源類型實例,函數的參數就是新的事件源類型大小。GSourceFuncs決定新的事件源類型的行爲。
新的事件源有兩種基本方式與GMainContext交互。它們GSourceFuncs中的準備函數可以設置睡眠事件,用來決定主循環下次檢測它們的時間。此外事件源也可使用g_source_add_poll()函數添加文件描述符到GMainContext進行檢測。函數
執行g_main_context_iteration()函數能夠完成GMainContext的單次迭代。在一些狀況下,咱們可能想獲取主循環更多的底層控制細節,咱們能夠調用g_main_context_iteration()裏的組件函數:g_main_context_prepare()、g_main_context_prepare ()、g_main_context_query()、g_main_context_check()和g_main_context_dispatch()。oop
在UNIX系統上,GLib的主循環和fork()是不兼容的。ui
有兩種可選的方式來管理傳遞給GSource回調函數用戶數據的內存。用戶數據就是在調用g_timeout_add()、g_timeout_add_full()、g_idle_add()傳入的參數。這些數據一般被timeout或idle回調函數所擁有,好比一個構件或一個網路協議的實現。有些時候這些回調函數會在數據被銷燬的後背調用,由於使用了已經被釋放的內存,因此這會致使一個錯誤。spa
struct _GSourcePrivate { GSList *child_sources; GSource *parent_source; gint64 ready_time; /* This is currently only used on UNIX, but we always declare it (and * let it remain empty on Windows) to avoid #ifdef all over the place. */ GSList *fds; }; struct _GSource { /*< private >*/ gpointer callback_data; GSourceCallbackFuncs *callback_funcs; const GSourceFuncs *source_funcs; guint ref_count; GMainContext *context; gint priority; guint flags; guint source_id; GSList *poll_fds; GSource *prev; GSource *next; char *name; GSourcePrivate *priv; }; struct _GPollRec { GPollFD *fd; GPollRec *prev; GPollRec *next; gint priority; }; struct _GMainContext { /* The following lock is used for both the list of sources * and the list of poll records */ GMutex mutex; GCond cond; GThread *owner; guint owner_count; GSList *waiters; gint ref_count; GHashTable *sources; /* guint -> GSource */ GPtrArray *pending_dispatches; gint timeout; /* Timeout for current iteration */ guint next_id; GList *source_lists; gint in_check_or_prepare; GPollRec *poll_records; guint n_poll_records; GPollFD *cached_poll_array; guint cached_poll_array_size; GWakeup *wakeup; GPollFD wake_up_rec; /* Flag indicating whether the set of fd's changed during a poll */ gboolean poll_changed; GPollFunc poll_func; gint64 time; gboolean time_is_fresh; }; struct _GMainLoop { GMainContext *context; gboolean is_running; gint ref_count; };
函數 | 說明 |
---|---|
g_main_context_add_poll_unlocked | 在g_source_add_poll和g_source_add_unix_fd中被調用,首先將須要監聽的文件描述符保存到_GSource->poll_fds或_GSource->priv->fds中,而後再建立個_GPollRec對象,接着將其保存到_GMainContext->poll_records中,最後設置context->poll_changed爲True,這個值會影響當前主循環的g_main_context_check |
g_source_attach_unlocked | 在g_source_attach和g_source_add_child_source中被調用,將_GSource保存到_GMainContext->sources,並將_GSource保存的文件描述符經過g_main_context_add_poll_unlocked函數保存到_GMainContext,最後返回_GSource->source_id |
g_main_context_prepare | 遍歷_GMainContext擁有的事件源,調用事件源的_GSourceFuncs->prepare函數計算下次輪訓間隔,並檢測事件源是否已經就緒 |
g_main_context_query | 從_GMainContext擁有的事件源中篩選出須要進行poll的事件源,並設置context->poll_changed爲False(只要不是在poll期間對事件源進行添加或刪除操做便可) |
g_main_context_poll | 使用poll函數監聽g_main_context_query篩選出的事件源 |
g_main_context_check | 遍歷g_main_context_query篩選出的事件源,調用事件源的_GSourceFuncs->check函數檢測事件源是否已經就緒,若是就緒則將事件源添加到_GMainContext->pending_dispatches中。若是context->poll_changed爲True(說明在poll的時候有新的事件源加入或移除),則跳事後面的分發(pending_dispatches爲空),從新執行g_main_context_iterate |
g_main_context_dispatch | 遍歷_GMainContext->pending_dispatches中的事件源,並調用事件源的_GSourceFuncs->dispatch函數進行分發 |
#define G_PRIORITY_DEFAULT_IDLE 200 GSourceFuncs g_idle_funcs = { g_idle_prepare, g_idle_check, g_idle_dispatch, NULL }; /* Idle functions */ static gboolean g_idle_prepare(GSource *source, gint *timeout) { *timeout = 0; return TRUE; } static gboolean g_idle_check(GSource *source) { return TRUE; } static gboolean g_idle_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { gboolean again; if (!callback) { g_warning ("Idle source dispatched without callback\n" "You must call g_source_set_callback()."); return FALSE; } again = callback (user_data); TRACE (GLIB_IDLE_DISPATCH (source, source->context, callback, user_data, again)); return again; }
函數 | 說明 |
---|---|
g_idle_add | 建立一個idle事件源,而後添加到GMainContext |
#define G_PRIORITY_DEFAULT 0 GSourceFuncs g_timeout_funcs = { NULL, /* prepare */ NULL, /* check */ g_timeout_dispatch, NULL }; static gboolean g_timeout_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { GTimeoutSource *timeout_source = (GTimeoutSource *)source; gboolean again; if (!callback) { g_warning ("Timeout source dispatched without callback\n" "You must call g_source_set_callback()."); return FALSE; } again = callback (user_data); TRACE (GLIB_TIMEOUT_DISPATCH (source, source->context, callback, user_data, again)); if (again) g_timeout_set_expiration (timeout_source, g_source_get_time (source)); return again; }
函數 | 說明 |
---|---|
g_timeout_add | 建立一個timeout事件源,而後添加到GMainContext。timeout事件源沒有本身的prepare和check函數,是由於在g_main_context_prepare和g_main_context_check 中都會獲取下當前系統時間,而後和timeout事件源的對比,若是時間已通過了,則將事件源的狀態修改成就緒狀態,因此就不須要本身來寫了 |
struct _GIOChannel { /*< private >*/ gint ref_count; GIOFuncs *funcs; gchar *encoding; GIConv read_cd; GIConv write_cd; gchar *line_term; /* String which indicates the end of a line of text */ guint line_term_len; /* So we can have null in the line term */ gsize buf_size; GString *read_buf; /* Raw data from the channel */ GString *encoded_read_buf; /* Channel data converted to UTF-8 */ GString *write_buf; /* Data ready to be written to the file */ gchar partial_write_buf[6]; /* UTF-8 partial characters, null terminated */ /* Group the flags together, immediately after partial_write_buf, to save memory */ guint use_buffer : 1; /* The encoding uses the buffers */ guint do_encode : 1; /* The encoding uses the GIConv coverters */ guint close_on_unref : 1; /* Close the channel on final unref */ guint is_readable : 1; /* Cached GIOFlag */ guint is_writeable : 1; /* ditto */ guint is_seekable : 1; /* ditto */ gpointer reserved1; gpointer reserved2; }; struct _GIOFuncs { GIOStatus (*io_read)(GIOChannel *channel, gchar *buf, gsize count, gsize *bytes_read, GError **err); GIOStatus (*io_write)(GIOChannel *channel, const gchar *buf, gsize count, gsize *bytes_written, GError **err); GIOStatus (*io_seek)(GIOChannel *channel, gint64 offset, GSeekType type, GError **err); GIOStatus (*io_close)(GIOChannel *channel, GError **err); GSource* (*io_create_watch)(GIOChannel *channel, GIOCondition condition); void (*io_free)(GIOChannel *channel); GIOStatus (*io_set_flags)(GIOChannel *channel, GIOFlags flags, GError **err); GIOFlags (*io_get_flags)(GIOChannel *channel); }; GSourceFuncs g_io_watch_funcs = { g_io_unix_prepare, g_io_unix_check, g_io_unix_dispatch, g_io_unix_finalize }; static GIOFuncs unix_channel_funcs = { g_io_unix_read, g_io_unix_write, g_io_unix_seek, g_io_unix_close, g_io_unix_create_watch, g_io_unix_free, g_io_unix_set_flags, g_io_unix_get_flags, };
函數 | 說明 |
---|---|
g_io_channel_new_file | 獲取並保存文件句柄,而後使用unix_channel_funcs初始化_GIOChannel->funcs |
g_io_create_watch | 使用g_io_unix_create_watch函數將channel保存的文件句柄轉換爲事件源,事件源函數爲g_io_watch_funcs |
g_io_add_watch | 使用g_io_create_watch建立事件源,而後附加到當前主循環的GMainContext上 |
g_io_unix_prepare | 獲取channel的輸入輸出buffer的數據狀態,並與須要監聽的channel事件進行對比 |
g_io_unix_check | 獲取channel的輸入輸出buffer的數據狀態,並與channel實際觸發的事件進行對比 |