簡體   English   中英

GFileMonitor-g_signal_handler_block“已更改”信號不會阻止處理程序嗎?

[英]GFileMonitor - g_signal_handler_block “changed” signal doesn't block handler?

所有,

可能要花一點時間進行設置。 在過去的幾個月中,我有一個小型的編輯器項目[1]。 我最初想在當前的編輯器文件上實現一個inotify監視,以防止被前面的過程修改。 我創建的自定義信號,並且例程addremove ,和monitor (與pselect )手表和例程來blockunblock自定義信號的發射,以允許正常save/save as不觸發回調。 我遇到的問題是如何將monitor添加到gtk_event_loop以便對循環的每次迭代進行檢查。 我決定使用g_idle_add並轉到gtk-app-devel列表來確定該方法是否合理或是否有更好的方法。 共識是使用GIO / GFileMonitor而不是直接使用inotify

快速解決當前問題。 我重寫了使用GFileMonitor/g_file_monitor_file的實現,並重寫了blockunblock例程以阻止對"changed"信號的處理,從而允許正常save/save as而無需觸發回調。 問題是當我在保存文件之前block實例和handler_id的回調時,該回調仍會觸發。 當將inotify實現與自定義信號一起使用時,阻止信號的發射效果很好。 我已將其發布回gtk-app-devel列表,但沒有得到任何回報-這就是為什么我在這里詢問。 為什么g_signal_handler_block與GIO / GFileMonitor不是塊處理的"changed"信號的回調? (更重要的是,我該如何解決)

注意: (MCVE-完整的測試代碼位於https://github.com/drankinatty/gtktest )。 要使用GtkSourceView2進行構建,只需鍵入make with=-DWGTKSOURCEVIEW2 ,它將構建為bin/gtkwrite ;否則,不進行構建,只需鍵入make ,它將構建為bin/gtkedit

相關代碼的邏輯如下( app是包含相關編輯器變量/信息和設置的struct的實例)GIO / GFileMonitor實現位於gtk_filemon.[ch] ,保存功能的包裝位於gtk_filebuf.c

typedef struct {
    ...
    gchar           *filename;
    GFileMonitor    *filemon;
    gulong          mfp_handler;
    ...
} kwinst;

kwinst *app = g_slice_new (kwinst);

我將手表設置為:

GFile *gfile = g_file_new_for_path (app->filename);
...

/* create monitor for app->filename */
app->filemon = g_file_monitor_file (gfile,
                        G_FILE_MONITOR_NONE,
                        cancellable, &err);
...

/* connect changed signal to monitored file saving ID */
app->mfp_handler = g_signal_connect (G_OBJECT(app->filemon), "changed",
                        G_CALLBACK (file_monitor_on_changed), data);

實例( app->filemon )和handler_id( app->mfp_handler )均被保存。 mfp只是外來進程修改的縮寫)為了防止在正常的保存/保存操作過程中處理更改,我創建了阻止和取消阻止功能以防止對文件的更改觸發回調,例如,下面用debug g_print進行顯示要求:

void file_monitor_block_changed (gpointer data)
{
    kwinst *app = (kwinst *)data;

    if (!app->filemon || !app->mfp_handler) return;

    g_print ("blocking changed (%lu)\n", app->mfp_handler);

    g_signal_handler_block (app->filemon, app->mfp_handler);
}

void file_monitor_unblock_changed (gpointer data)
{
    kwinst *app = (kwinst *)data;

    if (!app->filemon || !app->mfp_handler) return;

    g_print ("unblocking changed (%lu)\n", app->mfp_handler);

    g_signal_handler_unblock (app->filemon, app->mfp_handler);
}

為了實現block/unblock ,我將文件'save / save as'函數包裝在block ,然后是save [2],然后是unblock ,但是回調仍在正常保存時觸發。 例如,我擁有的保存功能的相關部分是:

if (app->mfp_handler)                   /* current file monitor on file */
    file_monitor_block_changed (app);   /* block "changed" signal */

g_print ("  buffer_write_file (app, filename)\n");
buffer_write_file (app, filename);      /* write to file app->filename */

if (filename)
    file_monitor_add (app);             /* setup monitoring on new name */
else if (app->mfp_handler)
    file_monitor_unblock_changed (app); /* unblock "changed" signal */

使用上面的debug g_print語句,在以下輸出中發出save結果:

$ ./bin/gtkwrite
blocking changed (669)
  buffer_write_file (app, filename)
unblocking changed (669)
Monitor Event: File = /home/david/tmp/foo.txt.UY9IXY
G_FILE_MONITOR_EVENT_DELETED
Monitor Event: File = /home/david/tmp/foo.txt
G_FILE_MONITOR_EVENT_CREATED
Monitor Event: File = /home/david/tmp/foo.txt
G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT
Monitor Event: File = /home/david/tmp/foo.txt
G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED

我是否包含block/unblock都沒有區別,回調的觸發是不變的。 我懷疑問題在於, "changed"信號及其處理是通過GIO而不是GTK來實現的(就像inotify實現的自定義信號一樣),並且在阻撓處理"changed" g_signal_handler_block使用"changed"信號-盡管我在文檔中找不到區別。 該文檔指出:

 To get informed about changes to the file or directory you are monitoring, connect to the “changed” signal. The signal will be emitted in the thread-default main context of the thread that the monitor was created in (though if the global default main context is blocked, this may cause notifications to be blocked even if the thread-default context is still running). 

https://developer.gnome.org/gio/stable/GFile.html#g-file-monitor-file

應用程序本身不使用任何顯式線程,並且不fork保存的任何部分。 因此,我對如何block/unblock不阻止處理"changed"信號感到block/unblock 保存完全包裝在block/unblock ,除非g_file_set_contents調用是異步的,否則我不會看到任何時序問題。

為什么調用g_signal_handler_block/g_signal_handler_unblock無法阻止處理對當前文件的更改發出的"changed"信號? 我可以g_signal_handler_disconnect並且什么也不會觸發,但我不必disconnect即可暫時阻止處理。 我想念什么?

為了完整file_monitor_on_changed函數與腳注一起包含在下面:

void file_monitor_on_changed (GFileMonitor *mon, 
                                GFile *file, GFile *other,
                                GFileMonitorEvent evtype,
                                gpointer data)
{
    kwinst *app = (kwinst *)data;

    g_print ("Monitor Event: File = %s\n", g_file_get_parse_name (file));

    switch (evtype)
    {
        case G_FILE_MONITOR_EVENT_CHANGED:
            /* prompt or emit custom signal modified by foreign process */
            g_print ("G_FILE_MONITOR_EVENT_CHANGED\n");
            break;
        case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
            g_print ("G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT\n");
            break;
        case G_FILE_MONITOR_EVENT_DELETED:
            /* avoid firing on normal '.tmp' file delete */
            if (g_strcmp0 (g_file_get_parse_name (file), app->filename)) {
                g_print ("  ignoring 'tmp' file delete.\n");
                break;
            }
            /* prompt or emit custom signal modified by foreign process */
            g_print ("G_FILE_MONITOR_EVENT_DELETED\n");
            /* prompt save file */
            break;
        case G_FILE_MONITOR_EVENT_CREATED:
            g_print ("G_FILE_MONITOR_EVENT_CREATED\n");
            break;
        case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
            g_print ("G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED\n");
            break;
        case G_FILE_MONITOR_EVENT_PRE_UNMOUNT:
            g_print ("G_FILE_MONITOR_EVENT_PRE_UNMOUNT\n");
            break;
        case G_FILE_MONITOR_EVENT_UNMOUNTED:
            g_print ("G_FILE_MONITOR_EVENT_UNMOUNTED\n");
            break;
        case G_FILE_MONITOR_EVENT_MOVED:
            g_print ("G_FILE_MONITOR_EVENT_MOVED\n");
            /* prompt save file */
            break;
        case G_FILE_MONITOR_EVENT_RENAMED:
            /* prompt save file */
            g_print ("G_FILE_MONITOR_EVENT_RENAMED\n");
            break;
        case G_FILE_MONITOR_EVENT_MOVED_IN:
            g_print ("G_FILE_MONITOR_EVENT_MOVED_IN\n");
            break;
        case G_FILE_MONITOR_EVENT_MOVED_OUT:
            g_print ("G_FILE_MONITOR_EVENT_MOVED_OUT\n");
            break;
        default:
            g_print ("unknown EVENT on changed signal.\n");
    }

    if (mon || other) {}
}

在其他情況下,使用g_signal_handler_block可以正常工作

作為評論中提到的一點,我可以確認我可以輕松地blockunblock其他信號處理程序而沒有問題。 具體來說,在使用inotify實現時,我創建了以下自定義信號:

/* create signal to monitor file modified by foreign process */
app->SIG_MODFP = g_signal_new ("modified-foreign",
                                GTK_TYPE_TEXT_BUFFER,
                                GTK_RUN_ACTION,
                                0,
                                NULL,
                                NULL,
                                NULL,
                                G_TYPE_NONE,
                                1,
                                G_TYPE_POINTER);

然后將信號連接到處理程序,並按以下方式保存handler_id:

/* custom signals */
app->mfp_handler2 = g_signal_connect (GTK_TEXT_BUFFER(app->buffer), 
                  "modified-foreign",
                  G_CALLBACK (on_modified_foreign), app);

on_modified_foreign回調是用於測試阻塞/解除阻塞的簡單測試回調:

void on_modified_foreign (GtkTextBuffer *buffer,
                        kwinst *app)
{
    dlg_info ("File has changed on disk, reload?", "Modified by Foreign Process");

    if (buffer || app) {}
}

上述dlg_info僅僅是一個包裝gtk_message_dialog_new時,彈出一個對話框"modified-foreign"信號發出。

然后執行一個簡單的測試,其中一個菜單項導致信號被發出,例如:

void menu_status_bigredbtn_activate (GtkMenuItem *menuitem, kwinst *app)
{
    g_signal_emit_by_name (G_OBJECT(app->buffer), "modified-foreign::", app);
}

而且,最終阻止/取消阻止就可以了:

void menu_status_block_activate (GtkMenuItem *menuitem, kwinst *app)
{
    if (!app->mfp_handler2) return;
    GtkTextBuffer *buffer = GTK_TEXT_BUFFER(app->buffer);
    g_signal_handler_block (buffer, app->mfp_handler2);
}

void menu_status_unblock_activate (GtkMenuItem *menuitem, kwinst *app)
{
    if (!app->mfp_handler2) return;
    GtkTextBuffer *buffer = GTK_TEXT_BUFFER(app->buffer);
    g_signal_handler_unblock (buffer, app->mfp_handler2);
}

一切正常。 選擇bigredbtn菜單項,信號被發射並向上彈出對話框。 然后選擇block菜單項,然后再次嘗試bigredbtn什么也沒有發生,沒有對話框,什么也沒有。 然后選擇unblock菜單項,然后再次選擇bigredbtn ,每次選擇時都會再次彈出對話框。 (並且在處理程序被阻塞時發出的信號沒有排隊,並且一旦被解除阻塞,就不會調用該處理程序)

這就是我遇到的問題。 問題的很大一部分是沒有選擇GIO源代碼,盡管它在很大程度上是一個很大的黑匣子。 一切在GTK方面都可以正常工作,但是當執行涉及GIO功能的相同操作時,結果似乎無法達到預期效果。

感謝您對這個問題的其他見解。

腳注1: https //github.com/drankinatty/gtkwrite

腳注2: buffer_write_file調用g_file_set_contents寫入磁盤。

好的,我已經玩了很長時間,並且找到了解決方案。 出於某種原因,關鍵似乎是出於GIO大計划中的原因,從原始連接信號的同一源中調用blockunblock 。例如,在gtk_filemon.c源和然后從任何地方調用(如上述測試菜單項中所述)都可以正常工作,例如:

void file_monitor_block_changed (gpointer data)
{
    kwinst *app = (kwinst *)data;

    if (!app->filemon || !app->mfp_handler) return;

    g_signal_handler_block (app->filemon, app->mfp_handler);
}

void file_monitor_unblock_changed (gpointer data)
{
    kwinst *app = (kwinst *)data;

    if (!app->filemon || !app->mfp_handler) return;

    g_signal_handler_unblock (app->filemon, app->mfp_handler);
}

這使來自menu_status_block_activatemenu_status_unblock_activate上述兩個函數的調用可以按預期工作,並在阻止時成功阻止對"changed"信號的處理,並在取消阻止時恢復。 為什么g_signal_handler_unblock使用實例handler_id直接調用g_signal_handler_blockg_signal_handler_unblockg_signal_handler_blockg_signal_handler_unblock將只剩下文檔之謎。

注意:如果有人想玩,我將把github.com gtktest代碼保留到4月中旬,之后工作代碼將存在於https://github.com/drankinatty/gtkwrite

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM