194 lines
5.6 KiB
C
194 lines
5.6 KiB
C
#ifndef SELECTOR_H
|
|
#define SELECTOR_H
|
|
|
|
#include <sys/time.h>
|
|
#include <stdbool.h>
|
|
|
|
/**
|
|
* selector.c - un muliplexor de entrada salida
|
|
*
|
|
* Un selector permite manejar en un único hilo de ejecución la entrada salida
|
|
* de file descriptors de forma no bloqueante.
|
|
*
|
|
* Esconde la implementación final (select(2) / poll(2) / epoll(2) / ..)
|
|
*
|
|
* El usuario registra para un file descriptor especificando:
|
|
* 1. un handler: provee funciones callback que manejarán los eventos de
|
|
* entrada/salida
|
|
* 2. un interés: que especifica si interesa leer o escribir.
|
|
*
|
|
* Es importante que los handlers no ejecute tareas bloqueantes ya que demorará
|
|
* el procesamiento del resto de los descriptores.
|
|
*
|
|
* Si el handler requiere bloquearse por alguna razón (por ejemplo realizar
|
|
* una resolución de DNS utilizando getaddrinfo(3)), tiene la posiblidad de
|
|
* descargar el trabajo en un hilo notificará al selector que el resultado del
|
|
* trabajo está disponible y se le presentará a los handlers durante
|
|
* la iteración normal. Los handlers no se tienen que preocupar por la
|
|
* concurrencia.
|
|
*
|
|
* Dicha señalización se realiza mediante señales, y es por eso que al
|
|
* iniciar la librería `selector_init' se debe configurar una señal a utilizar.
|
|
*
|
|
* Todos métodos retornan su estado (éxito / error) de forma uniforme.
|
|
* Puede utilizar `selector_error' para obtener una representación human
|
|
* del estado. Si el valor es `SELECTOR_IO' puede obtener información adicional
|
|
* en errno(3).
|
|
*
|
|
* El flujo de utilización de la librería es:
|
|
* - iniciar la libreria `selector_init'
|
|
* - crear un selector: `selector_new'
|
|
* - registrar un file descriptor: `selector_register_fd'
|
|
* - esperar algún evento: `selector_iteratate'
|
|
* - destruir los recursos de la librería `selector_close'
|
|
*/
|
|
typedef struct fdselector * fd_selector;
|
|
|
|
/** valores de retorno. */
|
|
typedef enum {
|
|
/** llamada exitosa */
|
|
SELECTOR_SUCCESS = 0,
|
|
/** no pudimos alocar memoria */
|
|
SELECTOR_ENOMEM = 1,
|
|
/** llegamos al límite de descriptores que la plataforma puede manejar */
|
|
SELECTOR_MAXFD = 2,
|
|
/** argumento ilegal */
|
|
SELECTOR_IARGS = 3,
|
|
/** descriptor ya está en uso */
|
|
SELECTOR_FDINUSE = 4,
|
|
/** I/O error check errno */
|
|
SELECTOR_IO = 5,
|
|
} selector_status;
|
|
|
|
/** retorna una descripción humana del fallo */
|
|
const char *
|
|
selector_error(const selector_status status);
|
|
|
|
/** opciones de inicialización del selector */
|
|
struct selector_init {
|
|
/** señal a utilizar para notificaciones internas */
|
|
const int signal;
|
|
|
|
/** tiempo máximo de bloqueo durante `selector_iteratate' */
|
|
struct timespec select_timeout;
|
|
};
|
|
|
|
/** inicializa la librería */
|
|
selector_status
|
|
selector_init(const struct selector_init *c);
|
|
|
|
/** deshace la incialización de la librería */
|
|
selector_status
|
|
selector_close(void);
|
|
|
|
/* instancia un nuevo selector. returna NULL si no puede instanciar */
|
|
fd_selector
|
|
selector_new(const size_t initial_elements);
|
|
|
|
/** destruye un selector creado por _new. Tolera NULLs */
|
|
void
|
|
selector_destroy(fd_selector s);
|
|
|
|
/**
|
|
* Intereses sobre un file descriptor (quiero leer, quiero escribir, …)
|
|
*
|
|
* Son potencias de 2, por lo que se puede requerir una conjunción usando el OR
|
|
* de bits.
|
|
*
|
|
* OP_NOOP es útil para cuando no se tiene ningún interés.
|
|
*/
|
|
typedef enum {
|
|
OP_NOOP = 0,
|
|
OP_READ = 1 << 0,
|
|
OP_WRITE = 1 << 2,
|
|
} fd_interest;
|
|
|
|
/**
|
|
* Quita un interés de una lista de intereses
|
|
*/
|
|
#define INTEREST_OFF(FLAG, MASK) ( (FLAG) & ~(MASK) )
|
|
|
|
/**
|
|
* Argumento de todas las funciones callback del handler
|
|
*/
|
|
struct selector_key {
|
|
/** el selector que dispara el evento */
|
|
fd_selector s;
|
|
/** el file descriptor en cuestión */
|
|
int fd;
|
|
/** dato provisto por el usuario */
|
|
void * data;
|
|
};
|
|
|
|
/**
|
|
* Manejador de los diferentes eventos..
|
|
*/
|
|
typedef struct fd_handler {
|
|
void (*handle_read) (struct selector_key *key);
|
|
void (*handle_write) (struct selector_key *key);
|
|
void (*handle_block) (struct selector_key *key);
|
|
|
|
/**
|
|
* llamado cuando se se desregistra el fd
|
|
* Seguramente deba liberar los recusos alocados en data.
|
|
*/
|
|
void (*handle_close) (struct selector_key *key);
|
|
|
|
} fd_handler;
|
|
|
|
/**
|
|
* registra en el selector `s' un nuevo file descriptor `fd'.
|
|
*
|
|
* Se especifica un `interest' inicial, y se pasa handler que manejará
|
|
* los diferentes eventos. `data' es un adjunto que se pasa a todos
|
|
* los manejadores de eventos.
|
|
*
|
|
* No se puede registrar dos veces un mismo fd.
|
|
*
|
|
* @return 0 si fue exitoso el registro.
|
|
*/
|
|
selector_status
|
|
selector_register(fd_selector s,
|
|
const int fd,
|
|
const fd_handler *handler,
|
|
const fd_interest interest,
|
|
void *data);
|
|
|
|
/**
|
|
* desregistra un file descriptor del selector
|
|
*/
|
|
selector_status
|
|
selector_unregister_fd(fd_selector s,
|
|
const int fd);
|
|
|
|
/** permite cambiar los intereses para un file descriptor */
|
|
selector_status
|
|
selector_set_interest(fd_selector s, int fd, fd_interest i);
|
|
|
|
/** permite cambiar los intereses para un file descriptor */
|
|
selector_status
|
|
selector_set_interest_key(struct selector_key *key, fd_interest i);
|
|
|
|
|
|
/**
|
|
* se bloquea hasta que hay eventos disponible y los despacha.
|
|
* Retorna luego de cada iteración, o al llegar al timeout.
|
|
*/
|
|
selector_status
|
|
selector_select(fd_selector s);
|
|
|
|
/**
|
|
* Método de utilidad que activa O_NONBLOCK en un fd.
|
|
*
|
|
* retorna -1 ante error, y deja detalles en errno.
|
|
*/
|
|
int
|
|
selector_fd_set_nio(const int fd);
|
|
|
|
/** notifica que un trabajo bloqueante terminó */
|
|
selector_status
|
|
selector_notify_block(fd_selector s,
|
|
const int fd);
|
|
|
|
#endif
|