You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GTK IOChannel问题:G_IO_IN事件触发异常致程序无限循环

Fixing the Infinite G_IO_STATUS_EOF Loop for Log File Updates

Hey there, I’ve run into this exact issue before with GLib’s GIO channels and log files. Let’s break down why this is happening and how to fix it properly.

Why You’re Seeing the Infinite EOF Loop

When your GIO channel hits EOF on the log file, the underlying file descriptor stays in a "readable" state—because calling read() on it will immediately return EOF (no blocking). The GLib event loop interprets this as a continuous "ready to read" signal, so it keeps firing your DisplayLogs callback over and over, each time returning G_IO_STATUS_EOF.

This is totally normal behavior for file descriptors, but it’s not what we want when monitoring a log file that gets appended to over time. Let’s look at two solutions, with the first being the most robust for your use case.


Instead of relying on GIO channel’s G_IO_IN watch (which isn’t designed for monitoring appended files), use GLib’s GFileMonitor—it’s purpose-built to detect changes to files, like when new content is added. This avoids the EOF loop entirely.

Step 1: Track State with a Data Structure

First, create a struct to hold the state we need (so we can pass it to callbacks):

typedef struct {
    GtkTextBuffer *text_buffer;
    goffset last_read_position;
    GFile *log_file;
} LogMonitorData;

Step 2: Initialize File Reading + Monitor

Replace your initial GIO channel setup with this code. It reads the full log file, then sets up a monitor to watch for changes:

// Get your TextView's buffer
GtkTextBuffer *txtBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tvLogs));
gtk_text_buffer_get_end_iter(txtBuf, &txtIter);

// Open and read the initial log content
GError *error = NULL;
GFile *log_file = g_file_new_for_path("./errorlog.log");
GFileInputStream *stream = g_file_read(log_file, NULL, &error);

if (stream) {
    // Read the entire file
    gchar *initial_content;
    gsize content_len;
    g_input_stream_read_all(G_INPUT_STREAM(stream), &initial_content, &content_len, NULL, &error);
    
    // Insert into TextView
    gtk_text_buffer_insert(txtBuf, &txtIter, initial_content, content_len);
    g_free(initial_content);
    
    // Record where we stopped reading (for future updates)
    goffset last_pos = g_seekable_tell(G_SEEKABLE(stream));
    g_object_unref(stream);

    // Set up our monitor data
    LogMonitorData *monitor_data = g_new(LogMonitorData, 1);
    monitor_data->text_buffer = txtBuf;
    monitor_data->last_read_position = last_pos;
    monitor_data->log_file = g_object_ref(log_file);

    // Create the file monitor
    GFileMonitor *monitor = g_file_monitor_file(log_file, G_FILE_MONITOR_NONE, NULL, &error);
    if (monitor) {
        // Connect the change event to our callback
        g_signal_connect(monitor, "changed", G_CALLBACK(on_log_updated), monitor_data);
    } else {
        g_warning("Failed to create log monitor: %s", error->message);
        g_error_free(error);
    }
} else {
    g_warning("Couldn't open log file: %s", error->message);
    g_error_free(error);
}

g_object_unref(log_file);

Step 3: Handle File Change Events

Add this callback to read new content when the log file is updated:

static void on_log_updated(GFileMonitor *monitor, GFile *file, GFile *unused, GFileMonitorEvent event, gpointer user_data) {
    LogMonitorData *data = user_data;
    
    // Only react to file content changes
    if (event != G_FILE_MONITOR_EVENT_CHANGED) {
        return;
    }

    GError *error = NULL;
    GFileInputStream *stream = g_file_read(data->log_file, NULL, &error);
    if (!stream) {
        g_warning("Failed to open log for update: %s", error->message);
        g_error_free(error);
        return;
    }

    // Jump to where we left off last time
    if (!g_seekable_seek(G_SEEKABLE(stream), data->last_read_position, G_SEEK_SET, NULL, &error)) {
        g_warning("Couldn't seek to last read position: %s", error->message);
        g_error_free(error);
        g_object_unref(stream);
        return;
    }

    // Read only the new content
    gchar *new_content;
    gsize new_content_len;
    if (g_input_stream_read_all(G_INPUT_STREAM(stream), &new_content, &new_content_len, NULL, &error)) {
        if (new_content_len > 0) {
            // Insert new content into the TextView
            GtkTextIter end_iter;
            gtk_text_buffer_get_end_iter(data->text_buffer, &end_iter);
            gtk_text_buffer_insert(data->text_buffer, &end_iter, new_content, new_content_len);
            
            // Update our last read position
            data->last_read_position = g_seekable_tell(G_SEEKABLE(stream));
        }
        g_free(new_content);
    } else {
        g_warning("Failed to read new log content: %s", error->message);
        g_error_free(error);
    }

    g_object_unref(stream);
}

Solution 2: Fix the Existing GIO Channel Callback

If you want to stick with your original GIO channel approach, you need to stop the watch when hitting EOF (since it will keep firing otherwise). You’ll also need a way to restart the watch when new content is added (we’d still recommend pairing this with GFileMonitor to trigger restarts).

Here’s the modified callback:

gboolean DisplayLogs(GIOChannel *chanErr, GIOCondition cond, gpointer data) {
    GtkWidget *tvLogs = GTK_WIDGET(gtk_builder_get_object(builder, "tvLogs"));
    g_assert(tvLogs);
    
    GtkTextBuffer *txtBuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(tvLogs));
    GtkTextIter txtIter;
    gchar *buf;
    gsize bufLen;
    GError *err = NULL;
    GIOStatus ret = g_io_channel_read_line(chanErr, &buf, &bufLen, NULL, &err);

    if (ret == G_IO_STATUS_NORMAL) {
        // We got new content—update the TextView
        gtk_text_buffer_get_end_iter(txtBuf, &txtIter);
        gtk_text_buffer_insert(txtBuf, &txtIter, buf, bufLen);
        g_free(buf);
        // Keep the watch active
        return TRUE;
    } else {
        switch (ret) {
            case G_IO_STATUS_ERROR:
                g_warning("Log read error: %s", err ? err->message : "Unknown error");
                break;
            case G_IO_STATUS_EOF:
                g_warning("Reached end of log—stopping IO watch");
                // Return FALSE to remove the watch (it's useless now until new content is added)
                break;
            case G_IO_STATUS_AGAIN:
                g_warning("Temporary read failure—keeping watch active");
                // Keep the watch going, since the issue is temporary
                return TRUE;
        }
        
        if (err) g_error_free(err);
        // Stop the watch on error or EOF
        return FALSE;
    }
}

Final Notes

The GFileMonitor approach is far better for log file monitoring—it’s efficient, designed for this exact use case, and avoids the EOF loop entirely. The second solution works but requires extra work to restart the watch when new content arrives.

内容的提问来源于stack exchange,提问作者Chouchenn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:47:45