Replace select with poll

Added prio fdes, handling of EINTR,
optimized double for loops, handle POLLHUP
This commit is contained in:
Olof hagsand 2025-02-05 12:36:49 +01:00
parent aa617fd677
commit 74c8244c66
6 changed files with 283 additions and 277 deletions

View file

@ -18,6 +18,8 @@ Planned: April 2025
### Features ### Features
* Event handling: replace `select` with `poll`
* See [Support more than 100 devices](https://github.com/clicon/clixon-controller/issues/174)
* Added new `ca_userdef` callback * Added new `ca_userdef` callback
* New `clixon-restconf@2025-02-01.yang` revision * New `clixon-restconf@2025-02-01.yang` revision
* Added timeout parameter * Added timeout parameter

View file

@ -1631,7 +1631,7 @@ from_client_process_control(clixon_handle h,
* @retval -1 Error * @retval -1 Error
*/ */
static int static int
from_client_hello(clixon_handle h, from_client_hello(clixon_handle h,
cxobj *x, cxobj *x,
struct client_entry *ce, struct client_entry *ce,
cbuf *cbret) cbuf *cbret)

14
configure vendored
View file

@ -777,7 +777,6 @@ with_yang_standard_dir
with_clicon_user with_clicon_user
with_clicon_group with_clicon_group
enable_nls enable_nls
enable_event_poll
' '
ac_precious_vars='build_alias ac_precious_vars='build_alias
host_alias host_alias
@ -1424,7 +1423,6 @@ Optional Features:
http/1 only http/1 only
--enable-netsnmp Enable net-snmp Clixon YANG mapping --enable-netsnmp Enable net-snmp Clixon YANG mapping
--enable-event-poll Enable event polling feature
Optional Packages: Optional Packages:
--with-PACKAGE[=ARG] use PACKAGE [ARG=yes] --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
@ -6249,18 +6247,6 @@ fi
test "x$prefix" = xNONE && prefix=$ac_default_prefix test "x$prefix" = xNONE && prefix=$ac_default_prefix
# Check whether --enable-event-poll was given.
if test ${enable_event_poll+y}
then :
enableval=$enable_event_poll; if test "$enable_event_poll" = "yes"; then
printf "%s\n" "#define CLIXON_EVENT_POLL 1" >>confdefs.h
fi
fi
ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/snmp/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/main/example.xml docker/Makefile docker/clixon-dev/Makefile docker/example/Makefile docker/test/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile doc/Makefile test/Makefile test/config.sh test/cicd/Makefile test/vagrant/Makefile" ac_config_files="$ac_config_files Makefile lib/Makefile lib/src/Makefile lib/clixon/Makefile apps/Makefile apps/cli/Makefile apps/backend/Makefile apps/netconf/Makefile apps/restconf/Makefile apps/snmp/Makefile include/Makefile etc/Makefile etc/clixonrc example/Makefile example/main/Makefile example/main/example.xml docker/Makefile docker/clixon-dev/Makefile docker/example/Makefile docker/test/Makefile yang/Makefile yang/clixon/Makefile yang/mandatory/Makefile doc/Makefile test/Makefile test/config.sh test/cicd/Makefile test/vagrant/Makefile"
cat >confcache <<\_ACEOF cat >confcache <<\_ACEOF

View file

@ -463,21 +463,6 @@ AH_BOTTOM([#include <clixon_custom.h>])
test "x$prefix" = xNONE && prefix=$ac_default_prefix test "x$prefix" = xNONE && prefix=$ac_default_prefix
AC_ARG_ENABLE([event-poll],
AS_HELP_STRING([--enable-event-poll], [Enable event polling feature]),
[if test "$enable_event_poll" = "yes"; then
AC_DEFINE([CLIXON_EVENT_POLL], 1, [Enable event polling feature])
fi]
)
AC_ARG_WITH([max-events],
AS_HELP_STRING([--with-max-events=VALUE], [Set the maximum number of events (default: 1024)]),
[MAX_EVENTS=$withval],
[MAX_EVENTS=1024] # Значение по умолчанию, если не указано
)
AC_DEFINE_UNQUOTED([MAX_EVENTS], [$MAX_EVENTS], [Maximum number of events])
AC_CONFIG_FILES([Makefile AC_CONFIG_FILES([Makefile
lib/Makefile lib/Makefile
lib/src/Makefile lib/src/Makefile

View file

@ -75,9 +75,6 @@
/* Define to 1 if you have the `fcgi' library (-lfcgi). */ /* Define to 1 if you have the `fcgi' library (-lfcgi). */
#undef HAVE_LIBFCGI #undef HAVE_LIBFCGI
/* Define to 1 if you have the `m' library (-lm). */
#undef HAVE_LIBM
/* Define to 1 if you have the `netsnmp' library (-lnetsnmp). */ /* Define to 1 if you have the `netsnmp' library (-lnetsnmp). */
#undef HAVE_LIBNETSNMP #undef HAVE_LIBNETSNMP

View file

@ -1,7 +1,7 @@
/* /*
* *
***** BEGIN LICENSE BLOCK ***** ***** BEGIN LICENSE BLOCK *****
Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren Copyright (C) 2009-2016 Olof Hagsand and Benny Holmgren
Copyright (C) 2017-2019 Olof Hagsand Copyright (C) 2017-2019 Olof Hagsand
Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgat)e Copyright (C) 2020-2022 Olof Hagsand and Rubicon Communications, LLC(Netgat)e
@ -25,7 +25,7 @@
in which case the provisions of the GPL are applicable instead in which case the provisions of the GPL are applicable instead
of those above. If you wish to allow use of your version of this file only of those above. If you wish to allow use of your version of this file only
under the terms of the GPL, and not to allow others to under the terms of the GPL, and not to allow others to
use your version of this file under the terms of Apache License version 2, use your version of this file under the terms of Apache License version 2,
indicate your decision by deleting the provisions above and replace them with indicate your decision by deleting the provisions above and replace them with
the notice and other provisions required by the GPL. If you do not delete the notice and other provisions required by the GPL. If you do not delete
the provisions above, a recipient may use your version of this file under the provisions above, a recipient may use your version of this file under
@ -49,6 +49,7 @@
#include <errno.h> #include <errno.h>
#include <string.h> #include <string.h>
#include <signal.h> #include <signal.h>
#include <poll.h>
#include <syslog.h> #include <syslog.h>
#include <sys/param.h> #include <sys/param.h>
#include <sys/types.h> #include <sys/types.h>
@ -69,10 +70,6 @@
#include "clixon_options.h" #include "clixon_options.h"
#include "clixon_event.h" #include "clixon_event.h"
#ifdef CLIXON_EVENT_POLL
#include <poll.h>
#endif
/* /*
* Constants * Constants
*/ */
@ -86,20 +83,30 @@ struct event_data{
int (*e_fn)(int, void*); /* Callback function */ int (*e_fn)(int, void*); /* Callback function */
enum {EVENT_FD, EVENT_TIME} e_type; /* Type of event */ enum {EVENT_FD, EVENT_TIME} e_type; /* Type of event */
int e_fd; /* File descriptor */ int e_fd; /* File descriptor */
int e_prio; /* 1: high-prio FD:s only*/
struct timeval e_time; /* Timeout */ struct timeval e_time; /* Timeout */
void *e_arg; /* Function argument */ void *e_arg; /* Function argument */
char e_string[EVENT_STRLEN]; /* String for debugging */ char e_descr[EVENT_STRLEN]; /* String for debugging */
struct pollfd *e_pollfd; /* Pointer to pull struct */
}; };
/* /*
* Internal variables * Internal variables
* XXX consider use handle variables instead of global * Consider use handle variables instead of global, but needs API changes
*/ */
static struct event_data *ee = NULL; /* File event handlers */
static struct event_data *ee_timers = NULL; static struct event_data *_ee = NULL;
static int _ee_nr = 0;
/* Set if element in ee is deleted (clixon_event_unreg_fd). Check in ee loops */ /* Prioritized File event handlers */
static struct event_data *_ee_prio = NULL;
static int _ee_prio_nr = 0;
/* Timer event handlers */
static struct event_data *_ee_timers = NULL;
/* Set if element in _ee is deleted (clixon_event_unreg_fd). Check in _ee loops
* XXX: algorithm has flaw: which _ee is unregged?
*/
static int _ee_unreg = 0; static int _ee_unreg = 0;
/* If set (eg by signal handler) exit select loop on next run and return 0 */ /* If set (eg by signal handler) exit select loop on next run and return 0 */
@ -113,8 +120,8 @@ static int _clicon_sig_ignore = 0;
/*! For signal handlers: instead of doing exit, set a global variable to exit /*! For signal handlers: instead of doing exit, set a global variable to exit
* *
* - zero means dont exit, * - zero means dont exit,
* - one means exit, * - one means exit,
* - more than one means decrement and make another event loop * - more than one means decrement and make another event loop
* Status is checked in event_loop and decremented by one. * Status is checked in event_loop and decremented by one.
* When it reaches one the exit is made. * When it reaches one the exit is made.
@ -136,7 +143,7 @@ clixon_exit_get(void)
return _clicon_exit; return _clicon_exit;
} }
/*! If > 1 decrement exit counter /*! If > 1 decrement exit counter
*/ */
static int static int
clixon_exit_decr(void) clixon_exit_decr(void)
@ -174,17 +181,22 @@ clicon_sig_ignore_get(void)
/*! Register a callback function to be called on input on a file descriptor. /*! Register a callback function to be called on input on a file descriptor.
* *
* Prio is primitive, non-preemptive as follows:
* If several file events are active, then the prioritized are served first.
* If a non-prioritized is running, and a prioritized becomes active, then the
* running un-prioritized handler will run to completion (not pre-empted) and then
* the priorizited events will run.
* A timeout will always run.
* @param[in] fd File descriptor * @param[in] fd File descriptor
* @param[in] fn Function to call when input available on fd * @param[in] fn Function to call when input available on fd
* @param[in] arg Argument to function fn * @param[in] arg Argument to function fn
* @param[in] str Describing string for logging * @param[in] str Describing string for logging
* @param[in] prio Priority (0 or 1) * @param[in] prio Priority (0 or 1)
* @code * @code
* int fn(int fd, void *arg){ * static int fn(int fd, void *arg){}
* } * clixon_event_reg_fd(fd, fn, (void*)42, "call fn on input on fd", 0);
* clixon_event_reg_fd(fd, fn, (void*)42, "call fn on input on fd"); * @endcode
* @endcode * @see clixon_event_loop
* @see clixon_event_unreg_fd
*/ */
int int
clixon_event_reg_fd_prio(int fd, clixon_event_reg_fd_prio(int fd,
@ -200,18 +212,29 @@ clixon_event_reg_fd_prio(int fd,
return -1; return -1;
} }
memset(e, 0, sizeof(struct event_data)); memset(e, 0, sizeof(struct event_data));
strncpy(e->e_string, str, EVENT_STRLEN-1); strncpy(e->e_descr, str, EVENT_STRLEN-1);
e->e_fd = fd; e->e_fd = fd;
e->e_fn = fn; e->e_fn = fn;
e->e_arg = arg; e->e_arg = arg;
e->e_type = EVENT_FD; e->e_type = EVENT_FD;
e->e_prio = prio; if (prio){
e->e_next = ee; e->e_next = _ee_prio;
ee = e; _ee_prio = e;
clixon_debug(CLIXON_DBG_EVENT, "registering %s", e->e_string); _ee_prio_nr++;
}
else {
e->e_next = _ee;
_ee = e;
_ee_nr++;
}
clixon_debug(CLIXON_DBG_EVENT, "registering %s", e->e_descr);
return 0; return 0;
} }
/*! Register un-prioritized file event callback
*
* @see clixon_event_unreg_fd_prio
*/
int int
clixon_event_reg_fd(int fd, clixon_event_reg_fd(int fd,
int (*fn)(int, void*), int (*fn)(int, void*),
@ -228,6 +251,7 @@ clixon_event_reg_fd(int fd,
* @retval 0 OK * @retval 0 OK
* @retval -1 Error * @retval -1 Error
* Note: deregister when exactly function and socket match, not argument * Note: deregister when exactly function and socket match, not argument
* Consider adding prio to argument
* @see clixon_event_reg_fd * @see clixon_event_reg_fd
* @see clixon_event_unreg_timeout * @see clixon_event_unreg_timeout
*/ */
@ -239,17 +263,33 @@ clixon_event_unreg_fd(int s,
int found = 0; int found = 0;
struct event_data **e_prev; struct event_data **e_prev;
e_prev = &ee; /* First try prioritized */
for (e = ee; e; e = e->e_next){ e_prev = &_ee_prio;
for (e = _ee_prio; e; e = e->e_next){
if (fn == e->e_fn && s == e->e_fd) { if (fn == e->e_fn && s == e->e_fd) {
found++; found++;
*e_prev = e->e_next; *e_prev = e->e_next;
_ee_prio_nr--;
_ee_unreg++; _ee_unreg++;
free(e); free(e);
break; break;
} }
e_prev = &e->e_next; e_prev = &e->e_next;
} }
if (!found){
e_prev = &_ee;
for (e = _ee; e; e = e->e_next){
if (fn == e->e_fn && s == e->e_fd) {
found++;
*e_prev = e->e_next;
_ee_nr--;
_ee_unreg++;
free(e);
break;
}
e_prev = &e->e_next;
}
}
return found?0:-1; return found?0:-1;
} }
@ -268,9 +308,9 @@ clixon_event_unreg_fd(int s,
* t1.tv_sec = 1; t1.tv_usec = 0; * t1.tv_sec = 1; t1.tv_usec = 0;
* timeradd(&t, &t1, &t); * timeradd(&t, &t1, &t);
* clixon_event_reg_timeout(t, fn, NULL, "call every second"); * clixon_event_reg_timeout(t, fn, NULL, "call every second");
* } * }
* @endcode * @endcode
* *
* @note The timestamp is an absolute timestamp, not relative. * @note The timestamp is an absolute timestamp, not relative.
* @note The callback is not periodic, you need to make a new registration for each period, see example. * @note The callback is not periodic, you need to make a new registration for each period, see example.
* @note The first argument to fn is a dummy, just to get the same signature as for file-descriptor callbacks. * @note The first argument to fn is a dummy, just to get the same signature as for file-descriptor callbacks.
@ -278,7 +318,7 @@ clixon_event_unreg_fd(int s,
* @see clixon_event_unreg_timeout * @see clixon_event_unreg_timeout
*/ */
int int
clixon_event_reg_timeout(struct timeval t, clixon_event_reg_timeout(struct timeval t,
int (*fn)(int, void*), int (*fn)(int, void*),
void *arg, void *arg,
char *str) char *str)
@ -297,14 +337,14 @@ clixon_event_reg_timeout(struct timeval t,
return -1; return -1;
} }
memset(e, 0, sizeof(struct event_data)); memset(e, 0, sizeof(struct event_data));
strncpy(e->e_string, str, EVENT_STRLEN-1); strncpy(e->e_descr, str, EVENT_STRLEN-1);
e->e_fn = fn; e->e_fn = fn;
e->e_arg = arg; e->e_arg = arg;
e->e_type = EVENT_TIME; e->e_type = EVENT_TIME;
e->e_time = t; e->e_time = t;
/* Sort into right place */ /* Sort into right place */
e_prev = &ee_timers; e_prev = &_ee_timers;
for (e1=ee_timers; e1; e1=e1->e_next){ for (e1=_ee_timers; e1; e1=e1->e_next){
if (timercmp(&e->e_time, &e1->e_time, <)) if (timercmp(&e->e_time, &e1->e_time, <))
break; break;
e_prev = &e1->e_next; e_prev = &e1->e_next;
@ -337,8 +377,8 @@ clixon_event_unreg_timeout(int (*fn)(int, void*),
int found = 0; int found = 0;
struct event_data **e_prev; struct event_data **e_prev;
e_prev = &ee_timers; e_prev = &_ee_timers;
for (e = ee_timers; e; e = e->e_next){ for (e = _ee_timers; e; e = e->e_next){
if (fn == e->e_fn && arg == e->e_arg) { if (fn == e->e_fn && arg == e->e_arg) {
found++; found++;
*e_prev = e->e_next; *e_prev = e->e_next;
@ -353,254 +393,242 @@ clixon_event_unreg_timeout(int (*fn)(int, void*),
/*! Poll to see if there is any data available on this file descriptor. /*! Poll to see if there is any data available on this file descriptor.
* *
* @param[in] fd File descriptor * @param[in] fd File descriptor
* @retval 1 Something to read on fd * @retval >0 Nr of elements to read on fd
* @retval 0 Nothing to read/empty fd * @retval 0 Nothing to read/empty fd
* @retval -1 Error * @retval -1 Error
*/ */
#ifdef CLIXON_EVENT_POLL
int
clixon_event_poll(int fd) {
struct pollfd pfd;
int retval;
pfd.fd = fd;
pfd.events = POLLIN;
retval = poll(&pfd, 1, 0);
if (retval < 0) {
clixon_err(OE_EVENTS, errno, "poll");
}
return retval;
}
#else
int int
clixon_event_poll(int fd) clixon_event_poll(int fd)
{ {
int retval = -1; int retval = -1;
fd_set fdset; struct pollfd pfd = {0,};
struct timeval tnull = {0,}; int ret;
FD_ZERO(&fdset); pfd.fd = fd;
FD_SET(fd, &fdset); pfd.events = POLLIN;
if ((retval = select(FD_SETSIZE, &fdset, NULL, NULL, &tnull)) < 0) if ((ret = poll(&pfd, 1, 0)) < 0){
clixon_err(OE_EVENTS, errno, "select"); clixon_err(OE_EVENTS, errno, "poll");
goto done;
}
retval = ret;
done:
return retval;
}
/*! Handle signal interrupt
*
* Signals are in three classes:
* (1) Signals that exit gracefully, the function returns 0
* Must be registered such as by set_signal() of SIGTERM,SIGINT, etc with a
* handler that calls clicon_exit_set().
* (2) SIGCHILD Childs that exit(), go through clixon_proc list and cal waitpid
* New select loop is called
* (2) Signals are ignored, and the select is rerun, ie handler calls
* clicon_sig_ignore_get.
* New select loop is called
* (3) Other signals result in an error and return -1.
*/
static int
event_handle_eintr(clixon_handle h)
{
int retval = -1;
clixon_debug(CLIXON_DBG_EVENT, "poll/select %s", strerror(errno));
if (clixon_exit_get() == 1){
clixon_err(OE_EVENTS, errno, "poll/select");
goto exit;
}
else if (clicon_sig_child_get()){
/* Go through processes and wait for child processes */
if (clixon_process_waitpid(h) < 0)
goto done;
clicon_sig_child_set(0);
}
else if (clicon_sig_ignore_get()){
clicon_sig_ignore_set(0);
}
else{
clixon_err(OE_EVENTS, errno, "poll/select");
goto done;
}
retval = 1;
done:
return retval;
exit:
retval = 0;
goto done;
}
static int
event_handle_fds(struct event_data *ee,
int prio)
{
int retval = -1;
struct pollfd *pfd;
struct event_data *e = NULL;
for (e = ee; e; e = e->e_next) {
if (e->e_type != EVENT_FD)
continue;
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "check s:%d prio:%d fd %s", e->e_fd, prio, e->e_descr);
if ((pfd = e->e_pollfd) == NULL) /* Could be added after poll regitsration */
continue;
if (pfd->revents != 0) { /* returned events */
if (pfd->revents & POLLIN || pfd->revents & POLLHUP) {
clixon_debug(CLIXON_DBG_EVENT, "fd %s", e->e_descr);
_ee_unreg = 0;
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0) {
clixon_debug(CLIXON_DBG_EVENT, "Error in: %s", e->e_descr);
goto done;
}
if (_ee_unreg){ /* and this socket,... */
_ee_unreg = 0;
break;
}
if (prio == 0 && _ee_prio_nr > 0) /* Prioritized exists, break unprio fairness */
break;
}
else if (pfd->revents & POLLNVAL) { /* fd not open */
clixon_err(OE_EVENTS, 0, "poll: Invalid request: %s fd %d not open",
e->e_descr, pfd->fd);
goto done;
}
else {
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL,
"%s %d revents:0x%x", e->e_descr, pfd->fd, pfd->revents);
goto done;
}
}
}
retval = 0;
done:
return retval; return retval;
} }
#endif
/*! Dispatch file descriptor events (and timeouts) by invoking callbacks. /*! Dispatch file descriptor events (and timeouts) by invoking callbacks.
* *
* @param[in] h Clixon handle * @param[in] h Clixon handle
* @retval 0 OK * @retval 0 OK
* @retval -1 Error: eg select, callback, timer, * @retval -1 Error: eg select, callback, timer,
* @note There is an issue with fairness between timeouts and events * @note There is an issue with fairness between timeouts and events
* Currently a socket that is not read/emptied properly starve timeouts. * Currently a socket that is not read/emptied properly starve timeouts.
* One could try to poll the file descriptors after a timeout? * One could try to poll the file descriptors after a timeout?
* TODO: unreg, better prio algorithm
*/ */
#ifdef CLIXON_EVENT_POLL
int int
clixon_event_loop(clixon_handle h) clixon_event_loop(clixon_handle h)
{ {
struct event_data *e = NULL; int retval = -1;
struct pollfd fds[MAX_EVENTS]; struct event_data *e = NULL;
int retval = -1; struct pollfd *fds = NULL;
int nfds = 0; struct pollfd *pfd;
uint32_t nfds_max = 0;
int nfds = 0;
struct timeval t0;
struct timeval t;
int64_t tdiff;
int timeout;
int n;
int ret;
while (clixon_exit_get() != 1) { while (clixon_exit_get() != 1) {
nfds = _ee_prio_nr + _ee_nr;
if (nfds > nfds_max){
nfds_max = nfds;
if ((fds = realloc(fds, nfds_max*sizeof(struct pollfd))) == NULL){
clixon_err(OE_UNIX, errno, "realloc");
goto done;
}
}
nfds = 0; nfds = 0;
for (e = ee; e; e = e->e_next) { clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "register prio");
for (e = _ee_prio; e; e = e->e_next) {
if (e->e_type == EVENT_FD) { if (e->e_type == EVENT_FD) {
fds[nfds].fd = e->e_fd; pfd = &fds[nfds];
fds[nfds].events = POLLIN; pfd->fd = e->e_fd;
pfd->events = POLLIN; /* requested event */
e->e_pollfd = pfd;
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "register fd prio %s nr:%d",
e->e_descr, nfds);
nfds++; nfds++;
} }
} }
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "register unprio");
int timeout = -1; for (e = _ee; e; e = e->e_next) {
if (ee_timers != NULL) { if (e->e_type == EVENT_FD) {
struct timeval t0, t; pfd = &fds[nfds];
gettimeofday(&t0, NULL); pfd->fd = e->e_fd;
timersub(&ee_timers->e_time, &t0, &t); pfd->events = POLLIN; /* requested event */
timeout = t.tv_sec * 1000 + t.tv_usec / 1000; e->e_pollfd = pfd;
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "register fd %s nr:%d",
e->e_descr, nfds);
nfds++;
}
} }
if (nfds != _ee_nr + _ee_prio_nr){
int n = poll(fds, nfds, timeout); clixon_err(OE_EVENTS, 0, "File descriptor mismatch");
goto done;
}
timeout = -1;
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "timeout");
if (_ee_timers != NULL) {
gettimeofday(&t0, NULL);
timersub(&_ee_timers->e_time, &t0, &t);
tdiff = t.tv_sec * 1000 + t.tv_usec / 1000;
if (tdiff < 0)
timeout = 0;
else
timeout = (int)tdiff;
}
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "poll timeout: %d", timeout);
n = poll(fds, nfds, timeout);
if (n == -1) { if (n == -1) {
if (errno == EINTR) { clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "n=-1 Error");
if (errno == EINTR){
if (clixon_exit_get() == 1){
clixon_err(OE_EVENTS, errno, "poll");
goto ok;
}
if ((ret = event_handle_eintr(h)) < 0)
goto done;
if (ret == 0){ // exit
retval = 0;
goto done;
}
continue; continue;
} }
clixon_err(OE_EVENTS, errno, "poll"); else
goto err; clixon_err(OE_EVENTS, errno, "poll");
goto done;
} }
if (n == 0) { /* timeout */
if (n == 0) { clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "n=0 Timeout");
// Таймаут e = _ee_timers;
e = ee_timers; _ee_timers = _ee_timers->e_next;
ee_timers = ee_timers->e_next; clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "timeout: %s", e->e_descr);
if ((*e->e_fn)(0, e->e_arg) < 0) { if ((*e->e_fn)(0, e->e_arg) < 0) {
free(e); free(e);
goto err; goto done;
}
free(e);
continue;
}
for (int i = 0; i < nfds; i++) {
if (fds[i].revents & POLLIN) {
for (e = ee; e; e = e->e_next) {
if (e->e_type == EVENT_FD && e->e_fd == fds[i].fd) {
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0) {
goto err;
}
break;
}
}
}
}
}
retval = 0;
err:
return retval;
}
#else
int
clixon_event_loop(clixon_handle h)
{
struct event_data *e;
int n;
struct timeval t;
struct timeval t0;
struct timeval tnull = {0,};
fd_set fdset;
int retval = -1;
struct event_data *e_next;
while (clixon_exit_get() != 1){
FD_ZERO(&fdset);
if (clicon_sig_child_get()){
/* Go through processes and wait for child processes */
if (clixon_process_waitpid(h) < 0)
goto err;
clicon_sig_child_set(0);
}
for (e=ee; e; e=e->e_next)
if (e->e_type == EVENT_FD)
FD_SET(e->e_fd, &fdset);
if (ee_timers != NULL){
gettimeofday(&t0, NULL);
timersub(&ee_timers->e_time, &t0, &t);
if (t.tv_sec < 0)
n = select(FD_SETSIZE, &fdset, NULL, NULL, &tnull);
else
n = select(FD_SETSIZE, &fdset, NULL, NULL, &t);
}
else
n = select(FD_SETSIZE, &fdset, NULL, NULL, NULL);
if (clixon_exit_get() == 1){
break;
}
if (n == -1) {
if (errno == EINTR){
/* Signals are checked and are in three classes:
* (1) Signals that exit gracefully, the function returns 0
* Must be registered such as by set_signal() of SIGTERM,SIGINT, etc with a handler that calls
* clicon_exit_set().
* (2) SIGCHILD Childs that exit(), go through clixon_proc list and cal waitpid
* New select loop is called
* (2) Signals are ignored, and the select is rerun, ie handler calls clicon_sig_ignore_get
* New select loop is called
* (3) Other signals result in an error and return -1.
*/
clixon_debug(CLIXON_DBG_EVENT, "select: %s", strerror(errno));
if (clixon_exit_get() == 1){
clixon_err(OE_EVENTS, errno, "select");
retval = 0;
}
else if (clicon_sig_child_get()){
/* Go through processes and wait for child processes */
if (clixon_process_waitpid(h) < 0)
goto err;
clicon_sig_child_set(0);
continue;
}
else if (clicon_sig_ignore_get()){
clicon_sig_ignore_set(0);
continue;
}
else
clixon_err(OE_EVENTS, errno, "select");
}
else
clixon_err(OE_EVENTS, errno, "select");
goto err;
}
if (n==0){ /* Timeout */
e = ee_timers;
ee_timers = ee_timers->e_next;
clixon_debug(CLIXON_DBG_EVENT | CLIXON_DBG_DETAIL, "timeout: %s", e->e_string);
if ((*e->e_fn)(0, e->e_arg) < 0){
free(e);
goto err;
} }
free(e); free(e);
} }
_ee_unreg = 0; /* Prio files */
if (clicon_option_bool(h, "CLICON_SOCK_PRIO")){ if ((ret = event_handle_fds(_ee_prio, 1)) < 0)
for (e=ee; e; e=e_next) { goto done;
if (clixon_exit_get() == 1) /* Unprio files */
break; if ((ret = event_handle_fds(_ee, 0)) < 0)
e_next = e->e_next; goto done;
if (e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset) && e->e_prio){
clixon_debug(CLIXON_DBG_EVENT, "FD_ISSET: %s prio:%d", e->e_string, e->e_prio);
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0){
clixon_debug(CLIXON_DBG_EVENT, "Error in: %s", e->e_string);
goto err;
}
if (_ee_unreg){
_ee_unreg = 0;
break;
}
}
}
}
/* Unprio
* Note that without prio, round-robin fairness is ensured, not with prio */
for (e=ee; e; e=e_next){
if (clixon_exit_get() == 1)
break;
e_next = e->e_next;
if (e->e_type == EVENT_FD && FD_ISSET(e->e_fd, &fdset) && e->e_prio==0){
clixon_debug(CLIXON_DBG_EVENT, "FD_ISSET: %s", e->e_string);
if ((*e->e_fn)(e->e_fd, e->e_arg) < 0){
clixon_debug(CLIXON_DBG_EVENT, "Error in: %s", e->e_string);
goto err;
}
if (_ee_unreg){
_ee_unreg = 0;
break;
}
if (clicon_option_bool(h, "CLICON_SOCK_PRIO"))
break;
}
}
clixon_exit_decr(); /* If exit is set and > 1, decrement it (and exit when 1) */ clixon_exit_decr(); /* If exit is set and > 1, decrement it (and exit when 1) */
continue; }
err: ok:
clixon_debug(CLIXON_DBG_EVENT, "err");
break;
}
if (clixon_exit_get() == 1) if (clixon_exit_get() == 1)
retval = 0; retval = 0;
done:
clixon_debug(CLIXON_DBG_EVENT, "retval:%d", retval); clixon_debug(CLIXON_DBG_EVENT, "retval:%d", retval);
if (fds)
free(fds);
return retval; return retval;
} }
#endif
int int
clixon_event_exit(void) clixon_event_exit(void)
@ -608,17 +636,25 @@ clixon_event_exit(void)
struct event_data *e; struct event_data *e;
struct event_data *e_next; struct event_data *e_next;
e_next = ee; e_next = _ee_prio;
while ((e = e_next) != NULL){ while ((e = e_next) != NULL){
e_next = e->e_next; e_next = e->e_next;
free(e); free(e);
} }
ee = NULL; _ee_prio = NULL;
e_next = ee_timers;
e_next = _ee;
while ((e = e_next) != NULL){ while ((e = e_next) != NULL){
e_next = e->e_next; e_next = e->e_next;
free(e); free(e);
} }
ee_timers = NULL; _ee = NULL;
e_next = _ee_timers;
while ((e = e_next) != NULL){
e_next = e->e_next;
free(e);
}
_ee_timers = NULL;
return 0; return 0;
} }