diff mcabber/src/fifo.c @ 1653:fca9a4c17432

Improve UI latency and CPU usage Remove main_loop(), and use GIOChannels for the FIFO system.
author Myhailo Danylenko <isbear@ukrpost.net>
date Tue, 01 Dec 2009 21:06:06 +0100
parents dcd5d4c75199
children
line wrap: on
line diff
--- a/mcabber/src/fifo.c	Sun Nov 22 23:38:31 2009 +0200
+++ b/mcabber/src/fifo.c	Tue Dec 01 21:06:06 2009 +0100
@@ -2,6 +2,7 @@
  * fifo.c       -- Read commands from a named pipe
  *
  * Copyright (C) 2008,2009 Mikael Berthe <mikael@lilotux.net>
+ * Copyrigth (C) 2009      Myhailo Danylenko <isbear@ukrpost.net>
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -19,14 +20,12 @@
  * USA
  */
 
-#include <stdio.h>
 #include <stdlib.h>
 #include <glib.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <sys/time.h>
 #include <errno.h>
 #include <fcntl.h>
 
@@ -34,156 +33,146 @@
 #include "logprint.h"
 #include "utils.h"
 #include "settings.h"
-
-#include "hbuf.h"   // For HBB_BLOCKSIZE
+#include "main.h"
 
-static FILE *sfd;
-static char *fifo_name;
+static char *fifo_name = NULL;
+static GIOChannel *fifo_channel = NULL;
 
 static const char *FIFO_ENV_NAME = "MCABBER_FIFO";
 
-//  fifo_init(fifo_path)
-// Create and open the FIFO file.
-// If fifo_path is NULL, reopen the current pipe.
-// Return 0 (success) or -1 (failure).
+static gboolean attach_fifo(const char *name);
+
+static guint fifo_callback(GIOChannel *channel,
+                           GIOCondition condition,
+                           gpointer data)
+{
+  if (condition & (G_IO_IN|G_IO_PRI)) {
+    GIOStatus  chstat;
+    gchar     *buf;
+    gsize      endpos;
+
+    chstat = g_io_channel_read_line(channel, &buf, NULL, &endpos, NULL);
+    if (chstat == G_IO_STATUS_ERROR || chstat == G_IO_STATUS_EOF) {
+      if (!attach_fifo(fifo_name))
+        scr_LogPrint(LPRINT_LOGNORM,
+                     "Reopening fifo failed! Fifo will not work from now!");
+      return FALSE;
+    }
+    if (buf) {
+      guint logflag;
+      guint fifo_ignore = settings_opt_get_int("fifo_ignore");
+
+      if (endpos)
+        buf[endpos] = '\0';
+
+      if (settings_opt_get_int("fifo_hide_commands"))
+        logflag = LPRINT_LOG;
+      else
+        logflag = LPRINT_LOGNORM;
+      scr_LogPrint(logflag, "%s FIFO command: %s",
+                   (fifo_ignore ? "Ignoring" : "Executing"), buf);
+      if (!fifo_ignore) {
+        if (process_command(buf, TRUE) == 255)
+          mcabber_set_terminate_ui();
+      }
+
+      g_free(buf);
+    }
+  } else if (condition & (G_IO_ERR|G_IO_NVAL|G_IO_HUP)) {
+    if (!attach_fifo(fifo_name))
+      scr_LogPrint(LPRINT_LOGNORM,
+                   "Reopening fifo failed! Fifo will not work from now!");
+    return FALSE;
+  }
+  return TRUE;
+}
+
+static void fifo_destroy_callback(gpointer data)
+{
+  GIOChannel *channel = (GIOChannel *)data;
+  g_io_channel_unref(channel);
+}
+
+static gboolean check_fifo(const char *name)
+{
+  struct stat finfo;
+  if (stat(name, &finfo) == -1) {
+    /* some unknown error */
+    if (errno != ENOENT)
+      return FALSE;
+    /* fifo not yet exists */
+    if (mkfifo(name, S_IRUSR|S_IWUSR) != -1)
+      return check_fifo(name);
+    else
+      return FALSE;
+  }
+
+  /* file exists */
+  if (S_ISFIFO(finfo.st_mode))
+    return TRUE;
+  else
+    return FALSE;
+}
+
+static gboolean attach_fifo(const char *name)
+{
+  GSource *source;
+  int fd = open (name, O_RDONLY|O_NONBLOCK);
+  if (fd == -1)
+    return FALSE;
+
+  fifo_channel = g_io_channel_unix_new(fd);
+
+  g_io_channel_set_flags(fifo_channel, G_IO_FLAG_NONBLOCK, NULL);
+  g_io_channel_set_encoding(fifo_channel, NULL, NULL);
+  g_io_channel_set_close_on_unref(fifo_channel, TRUE);
+
+  source = g_io_create_watch(fifo_channel,
+                             G_IO_IN|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL);
+  g_source_set_callback(source, (GSourceFunc)fifo_callback,
+                        (gpointer)fifo_channel,
+                        (GDestroyNotify)fifo_destroy_callback);
+  g_source_attach(source, main_context);
+
+  return TRUE;
+}
+
 int fifo_init(const char *fifo_path)
 {
-  struct stat buf;
-  int fd;
-  char *fifo_path_xp;
-
-  if (!sfd && !fifo_path)
-    return -1;  // Nothing to do...
+  if (fifo_path) {
+    fifo_name = expand_filename(fifo_path);
 
-  if (sfd && !fifo_path) {  // We want to reinitialize the pipe
-    fclose(sfd);
-    sfd = NULL;
-    if (fifo_name)
-      goto fifo_init_open;
-  }
-  sfd = NULL;
-
-  fifo_path_xp = expand_filename(fifo_path);
-
-  if (!stat(fifo_path_xp, &buf)) {
-    if (!S_ISFIFO(buf.st_mode)) {
+    if (!check_fifo(fifo_name)) {
       scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO. "
-                   "%s already exists and is not a pipe", fifo_path_xp);
-      g_free(fifo_path_xp);
+                   "%s already exists and is not a pipe", fifo_name);
+      g_free(fifo_name);
       return -1;
     }
+  } else if (fifo_name)
+    g_source_remove_by_user_data(fifo_channel);
+  else
+    return -1;
 
-    if (unlink(fifo_path_xp)) {
-      scr_LogPrint(LPRINT_LOGNORM, "WARNING: Unable to unlink FIFO %s [%s]",
-                   fifo_path_xp, g_strerror(errno));
-      g_free(fifo_path_xp);
-      return -1;
-    }
-  }
-
-  if (mkfifo(fifo_path_xp, S_IWUSR | S_IRUSR)) {
-    scr_LogPrint(LPRINT_LOGNORM, "WARNING: Cannot create the FIFO [%s]",
-                 g_strerror(errno));
-    g_free(fifo_path_xp);
+  if (!attach_fifo(fifo_name)) {
+    scr_LogPrint(LPRINT_LOGNORM, "Error: Cannot open fifo");
     return -1;
   }
 
-  fifo_name = fifo_path_xp;
-
-fifo_init_open:
-  fd = open(fifo_name, O_RDONLY | O_NONBLOCK);
-  if (!fd)
-    return -1;
-
   setenv(FIFO_ENV_NAME, fifo_name, 1);
 
-  sfd = fdopen(fd, "r");
-  if (fifo_path)
-    scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_name);
-  return 0;
+  scr_LogPrint(LPRINT_LOGNORM, "FIFO initialized (%s)", fifo_path);
+  return 1;
 }
 
-//  fifo_deinit()
-// Close the current FIFO pipe and delete it.
 void fifo_deinit(void)
 {
   unsetenv(FIFO_ENV_NAME);
-  if (sfd) {
-    fclose(sfd);
-    sfd = NULL;
-  }
-  if (fifo_name) {
-    unlink(fifo_name);
-    g_free(fifo_name);
-    fifo_name = NULL;
-  }
-}
 
-//  fifo_read()
-// Read a line from the FIFO pipe (if available), and execute it.
-void fifo_read(void)
-{
-  struct timeval tv;
-  fd_set fds;
-  char *getbuf;
-  char buf[HBB_BLOCKSIZE+1];
-  int fd;
-
-  if (!sfd) {
-    return;
-  }
-
-  tv.tv_sec = 0;
-  tv.tv_usec = 0;
-
-  fd = fileno(sfd);
-
-  FD_ZERO(&fds);
-  FD_SET(fd, &fds);
-
-  select(fd + 1, &fds, NULL, NULL, &tv);
-
-  if (!FD_ISSET(fd, &fds)) {
-    return;
-  }
-
-  getbuf = fgets(buf, HBB_BLOCKSIZE, sfd);
-  if (getbuf) {
-    guint logflag;
-    char *eol = buf;
-    guint fifo_ignore = settings_opt_get_int("fifo_ignore");
-
-    // Strip trailing newlines
-    for ( ; *eol ; eol++)
-      ;
-    if (eol > buf)
-      eol--;
-    while (eol > buf && *eol == '\n')
-      *eol-- = 0;
-
-    if (settings_opt_get_int("fifo_hide_commands"))
-      logflag = LPRINT_LOG;
-    else
-      logflag = LPRINT_LOGNORM;
-    scr_LogPrint(logflag, "%s FIFO command: %s",
-                 (fifo_ignore ? "Ignoring" : "Executing"), buf);
-    if (!fifo_ignore) {
-      if (process_command(buf, TRUE) == 255)
-        mcabber_set_terminate_ui();
-    }
-  } else {
-    if (feof(sfd))
-      fifo_init(NULL);  // Reopen the FIFO on EOF
-  }
-}
-
-//  fifo_get_fd()
-// Return the FIFO file descriptor (-1 if none).
-int fifo_get_fd(void)
-{
-  if (sfd)
-    return fileno(sfd);
-  return -1;
+  /* destroy open fifo */
+  unlink(fifo_name);
+  g_source_remove_by_user_data(fifo_channel);
+  /* channel itself should be destroyed by destruction callback */
+  g_free(fifo_name);
 }
 
 /* vim: set expandtab cindent cinoptions=>2\:2(0:  For Vim users... */