Skip to content

Commit

Permalink
signal: new signal handling backend based on signalfd
Browse files Browse the repository at this point in the history
Linux-specific signal handling backend based on signalfd(2)
system call, and public function event_base_get_signal_method()
to obtain an underlying kernel signal handling mechanism.

Signed-off-by: Dmitry Antipov <[email protected]>
  • Loading branch information
dmantipov authored and azat committed Nov 12, 2022
1 parent 9e34693 commit 1af745d
Show file tree
Hide file tree
Showing 14 changed files with 306 additions and 23 deletions.
6 changes: 6 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,7 @@ else()
sys/wait.h
sys/resource.h
sys/timerfd.h
sys/signalfd.h
netinet/in.h
netinet/in6.h
netinet/tcp.h
Expand Down Expand Up @@ -583,6 +584,7 @@ list(APPEND CMAKE_EXTRA_INCLUDE_FILES ${EVENT_INCLUDES} stdio.h)
CHECK_SYMBOLS_EXIST("${SYMBOLS_TO_CHECK}" "${CMAKE_EXTRA_INCLUDE_FILES}" "EVENT")
unset(SYMBOLS_TO_CHECK)
set(EVENT__HAVE_EPOLL ${EVENT__HAVE_EPOLL_CREATE})
set(EVENT__HAVE_SIGNALFD ${EVENT__HAVE_SYS_SIGNALFD_H})
if(WIN32 AND NOT CYGWIN)
set(EVENT__HAVE_WEPOLL 1)
endif()
Expand Down Expand Up @@ -904,6 +906,10 @@ if(EVENT__HAVE_EPOLL)
list(APPEND SRC_CORE epoll.c)
endif()

if(EVENT__HAVE_SIGNALFD)
list(APPEND SRC_CORE signalfd.c)
endif()

if(EVENT__HAVE_WEPOLL)
list(APPEND SRC_CORE
epoll.c
Expand Down
3 changes: 3 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,9 @@ endif
if SIGNAL_SUPPORT
SYS_SRC += signal.c
endif
if SIGNALFD_SUPPORT
SYS_SRC += signalfd.c
endif

BUILT_SOURCES += include/event2/event-config.h

Expand Down
3 changes: 2 additions & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ LIBEVENT_OPENSSL
LIBEVENT_MBEDTLS

dnl Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h ifaddrs.h mach/mach_time.h mach/mach.h netdb.h netinet/in.h netinet/in6.h netinet/tcp.h sys/un.h poll.h port.h stdarg.h stddef.h sys/devpoll.h sys/epoll.h sys/event.h sys/eventfd.h sys/ioctl.h sys/mman.h sys/param.h sys/queue.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/stat.h sys/time.h sys/timerfd.h sys/uio.h sys/wait.h sys/random.h errno.h afunix.h])
AC_CHECK_HEADERS([arpa/inet.h fcntl.h ifaddrs.h mach/mach_time.h mach/mach.h netdb.h netinet/in.h netinet/in6.h netinet/tcp.h sys/un.h poll.h port.h stdarg.h stddef.h sys/devpoll.h sys/epoll.h sys/event.h sys/eventfd.h sys/ioctl.h sys/mman.h sys/param.h sys/queue.h sys/resource.h sys/select.h sys/sendfile.h sys/socket.h sys/stat.h sys/time.h sys/timerfd.h sys/signalfd.h sys/uio.h sys/wait.h sys/random.h errno.h afunix.h])

case "${host_os}" in
linux*) ;;
Expand Down Expand Up @@ -543,6 +543,7 @@ if test "$bwin32" = "true"; then
fi
AM_CONDITIONAL(WEPOLL_BACKEND, [test "$havewepoll" = "yes"])
AM_CONDITIONAL(SIGNAL_SUPPORT, [test "$needsignal" = "yes"])
AM_CONDITIONAL(SIGNALFD_SUPPORT, [test "$ac_cv_header_sys_signalfd_h" = "yes"])

AC_TYPE_PID_T
AC_TYPE_SIZE_T
Expand Down
3 changes: 2 additions & 1 deletion epoll.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,7 +255,8 @@ epoll_init(struct event_base *base)
}
#endif

evsig_init_(base);
if (sigfd_init_(base) < 0)
evsig_init_(base);

return (epollop);
}
Expand Down
3 changes: 3 additions & 0 deletions event-config.h.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,9 @@
/* Define to 1 if you have the <sys/timerfd.h> header file. */
#cmakedefine EVENT__HAVE_SYS_TIMERFD_H 1

/* Define to 1 if you have the <sys/signalfd.h> header file. */
#cmakedefine EVENT__HAVE_SYS_SIGNALFD_H 1

/* Define to 1 if you have the <sys/time.h> header file. */
#cmakedefine EVENT__HAVE_SYS_TIME_H 1

Expand Down
7 changes: 7 additions & 0 deletions event.c
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,13 @@ event_base_get_method(const struct event_base *base)
return (base->evsel->name);
}

const char *
event_base_get_signal_method(const struct event_base *base)
{
EVUTIL_ASSERT(base);
return (base->evsigsel->name);
}

/** Callback: used to implement event_base_loopexit by telling the event_base
* that it's time to exit its loop. */
static void
Expand Down
5 changes: 3 additions & 2 deletions evmap.c
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ evmap_signal_add_(struct event_base *base, int sig, struct event *ev)
base->evsigsel->fdinfo_len);

if (LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, NULL)
if (evsel->add(base, ev->ev_fd, 0, EV_SIGNAL, ev)
== -1)
return (-1);
}
Expand Down Expand Up @@ -643,7 +643,8 @@ evmap_signal_reinit_iter_fn(struct event_base *base,
int *result = arg;

if (!LIST_EMPTY(&ctx->events)) {
if (evsel->add(base, signum, 0, EV_SIGNAL, NULL) == -1)
if (evsel->add(base, signum, 1, EV_SIGNAL,
LIST_FIRST(&ctx->events)) == -1)
*result = -1;
}
return 0;
Expand Down
13 changes: 13 additions & 0 deletions evsignal-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ struct evsig_info {
int ev_signal_added;
/* Count of the number of signals we're currently watching. */
int ev_n_signals_added;
#ifdef EVENT__HAVE_SYS_SIGNALFD_H
/* EV_READ events used to wakeup corresponding EV_SIGNAL ones. */
struct event *ev_sigevent[NSIG];
#endif /* EVENT__HAVE_SYS_SIGNALFD_H */

/* Array of previous signal handler objects before Libevent started
* messing with them. Used to restore old signal handlers. */
Expand All @@ -56,8 +60,17 @@ struct evsig_info {
/* Size of sh_old. */
int sh_old_max;
};

#ifdef EVENT__HAVE_SYS_SIGNALFD_H
int sigfd_init_(struct event_base *);
#else /* no signalfd() */
static inline int
sigfd_init_(struct event_base *base) { return -1; }
#endif /* have signalfd() */

int evsig_init_(struct event_base *);
void evsig_dealloc_(struct event_base *);
int evsig_ensure_saved_(struct evsig_info *, int);

void evsig_set_base_(struct event_base *base);
void evsig_free_globals_(void);
Expand Down
16 changes: 16 additions & 0 deletions include/event2/event.h
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,18 @@ int event_base_dispatch(struct event_base *base);
EVENT2_EXPORT_SYMBOL
const char *event_base_get_method(const struct event_base *eb);

/**
Get the kernel signal handling mechanism used by Libevent.
@param eb the event_base structure returned by event_base_new()
@return a string identifying the kernel signal handling mechanism,
which is "signal" for traditional UNIX signal handlers,
"kqueue_signal" for kqueue(2)-based method on *BSD and macOS,
and "signalfd_signal" for Linux-only signalfd(2)-based method.
*/
EVENT2_EXPORT_SYMBOL
const char *event_base_get_signal_method(const struct event_base *eb);

/**
Gets all event notification mechanisms supported by Libevent.
Expand Down Expand Up @@ -586,6 +598,10 @@ enum event_base_config_flag {
epoll and if you do not have EVENT_BASE_FLAG_PRECISE_TIMER enabled.
*/
EVENT_BASE_FLAG_EPOLL_DISALLOW_TIMERFD = 0x40,

/** Do not use signalfd(2) to handle signals even if supported.
*/
EVENT_BASE_FLAG_DISALLOW_SIGNALFD = 0x80,
};

/**
Expand Down
3 changes: 2 additions & 1 deletion poll.c
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,8 @@ poll_init(struct event_base *base)
if (!(pollop = mm_calloc(1, sizeof(struct pollop))))
return (NULL);

evsig_init_(base);
if (sigfd_init_(base) < 0)
evsig_init_(base);

evutil_weakrand_seed_(&base->weakrand_seed, 0);

Expand Down
3 changes: 2 additions & 1 deletion select.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,8 @@ select_init(struct event_base *base)
return (NULL);
}

evsig_init_(base);
if (sigfd_init_(base) < 0)
evsig_init_(base);

evutil_weakrand_seed_(&base->weakrand_seed, 0);

Expand Down
39 changes: 23 additions & 16 deletions signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -208,25 +208,13 @@ evsig_init_(struct event_base *base)
return 0;
}

/* Helper: set the signal handler for evsignal to handler in base, so that
* we can restore the original handler when we clear the current one. */
/* Helper: resize saved signal handler array up to the highest signal
number. A dynamic array is used to keep footprint on the low side. */
int
evsig_set_handler_(struct event_base *base,
int evsignal, void (__cdecl *handler)(int))
evsig_ensure_saved_(struct evsig_info *sig, int evsignal)
{
#ifdef EVENT__HAVE_SIGACTION
struct sigaction sa;
#else
ev_sighandler_t sh;
#endif
struct evsig_info *sig = &base->sig;
void *p;

/*
* resize saved signal handler array up to the highest signal number.
* a dynamic array is used to keep footprint on the low side.
*/
if (evsignal >= sig->sh_old_max) {
void *p;
int new_max = evsignal + 1;
event_debug(("%s: evsignal (%d) >= sh_old_max (%d), resizing",
__func__, evsignal, sig->sh_old_max));
Expand All @@ -242,6 +230,25 @@ evsig_set_handler_(struct event_base *base,
sig->sh_old_max = new_max;
sig->sh_old = p;
}
return 0;
}

/* Helper: set the signal handler for evsignal to handler in base, so that
* we can restore the original handler when we clear the current one. */
int
evsig_set_handler_(struct event_base *base,
int evsignal, void (__cdecl *handler)(int))
{
#ifdef EVENT__HAVE_SIGACTION
struct sigaction sa;
#else
ev_sighandler_t sh;
#endif
struct evsig_info *sig = &base->sig;

/* ensure saved array is large enough */
if (evsig_ensure_saved_(sig, evsignal) < 0)
return (-1);

/* allocate space for previous handler out of dynamic array */
sig->sh_old[evsignal] = mm_malloc(sizeof *sig->sh_old[evsignal]);
Expand Down
Loading

0 comments on commit 1af745d

Please sign in to comment.