Monitorizar E/S

Una característica elegante de Glib (una de las bibliotecas subyacentes de gtkmm) es la capacidad de verificar los datos en un descriptor de archivos. Esto es especialmente útil para aplicaciones de red. Se usa el siguiente método para lograrlo:

sigc::connection Glib::SignalIO::connect(const sigc::slot<bool(Glib::IOCondition)>& slot,
                                 Glib::PollFD::fd_t fd, Glib::IOCondition condition,
                                 int priority = Glib::PRIORITY_DEFAULT);

The first argument is a slot you wish to have called when the specified event (see argument 3) occurs on the file descriptor you specify using argument two. Argument three may be one or more (using |) of:

  • Glib::IO_IN: llame a su método cuando haya datos listos para leerse en su descriptor de archivos.
  • Glib::IO_OUT: llame a su método cuando el descriptor de archivos esté listo para la escritura.
  • Glib::IO_PRI: llame a su método cuando el descriptor de archivos tenga datos urgentes para leer.
  • Glib::IO_ERR: llame a su método cuando haya ocurrido un error en el descriptor de archivos.
  • Glib::IO_HUP: llame a su método cuando se cuelgue (la conexión se ha roto, normalmente por tuberías y «sockets»).

El valor de retorno es una sigc::connection que puede usarse para detener la monitorización del descriptor de archivos usando su método disconnect(). El gestor de señales slot debe declararse de la siguiente manera:

bool input_callback(Glib::IOCondition condition);

donde condition se especifica como se mencionó previamente. Como de costumbre, el «slot» se crea con sigc::mem_fun() (para un método miembro de un objeto), o sigc::ptr_fun() (para una función).

A continuación se muestra un pequeño ejemplo. Para usar el ejemplo, sólo ejecútelo desde un terminal; no crea una ventana. Creará una tubería llamada testfifo en la carpeta actual. Después inicie otro intérprete de comandos y ejecute echo "Hello" > testfifo. El ejemplo imprimirá cada línea que introduzca hasta que ejecute echo "Q" > testfifo.

Código fuente

File: main.cc (For use with gtkmm 4)

#include <gtkmm/application.h>
#include <glibmm/main.h>
#include <glibmm/iochannel.h>
#include <fcntl.h>
#include <iostream>

#include <unistd.h> //The SUN Forte compiler puts F_OK here.

//The SUN Forte compiler needs these for mkfifo:
#include <sys/types.h>
#include <sys/stat.h>

Glib::RefPtr<Gtk::Application> app;

int read_fd;
Glib::RefPtr<Glib::IOChannel> iochannel;

/*
  send to the fifo with:
  echo "Hello" > testfifo

  quit the program with:
  echo "Q" > testfifo
*/

// this will be our signal handler for read operations
// it will print out the message sent to the fifo
// and quit the program if the message was 'Q'.
bool MyCallback(Glib::IOCondition io_condition)
{
  if ((io_condition & Glib::IOCondition::IO_IN) != Glib::IOCondition::IO_IN) {
    std::cerr << "Invalid fifo response" << std::endl;
  }
  else {
   Glib::ustring buf;

   iochannel->read_line(buf);
   std::cout << buf;
   if (buf == "Q\n")
     app->quit();

  }
  return true;
}


int main(int argc, char *argv[])
{
  app = Gtk::Application::create("org.gtkmm.example");

  if (access("testfifo", F_OK) == -1) {
    // fifo doesn't exist - create it
    #ifndef DONT_HAVE_MKFIFO
    if (mkfifo("testfifo", 0666) != 0) {
      std::cerr << "error creating fifo" << std::endl;
      return -1;
    }
    #else
      std::cerr << "error creating fifo: This platform does not have mkfifo()"
          << std::endl;
    #endif //DONT_HAVE_MKFIFO
  }

  // Although we will only read from the fifo, we open it in read/write mode.
  // Due to a peculiarity with the poll() system call, used deep down in glib,
  // this small program will use all available CPU time, if the fifo is opened
  // as O_RDONLY. See a discussion on the gtkmm-list, e.g.
  // https://mail.gnome.org/archives/gtkmm-list/2015-September/msg00034.html
  // and the link from there to stackoverflow.
  read_fd = open("testfifo", O_RDWR);
  if (read_fd == -1)
  {
    std::cerr << "error opening fifo" << std::endl;
    return -1;
  }

  // connect the signal handler
  Glib::signal_io().connect(sigc::ptr_fun(MyCallback), read_fd, Glib::IOCondition::IO_IN);

  // Creates a iochannel from the file descriptor
  iochannel = Glib::IOChannel::create_from_fd(read_fd);

  // and last but not least - run the application main loop
  app->hold(); // keep the application running without a window
  app->run(argc, argv);

  // now remove the temporary fifo
  if(unlink("testfifo"))
    std::cerr << "error removing fifo" << std::endl;

  return 0;
}