From fc127fd297e5a320f51853b88a7259a5b0baa8b6 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 9 Sep 2018 08:32:37 -0600 Subject: [PATCH] sched/signal: Add a generic signal notification facility. Modify the custom IOB available notifier so that it is now just a wrapper around this generic signal notification. This generic signal notification faility will, eventually, be used to support network polling. Squashed commit of the following: mm/iob: The IOB available notifier is now just a wrapper around the common signal notifier. sched/signal: Add a generic signal notification facility. sched/signal/sig_evthread.c: More trivial naming changes. sched/signal: Rename nxsig_notification() to nxsig_evthread() to make forthcoming naming additions more consistent. --- fs/aio/aio_signal.c | 4 +- include/nuttx/mm/iob.h | 27 +- include/nuttx/signal.h | 102 +++- libs/libc/aio/lio_listio.c | 4 +- mm/iob/Kconfig | 12 +- mm/iob/Make.defs | 2 +- mm/iob/iob.h | 19 +- mm/iob/iob_free.c | 2 +- mm/iob/iob_initialize.c | 6 - mm/iob/iob_notifier.c | 151 ++++++ mm/iob/iob_notify.c | 394 -------------- sched/Kconfig | 20 + sched/mqueue/mq_sndinternal.c | 2 +- sched/signal/Make.defs | 6 +- .../{sig_notification.c => sig_evthread.c} | 23 +- sched/signal/sig_initialize.c | 9 +- sched/signal/sig_notifier.c | 492 ++++++++++++++++++ sched/signal/signal.h | 9 +- sched/timer/timer_settime.c | 2 +- 19 files changed, 820 insertions(+), 466 deletions(-) create mode 100644 mm/iob/iob_notifier.c delete mode 100644 mm/iob/iob_notify.c rename sched/signal/{sig_notification.c => sig_evthread.c} (91%) create mode 100644 sched/signal/sig_notifier.c diff --git a/fs/aio/aio_signal.c b/fs/aio/aio_signal.c index be46e11169..c1a50974ec 100644 --- a/fs/aio/aio_signal.c +++ b/fs/aio/aio_signal.c @@ -111,10 +111,10 @@ int aio_signal(pid_t pid, FAR struct aiocb *aiocbp) else if (aiocbp->aio_sigevent.sigev_notify == SIGEV_THREAD) { - ret = nxsig_notification(pid, &aiocbp->aio_sigevent); + ret = nxsig_evthread(pid, &aiocbp->aio_sigevent); if (ret < 0) { - ferr("ERROR: nxsig_notification failed: %d\n", ret); + ferr("ERROR: nxsig_evthread failed: %d\n", ret); } } #endif diff --git a/include/nuttx/mm/iob.h b/include/nuttx/mm/iob.h index fd904ff22a..ae610f9bdd 100644 --- a/include/nuttx/mm/iob.h +++ b/include/nuttx/mm/iob.h @@ -205,7 +205,7 @@ int iob_navail(void); FAR struct iob_s *iob_free(FAR struct iob_s *iob); /**************************************************************************** - * Name: iob_notify_setup + * Name: iob_notifier_setup * * Description: * Set up to notify the specified PID with the provided signal number. @@ -220,14 +220,11 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob); * signo - The signal number to use with the notification. * * Returned Value: - * > 0 - There are already free IOBs and this this number of free IOBs - * (CAUTION: This value is volatile). No signal notification - * will be provided. - * == 0 - There are no free IOBs. A signal will be sent to 'pid' when - * at least one IOB is available. That IOB is *not* reserved for - * the caller. Hence, due to race conditions, it could be taken - * by some other task. In that event, the caller should call - * sig_notify again. + * > 0 - The signal notification is in place. The returned value is a + * key that may be used later in a call to + * iob_notifier_teardown(). + * == 0 - There are already free IOBs. No signal notification will be + * provided. * < 0 - An unexpected error occurred and no signal will be sent. The * returned value is a negated errno value that indicates the * nature of the failure. @@ -235,21 +232,21 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob); ****************************************************************************/ #ifdef CONFIG_IOB_NOTIFIER -int iob_notify_setup(int pid, int signo); +int iob_notifier_setup(int pid, int signo); #endif /**************************************************************************** - * Name: iob_notify_teardown + * Name: iob_notifier_teardown * * Description: - * Eliminate an IOB notification previously setup by iob_notify_setup(). + * Eliminate an IOB notification previously setup by iob_notifier_setup(). * This function should only be called if the notification should be * aborted prior to the notification. The notification will automatically * be torn down after the signal is sent. * * Input Parameters: - * pid - The PID whose notification will be torn down. If a zero value - * is provided, then the PID of the calling thread will be used. + * key - The key value returned from a previous call to + * iob_notifier_setup(). * * Returned Value: * Zero (OK) is returned on success; a negated errno value is returned on @@ -258,7 +255,7 @@ int iob_notify_setup(int pid, int signo); ****************************************************************************/ #ifdef CONFIG_IOB_NOTIFIER -int iob_notify_teardown(int pid, int signo); +int iob_notifier_teardown(int key); #endif /**************************************************************************** diff --git a/include/nuttx/signal.h b/include/nuttx/signal.h index 903b4a9dfd..3710f0ea1d 100644 --- a/include/nuttx/signal.h +++ b/include/nuttx/signal.h @@ -46,6 +46,22 @@ #include #include +/**************************************************************************** + * Public Types + ****************************************************************************/ + +/* This is an enumeration of the various signal events that may be + * notified via nxig_notifier_signal(). + */ + +enum nxsig_evtype_e +{ + NXSIG_IOB_AVAIL = 1, /* Signal availability of an IOB */ + NXSIG_NET_DOWN, /* Signal that the network is down */ + NXSIG_TCP_READAHEAD, /* Signal that TCP read-ahead data is available */ + NXSIG_UDP_READAHEAD, /* Signal that TCP read-ahead data is available */ +}; + /**************************************************************************** * Public Function Prototypes ****************************************************************************/ @@ -431,7 +447,7 @@ unsigned int nxsig_sleep(unsigned int seconds); int nxsig_usleep(useconds_t usec); /**************************************************************************** - * Name: nxsig_notification + * Name: nxsig_evthread * * Description: * Notify a client a signal event via a function call. This function is @@ -451,7 +467,89 @@ int nxsig_usleep(useconds_t usec); ****************************************************************************/ #if defined(CONFIG_SIG_EVTHREAD) && defined(CONFIG_BUILD_FLAT) -int nxsig_notification(pid_t pid, FAR struct sigevent *event); +int nxsig_evthread(pid_t pid, FAR struct sigevent *event); +#endif + +/**************************************************************************** + * Name: nxsig_notifier_setup + * + * Description: + * Set up to notify the specified PID with the provided signal number. + * + * NOTE: To avoid race conditions, the caller should set the sigprocmask + * to block signal delivery. The signal will be delivered once the + * signal is removed from the sigprocmask. + * + * Input Parameters: + * pid - The PID to be notified. If a zero value is provided, + * then the PID of the calling thread will be used. + * signo - The signal number to use with the notification. + * evtype - The event type. + * qualifier - Event qualifier to distinguish different cases of the + * generic event type. + * + * Returned Value: + * > 0 - The key which may be used later in a call to + * nxsig_notifier_teardown(). + * == 0 - Not used (reserved for wrapper functions). + * < 0 - An unexpected error occurred and no signal will be sent. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SIG_NOTIFIER +int nxsig_notifier_setup(int pid, int signo, enum nxsig_evtype_e evtype, + FAR void *qualifier); +#endif + +/**************************************************************************** + * Name: nxsig_notifier_teardown + * + * Description: + * Eliminate a notification previously setup by nxsig_notifier_setup(). + * This function should only be called if the notification should be + * aborted prior to the notification. The notification will automatically + * be torn down after the signal is sent. + * + * Input Parameters: + * key - The key value returned from a previous call to + * nxsig_notifier_setup(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +#ifdef CONFIG_SIG_NOTIFIER +int nxsig_notifier_teardown(int key); +#endif + +/**************************************************************************** + * Name: nxsig_notifier_signal + * + * Description: + * An event has just occurred. Signal all threads waiting for that event. + * + * When an IOB becomes available, *all* of the waiters in this thread will + * be signaled. If there are multiple waiters for a resource then only + * the highest priority thread will get the resource. Lower priority + * threads will need to call nxsig_notify once again. + * + * Input Parameters: + * evtype - The type of the event that just occurred. + * qualifier - Event qualifier to distinguish different cases of the + * generic event type. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +#ifdef CONFIG_SIG_NOTIFIER +void nxsig_notifier_signal(enum nxsig_evtype_e evtype, + FAR void *qualifier); #endif #endif /* __INCLUDE_NUTTX_SIGNAL_H */ diff --git a/libs/libc/aio/lio_listio.c b/libs/libc/aio/lio_listio.c index f6f8572f38..89f9f57354 100644 --- a/libs/libc/aio/lio_listio.c +++ b/libs/libc/aio/lio_listio.c @@ -207,7 +207,7 @@ static void lio_sighandler(int signo, siginfo_t *info, void *ucontext) else if (ighand->sig->sigev_notify == SIGEV_THREAD) { - DEBUGASSERT(nxsig_notification(sighand->pid, &sighand->sig)); + DEBUGASSERT(nxsig_evthread(sighand->pid, &sighand->sig)); } #endif @@ -713,7 +713,7 @@ int lio_listio(int mode, FAR struct aiocb *const list[], int nent, else if (sig && sig->sigev_notify == SIGEV_THREAD) { - status = nxsig_notification(sighand->pid, &sighand->sig); + status = nxsig_evthread(sighand->pid, &sighand->sig); if (status < 0 && ret == OK) { /* Something bad happened while performing the notification diff --git a/mm/iob/Kconfig b/mm/iob/Kconfig index aa78d9d216..1689c6178f 100644 --- a/mm/iob/Kconfig +++ b/mm/iob/Kconfig @@ -62,23 +62,13 @@ config IOB_THROTTLE config IOB_NOTIFIER bool "Support IOB notifications" default n + select SIG_NOTIFIER ---help--- Enable building of IOB notifier logic that will send a signla to a kernel thread when an IOB is available. This is is a general purpose notifier, but was developed specifically to support poll() logic where the poll must wait for an IOB to become available. -config IOB_NWAITERS - int "Number of IOB waiters" - default 4 - range 1 32767 - depends on IOB_NOTIFIER - ---help--- - If CONFIG_IOB_NOTIFIER is selected, then a static array will be - allocated to hold information about pending notifications. This - then determines an upper limit for the number of waiters that can - be supported. - config IOB_DEBUG bool "Force I/O buffer debug" default n diff --git a/mm/iob/Make.defs b/mm/iob/Make.defs index e709db759c..7b4add3e1d 100644 --- a/mm/iob/Make.defs +++ b/mm/iob/Make.defs @@ -45,7 +45,7 @@ CSRCS += iob_trimhead.c iob_trimhead_queue.c iob_trimtail.c CSRCS += iob_navail.c ifeq ($(CONFIG_IOB_NOTIFIER),y) - CSRCS += iob_notify.c + CSRCS += iob_notifier.c endif ifeq ($(CONFIG_DEBUG_FEATURES),y) diff --git a/mm/iob/iob.h b/mm/iob/iob.h index 612fed8c15..ad640d3037 100644 --- a/mm/iob/iob.h +++ b/mm/iob/iob.h @@ -155,19 +155,7 @@ FAR struct iob_qentry_s *iob_tryalloc_qentry(void); FAR struct iob_qentry_s *iob_free_qentry(FAR struct iob_qentry_s *iobq); /**************************************************************************** - * Name: iob_notify_initialize - * - * Description: - * Set up the notification structure for normal operation. - * - ****************************************************************************/ - -#ifdef CONFIG_IOB_NOTIFIER -void iob_notify_initialize(void); -#endif - -/**************************************************************************** - * Name: iob_notify_signal + * Name: iob_notifier_signal * * Description: * An IOB has become available. Signal all threads waiting for an IOB @@ -176,7 +164,7 @@ void iob_notify_initialize(void); * When an IOB becomes available, *all* of the waiters in this thread will * be signaled. If there are multiple waiters then only the highest * priority thread will get the IOB. Lower priority threads will need to - * call iob_notify once again. + * call iob_notifier_setup() once again. * * Input Parameters: * None. @@ -187,9 +175,8 @@ void iob_notify_initialize(void); ****************************************************************************/ #ifdef CONFIG_IOB_NOTIFIER -void iob_notify_signal(void); +void iob_notifier_signal(void); #endif #endif /* CONFIG_MM_IOB */ #endif /* __MM_IOB_IOB_H */ - diff --git a/mm/iob/iob_free.c b/mm/iob/iob_free.c index 193a993673..18ab49c085 100644 --- a/mm/iob/iob_free.c +++ b/mm/iob/iob_free.c @@ -148,7 +148,7 @@ FAR struct iob_s *iob_free(FAR struct iob_s *iob) * when an IOB becomes available. */ - iob_notify_signal(); + iob_notifier_signal(); } #endif diff --git a/mm/iob/iob_initialize.c b/mm/iob/iob_initialize.c index bd79ba1c16..f0d54fa736 100644 --- a/mm/iob/iob_initialize.c +++ b/mm/iob/iob_initialize.c @@ -155,12 +155,6 @@ void iob_initialize(void) nxsem_init(&g_qentry_sem, 0, CONFIG_IOB_NCHAINS); #endif -#ifdef CONFIG_IOB_NOTIFIER - /* Initialize the IOB notification data structures */ - - iob_notify_initialize(); -#endif - initialized = true; } } diff --git a/mm/iob/iob_notifier.c b/mm/iob/iob_notifier.c new file mode 100644 index 0000000000..d126d1e9bc --- /dev/null +++ b/mm/iob/iob_notifier.c @@ -0,0 +1,151 @@ +/**************************************************************************** + * mm/iob/iob_notifier.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include + +#include +#include + +#include "iob.h" + +#ifdef CONFIG_IOB_NOTIFIER + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: iob_notifier_setup + * + * Description: + * Set up to notify the specified PID with the provided signal number. + * + * NOTE: To avoid race conditions, the caller should set the sigprocmask + * to block signal delivery. The signal will be delivered once the + * signal is removed from the sigprocmask. + * + * Input Parameters: + * pid - The PID to be notified. If a zero value is provided, then the + * PID of the calling thread will be used. + * signo - The signal number to use with the notification. + * + * Returned Value: + * > 0 - The signal notification is in place. The returned value is a + * key that may be used later in a call to + * iob_notifier_teardown(). + * == 0 - There are already free IOBs. No signal notification will be + * provided. + * < 0 - An unexpected error occurred and no signal will be sent. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +int iob_notifier_setup(int pid, int signo) +{ + /* If there are already free IOBs, then return zero without setting up the + * notification. + */ + + if (iob_navail() > 0) + { + return 0; + } + + /* Otherwise, this is just a simple wrapper around nxsig_notifer_setup(). */ + + return nxsig_notifier_setup(pid, signo, NXSIG_IOB_AVAIL, NULL); +} + +/**************************************************************************** + * Name: iob_notifier_teardown + * + * Description: + * Eliminate an IOB notification previously setup by iob_notifier_setup(). + * This function should only be called if the notification should be + * aborted prior to the notification. The notification will automatically + * be torn down after the signal is sent. + * + * Input Parameters: + * key - The key value returned from a previous call to + * iob_notifier_setup(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int iob_notifier_teardown(int key) +{ + /* This is just a simple wrapper around nxsig_notifier_teardown(). */ + + return nxsig_notifier_teardown(key); +} + +/**************************************************************************** + * Name: iob_notifier_signal + * + * Description: + * An IOB has become available. Signal all threads waiting for an IOB + * that an IOB is available. + * + * When an IOB becomes available, *all* of the waiters in this thread will + * be signaled. If there are multiple waiters then only the highest + * priority thread will get the IOB. Lower priority threads will need to + * call iob_notifier_setup() once again. + * + * Input Parameters: + * None. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void iob_notifier_signal(void) +{ + /* This is just a simple wrapper around nxsig_notifier_signal(). */ + + return nxsig_notifier_signal(NXSIG_IOB_AVAIL, NULL); +} + +#endif /* CONFIG_IOB_NOTIFIER */ diff --git a/mm/iob/iob_notify.c b/mm/iob/iob_notify.c deleted file mode 100644 index 5f9ce47151..0000000000 --- a/mm/iob/iob_notify.c +++ /dev/null @@ -1,394 +0,0 @@ -/**************************************************************************** - * mm/iob/iob_notify.c - * - * Copyright (C) 2018 Gregory Nutt. All rights reserved. - * Author: Gregory Nutt - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in - * the documentation and/or other materials provided with the - * distribution. - * 3. Neither the name NuttX nor the names of its contributors may be - * used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS - * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED - * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - * POSSIBILITY OF SUCH DAMAGE. - * - ****************************************************************************/ - -/**************************************************************************** - * Included Files - ****************************************************************************/ - -#include - -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include - -#include "iob.h" - -#ifdef CONFIG_IOB_NOTIFIER - -/**************************************************************************** - * Private Types - ****************************************************************************/ - -/* This is the saved information for one IOB notification */ - -struct iob_notify_s -{ - FAR struct iob_notify_s *flink; /* Supports a singly linked list */ - uint8_t signo; /* The signal number for notification */ - pid_t pid; /* The PID to be notified */ -}; - -/**************************************************************************** - * Private Data - ****************************************************************************/ - -/* This is a statically allocated pool of notification structures */ - -static struct iob_notify_s g_iobnotify_pool[CONFIG_IOB_NWAITERS]; - -/* This is a list of free notification structures */ - -static FAR struct iob_notify_s *g_iobnotify_free; - -/* This is a singly linked list pending notifications. When an IOB becomes - * available, *all* of the waiters in this list will be signaled and the - * entry will be freed. If there are multiple waiters then only the highest - * priority thread will get the IOB. Lower priority threads will need to - * call iob_notify_setup() once again. - */ - -static FAR struct iob_notify_s *g_iobnotify_pending; - -/* This semaphore is used as mutex to enforce mutually exclusive access to - * the IOB notification structures. - */ - -static sem_t g_iobnotify_sem; - -/**************************************************************************** - * Private Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: iob_notify_alloc - * - * Description: - * Allocate a notification structure by removing it from the free list. - * - ****************************************************************************/ - -static FAR struct iob_notify_s *iob_notify_alloc(void) -{ - FAR struct iob_notify_s *notify; - - notify = g_iobnotify_free; - if (notify != NULL) - { - g_iobnotify_free = notify->flink; - notify->flink = NULL; - } - - return notify; -} - -/**************************************************************************** - * Name: iob_notify_free - * - * Description: - * Free a notification structure by returning it to the head of the free - * list. - * - ****************************************************************************/ - -static void iob_notify_free(FAR struct iob_notify_s *notify) -{ - notify->flink = g_iobnotify_free; - g_iobnotify_free = notify; -} - -/**************************************************************************** - * Public Functions - ****************************************************************************/ - -/**************************************************************************** - * Name: iob_notify_initialize - * - * Description: - * Set up the notification structure for normal operation. - * - ****************************************************************************/ - -void iob_notify_initialize(void) -{ - int i; - - /* Add each notification structure to the free list */ - - for (i = 0; i < CONFIG_IOB_NWAITERS; i++) - { - FAR struct iob_notify_s *notify = &g_iobnotify_pool[i]; - - /* Add the pre-allocated notification to the head of the free list */ - - notify->flink = g_iobnotify_free; - g_iobnotify_free = notify; - } - - /* Initialize the semaphore that enforces mutually exclusive access to the - * notification data structures. - */ - - nxsem_init(&g_iobnotify_sem, 0, 1); -} - -/**************************************************************************** - * Name: iob_notify_setup - * - * Description: - * Set up to notify the specified PID with the provided signal number. - * - * NOTE: To avoid race conditions, the caller should set the sigprocmask - * to block signal delivery. The signal will be delivered once the - * signal is removed from the sigprocmask. - * - * Input Parameters: - * pid - The PID to be notified. If a zero value is provided, then the - * PID of the calling thread will be used. - * signo - The signal number to use with the notification. - * - * Returned Value: - * > 0 - There are already free IOBs and this this number of free IOBs - * (CAUTION: This value is volatile). No signal notification - * will be provided. - * == 0 - There are no free IOBs. A signal will be sent to 'pid' when - * at least one IOB is available. That IOB is *not* reserved for - * the caller. Hence, due to race conditions, it could be taken - * by some other task. In that event, the caller should call - * sig_notify again. - * < 0 - An unexpected error occurred and no signal will be sent. The - * returned value is a negated errno value that indicates the - * nature of the failure. - * - ****************************************************************************/ - -int iob_notify_setup(int pid, int signo) -{ - int ret; - - /* If the pid is zero, then use the pid of the calling thread */ - - if (pid <= 0) - { - pid = getpid(); - } - - /* Get exclusive access to the notifier data structures */ - - ret = nxsem_wait(&g_iobnotify_sem); - if (ret < 0) - { - return ret; - } - - /* If there are already free IOBs, then just return the number of free - * IOBs. - */ - - ret = iob_navail(); - if (ret <= 0) - { - /* Allocate a new notification */ - - FAR struct iob_notify_s *notify = iob_notify_alloc(); - if (notify == NULL) - { - ret = -ENOSPC; - } - else - { - /* Initialize the notification structure */ - - notify->pid = pid; - notify->signo = signo; - - /* And add it to the head of the pending list */ - - notify->flink = g_iobnotify_pending; - g_iobnotify_pending = notify; - ret = OK; - } - } - - (void)nxsem_post(&g_iobnotify_sem); - return ret; -} - -/**************************************************************************** - * Name: iob_notify_teardown - * - * Description: - * Eliminate an IOB notification previously setup by iob_notify_setup(). - * This function should only be called if the notification should be - * aborted prior to the notification. The notification will automatically - * be torn down after the signal is sent. - * - * Input Parameters: - * pid - The PID whose notification will be torn down. If a zero value - * is provided, then the PID of the calling thread will be used. - * - * Returned Value: - * Zero (OK) is returned on success; a negated errno value is returned on - * any failure. - * - ****************************************************************************/ - -int iob_notify_teardown(int pid, int signo) -{ - FAR struct iob_notify_s *notify; - FAR struct iob_notify_s *prev; - int ret; - - /* If the pid is zero, then use the pid of the calling thread */ - - if (pid <= 0) - { - pid = getpid(); - } - - /* Get exclusive access to the notifier data structures */ - - ret = nxsem_wait(&g_iobnotify_sem); - if (ret < 0) - { - return ret; - } - - /* Find the entry matching this PID in the g_iobnotify_pending list. We - * assume that there is only one. - */ - - ret = -ENOENT; - for (prev = NULL, notify = g_iobnotify_pending; - notify != NULL; - prev = notify, notify = notify->flink) - { - /* Is this the one we were looking for? */ - - if (notify->pid == pid) - { - /* Yes, remove it from the pending list */ - - if (prev == NULL) - { - g_iobnotify_pending = notify->flink; - } - else - { - prev->flink = notify->flink; - } - - /* And add it to the free list */ - - iob_notify_free(notify); - - ret = OK; - break; - } - } - - (void)nxsem_post(&g_iobnotify_sem); - return ret; -} - -/**************************************************************************** - * Name: iob_notify_signal - * - * Description: - * An IOB has become available. Signal all threads waiting for an IOB - * that an IOB is available. - * - * When an IOB becomes available, *all* of the waiters in this thread will - * be signaled. If there are multiple waiters then only the highest - * priority thread will get the IOB. Lower priority threads will need to - * call iob_notify once again. - * - * Input Parameters: - * None. - * - * Returned Value: - * None. - * - ****************************************************************************/ - -void iob_notify_signal(void) -{ - FAR struct iob_notify_s *notify; - int ret; - - /* Get exclusive access to the notifier data structure */ - - ret = nxsem_wait(&g_iobnotify_sem); - while (ret < 0) - { - DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); - } - - /* Don't let any newly started threads block this thread until all of - * the notifications and been sent. - */ - - sched_lock(); - - /* Process the notification at the head of the pending list until the - * pending list is empty */ - - while ((notify = g_iobnotify_pending) != NULL) - { - /* Remove the notification from the pending list */ - - g_iobnotify_pending = notify->flink; - - /* Signal the waiter */ - - (void)nxsig_kill(notify->pid, notify->signo); - - /* Free the notification by returning it to the free list */ - - iob_notify_free(notify); - } - - sched_unlock(); - (void)nxsem_post(&g_iobnotify_sem); -} - -#endif /* CONFIG_IOB_NOTIFIER */ diff --git a/sched/Kconfig b/sched/Kconfig index 7290fbbf15..896142ae50 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -1174,6 +1174,26 @@ endmenu # RTOS hooks menu "Signal Configuration" +config SIG_NOTIFIER + bool "Generic Signal notifier" + default n + ---help--- + Enable building of signal notifier logic that will send a signal to + a kernel thread when an event occurs. This is is a general + purpose notifier, but was developed specifically to support poll() + logic where the poll must wait for an resources to become available. + +config SIG_NOTIFIER_NWAITERS + int "Number of signal waiters" + default 4 + range 1 32766 + depends on SIG_NOTIFIER + ---help--- + If CONFIG_SIG_NOTIFIER is selected, then a pre-allocated pool of + notification structures will be allocated to hold information about + pending notifications. This then determines an upper limit for the + number of waiters that can be supported. + config SIG_EVTHREAD bool "Support SIGEV_THHREAD" default n diff --git a/sched/mqueue/mq_sndinternal.c b/sched/mqueue/mq_sndinternal.c index b11f23a4af..dccef88f7c 100644 --- a/sched/mqueue/mq_sndinternal.c +++ b/sched/mqueue/mq_sndinternal.c @@ -430,7 +430,7 @@ int nxmq_do_send(mqd_t mqdes, FAR struct mqueue_msg_s *mqmsg, else if (event.sigev_notify == SIGEV_THREAD) { - DEBUGVERIFY(nxsig_notification(pid, &event)); + DEBUGVERIFY(nxsig_evthread(pid, &event)); } #endif diff --git a/sched/signal/Make.defs b/sched/signal/Make.defs index cf907c277b..1294762f12 100644 --- a/sched/signal/Make.defs +++ b/sched/signal/Make.defs @@ -49,7 +49,11 @@ CSRCS += sig_default.c endif ifeq ($(CONFIG_SIG_EVTHREAD),y) -CSRCS += sig_notification.c +CSRCS += sig_evthread.c +endif + +ifeq ($(CONFIG_SIG_NOTIFIER),y) + CSRCS += sig_notifier.c endif # Include signal build support diff --git a/sched/signal/sig_notification.c b/sched/signal/sig_evthread.c similarity index 91% rename from sched/signal/sig_notification.c rename to sched/signal/sig_evthread.c index d0cdf174c3..66fbaffc93 100644 --- a/sched/signal/sig_notification.c +++ b/sched/signal/sig_evthread.c @@ -1,7 +1,7 @@ /**************************************************************************** - * sched/signal/sig_notification.c + * sched/signal/sig_evthread.c * - * Copyright (C) 2015, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2015, 2017-2018 Gregory Nutt. All rights reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -52,6 +52,7 @@ /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ + /* Use the low-prioriry work queue is it is available */ #if defined(CONFIG_SCHED_LPWORK) @@ -68,7 +69,7 @@ /* This structure retains all that is necessary to perform the notification */ -struct sig_notify_s +struct sig_evthread_s { struct work_s nt_work; /* Work queue structure */ union sigval nt_value; /* Data passed with notification */ @@ -80,7 +81,7 @@ struct sig_notify_s ****************************************************************************/ /**************************************************************************** - * Name: nxsig_notify_worker + * Name: nxsig_evthread_worker * * Description: * Perform the callback from the context of the worker thread. @@ -93,9 +94,9 @@ struct sig_notify_s * ****************************************************************************/ -static void nxsig_notify_worker(FAR void *arg) +static void nxsig_evthread_worker(FAR void *arg) { - FAR struct sig_notify_s *notify = (FAR struct sig_notify_s *)arg; + FAR struct sig_evthread_s *notify = (FAR struct sig_evthread_s *)arg; DEBUGASSERT(notify != NULL); @@ -117,7 +118,7 @@ static void nxsig_notify_worker(FAR void *arg) ****************************************************************************/ /**************************************************************************** - * Name: nxsig_notification + * Name: nxsig_evthread * * Description: * Notify a client a signal event via a function call. This function is @@ -136,15 +137,15 @@ static void nxsig_notify_worker(FAR void *arg) * ****************************************************************************/ -int nxsig_notification(pid_t pid, FAR struct sigevent *event) +int nxsig_evthread(pid_t pid, FAR struct sigevent *event) { - FAR struct sig_notify_s *notify; + FAR struct sig_evthread_s *notify; DEBUGASSERT(event != NULL && event->sigev_notify_function != NULL); int ret; /* Allocate a structure to hold the notification information */ - notify = kmm_zalloc(sizeof(struct sig_notify_s)); + notify = kmm_zalloc(sizeof(struct sig_evthread_s)); if (notify == NULL) { return -ENOMEM; @@ -161,7 +162,7 @@ int nxsig_notification(pid_t pid, FAR struct sigevent *event) /* Then queue the work */ - ret = work_queue(NTWORK, ¬ify->nt_work, nxsig_notify_worker, + ret = work_queue(NTWORK, ¬ify->nt_work, nxsig_evthread_worker, notify, 0); if (ret < 0) { diff --git a/sched/signal/sig_initialize.c b/sched/signal/sig_initialize.c index 97c229c742..654f0d8715 100644 --- a/sched/signal/sig_initialize.c +++ b/sched/signal/sig_initialize.c @@ -1,7 +1,8 @@ /**************************************************************************** * sched/signal/sig_initialize.c * - * Copyright (C) 2007, 2009, 2011, 2017 Gregory Nutt. All rights reserved. + * Copyright (C) 2007, 2009, 2011, 2017-2018 Gregory Nutt. All rights + * reserved. * Author: Gregory Nutt * * Redistribution and use in source and binary forms, with or without @@ -233,6 +234,12 @@ void nxsig_initialize(void) nxsig_alloc_pendingsignalblock(&g_sigpendingirqsignal, NUM_INT_SIGNALS_PENDING, SIG_ALLOC_IRQ); + +#ifdef CONFIG_SIG_NOTIFIER + /* Initialize the generic notifier facility */ + + nxsig_notifier_initialize(); +#endif } /**************************************************************************** diff --git a/sched/signal/sig_notifier.c b/sched/signal/sig_notifier.c new file mode 100644 index 0000000000..1bbcf989ec --- /dev/null +++ b/sched/signal/sig_notifier.c @@ -0,0 +1,492 @@ +/**************************************************************************** + * sched/signal/sig_notifier.c + * + * Copyright (C) 2018 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "signal/signal.h" + +#ifdef CONFIG_SIG_NOTIFIER + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/* This is the saved information for one notification */ + +struct nxsig_notifier_s +{ + FAR struct nxsig_notifier_s *flink; /* Supports a singly linked list */ + FAR void *qualifier; /* Event qualifier value */ + uint8_t signo; /* The signal number for notification */ + uint8_t evtype; /* See enum nxsig_evtype_e */ + pid_t pid; /* The PID to be notified */ + int16_t key; /* Unique ID for this notification */ +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/* This is a statically allocated pool of notification structures */ + +static struct nxsig_notifier_s g_notifier_pool[CONFIG_SIG_NOTIFIER_NWAITERS]; + +/* This is a list of free notification structures */ + +static FAR struct nxsig_notifier_s *g_notifier_free; + +/* This is a singly linked list of pending notifications. When an event + * occurs available, *all* of the waiters for that event in this list will + * be signaled and the entry will be freed. If there are multiple waiters + * for some resource, then only the highest priority thread will get the + * resource. Lower priority threads will need to call nxsig_notifier_setup() + * once again. + */ + +static FAR struct nxsig_notifier_s *g_notifier_pending; + +/* This semaphore is used as mutex to enforce mutually exclusive access to + * the notification data structures. + */ + +static sem_t g_notifier_sem; + +/* Used for lookup key generation */ + +static uint16_t g_notifier_key; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxsig_notifier_alloc + * + * Description: + * Allocate a notification structure by removing it from the free list. + * + ****************************************************************************/ + +static FAR struct nxsig_notifier_s *nxsig_notifier_alloc(void) +{ + FAR struct nxsig_notifier_s *notifier; + + notifier = g_notifier_free; + if (notifier != NULL) + { + g_notifier_free = notifier->flink; + notifier->flink = NULL; + } + + return notifier; +} + +/**************************************************************************** + * Name: nxsig_notifier_free + * + * Description: + * Free a notification structure by returning it to the head of the free + * list. + * + ****************************************************************************/ + +static void nxsig_notifier_free(FAR struct nxsig_notifier_s *notifier) +{ + notifier->flink = g_notifier_free; + g_notifier_free = notifier; +} + +/**************************************************************************** + * Name: nxsig_notifier_find + * + * Description: + * Given a unique key for notification, find the corresponding notification + * structure in the pending notification list. + * + ****************************************************************************/ + +static FAR struct nxsig_notifier_s * + nxsig_notifier_find(int16_t key, FAR struct nxsig_notifier_s **pprev) +{ + FAR struct nxsig_notifier_s *notifier; + FAR struct nxsig_notifier_s *prev; + + /* Find the entry matching this key in the g_notifier_pending list. */ + + for (prev = NULL, notifier = g_notifier_pending; + notifier != NULL; + prev = notifier, notifier = notifier->flink) + { + /* Is this the one we were looking for? */ + + if (notifier->key == key) + { + /* Return the previous entry if so requested */ + + if (pprev != NULL) + { + *pprev = prev; + } + + return notifier; + } + } + + return NULL; +} + +/**************************************************************************** + * Name: nxsig_notifier_key + * + * Description: + * Generate a unique key for this notification. + * + ****************************************************************************/ + +static int16_t nxsig_notifier_key(void) +{ + int16_t key; + + /* Loop until a unique key is generated */ + + do + { + if (g_notifier_key >= INT16_MAX) + { + g_notifier_key = 0; + } + + key = (int16_t)++g_notifier_key; + } + while (nxsig_notifier_find(key, NULL) != NULL); + + return key; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: nxsig_notifier_initialize + * + * Description: + * Set up the notification structure for normal operation. + * + ****************************************************************************/ + +void nxsig_notifier_initialize(void) +{ + int i; + + /* Add each notification structure to the free list */ + + for (i = 0; i < CONFIG_SIG_NOTIFIER_NWAITERS; i++) + { + FAR struct nxsig_notifier_s *notifier = &g_notifier_pool[i]; + + /* Add the pre-allocated notification to the head of the free list */ + + notifier->flink = g_notifier_free; + g_notifier_free = notifier; + } + + /* Initialize the semaphore that enforces mutually exclusive access to the + * notification data structures. + */ + + nxsem_init(&g_notifier_sem, 0, 1); +} + +/**************************************************************************** + * Name: nxsig_notifier_setup + * + * Description: + * Set up to notify the specified PID with the provided signal number. + * + * NOTE: To avoid race conditions, the caller should set the sigprocmask + * to block signal delivery. The signal will be delivered once the + * signal is removed from the sigprocmask. + * + * Input Parameters: + * pid - The PID to be notified. If a zero value is provided, + * then the PID of the calling thread will be used. + * signo - The signal number to use with the notification. + * evtype - The event type. + * qualifier - Event qualifier to distinguish different cases of the + * generic event type. + * + * Returned Value: + * > 0 - The key which may be used later in a call to + * nxsig_notifier_teardown(). + * == 0 - Not used (reserved for wrapper functions). + * < 0 - An unexpected error occurred and no signal will be sent. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +int nxsig_notifier_setup(int pid, int signo, enum nxsig_evtype_e evtype, + FAR void *qualifier) +{ + int ret; + + /* If the 'pid' is zero, then use the PID of the calling thread */ + + if (pid <= 0) + { + pid = getpid(); + } + + /* Get exclusive access to the notifier data structures */ + + ret = nxsem_wait(&g_notifier_sem); + if (ret < 0) + { + return ret; + } + + /* Allocate a new notification */ + + FAR struct nxsig_notifier_s *notifier = nxsig_notifier_alloc(); + if (notifier == NULL) + { + ret = -ENOSPC; + } + else + { + /* Generate a unique key for this notification */ + + int16_t key = nxsig_notifier_key(); + + /* Initialize the notification structure */ + + notifier->pid = pid; + notifier->signo = signo; + notifier->evtype = evtype; + notifier->key = key; + notifier->qualifier = qualifier; + + /* And add it to the head of the pending list */ + + notifier->flink = g_notifier_pending; + g_notifier_pending = notifier; + ret = key; + } + + (void)nxsem_post(&g_notifier_sem); + return ret; +} + +/**************************************************************************** + * Name: nxsig_notifier_teardown + * + * Description: + * Eliminate a notification previously setup by nxsig_notifier_setup(). + * This function should only be called if the notification should be + * aborted prior to the notification. The notification will automatically + * be torn down after the signal is sent. + * + * Input Parameters: + * key - The key value returned from a previous call to + * nxsig_notifier_setup(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int nxsig_notifier_teardown(int key) +{ + FAR struct nxsig_notifier_s *notifier; + FAR struct nxsig_notifier_s *prev; + int ret; + + /* Get exclusive access to the notifier data structures */ + + ret = nxsem_wait(&g_notifier_sem); + if (ret < 0) + { + return ret; + } + + /* Find the entry matching this PID in the g_notifier_pending list. We + * assume that there is only one. + */ + + notifier = nxsig_notifier_find(key, &prev); + if (notifier == NULL) + { + /* There is no notification with this key in the pending list */ + + ret = -ENOENT; + } + else + { + /* Found it! Remove the notification from the pending list */ + + if (prev == NULL) + { + g_notifier_pending = notifier->flink; + } + else + { + prev->flink = notifier->flink; + } + + /* And add it to the free list */ + + nxsig_notifier_free(notifier); + ret = OK; + } + + (void)nxsem_post(&g_notifier_sem); + return ret; +} + +/**************************************************************************** + * Name: nxsig_notifier_signal + * + * Description: + * An event has just occurred. Signal all threads waiting for that event. + * + * When an IOB becomes available, *all* of the waiters in this thread will + * be signaled. If there are multiple waiters for a resource then only + * the highest priority thread will get the resource. Lower priority + * threads will need to call nxsig_notify once again. + * + * Input Parameters: + * evtype - The type of the event that just occurred. + * qualifier - Event qualifier to distinguish different cases of the + * generic event type. + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void nxsig_notifier_signal(enum nxsig_evtype_e evtype, + FAR void *qualifier) +{ + FAR struct nxsig_notifier_s *notifier; + FAR struct nxsig_notifier_s *prev; + FAR struct nxsig_notifier_s *next; + int ret; + + /* Get exclusive access to the notifier data structure */ + + ret = nxsem_wait(&g_notifier_sem); + while (ret < 0) + { + DEBUGASSERT(ret == -EINTR || ret == -ECANCELED); + } + + /* Don't let any newly started threads block this thread until all of + * the notifications and been sent. + */ + + sched_lock(); + + /* Process the notification at the head of the pending list until the + * pending list is empty */ + + /* Find the entry matching this key in the g_notifier_pending list. */ + + for (prev = NULL, notifier = g_notifier_pending; + notifier != NULL; + notifier = next) + { + /* Set up for the next time through the loop (in case the entry is + * removed from the list). + */ + + next = notifier->flink; + + /* Check if this is the a notification request for the event that + * just occurred. + */ + + if (notifier->evtype == evtype && notifier->qualifier == qualifier) + { + /* Yes.. Remove the notification from the pending list */ + + if (prev == NULL) + { + g_notifier_pending = next; + } + else + { + prev->flink = next; + } + + /* Signal the waiter */ + + (void)nxsig_kill(notifier->pid, notifier->signo); + + /* Free the notification by returning it to the free list */ + + nxsig_notifier_free(notifier); + } + else + { + /* The entry was not removed, the current entry will be the + * next previous entry. + */ + + prev = notifier; + } + } + + sched_unlock(); + (void)nxsem_post(&g_notifier_sem); +} + +#endif /* CONFIG_SIG_NOTIFIER */ diff --git a/sched/signal/signal.h b/sched/signal/signal.h index c6c632c556..6ca41ad913 100644 --- a/sched/signal/signal.h +++ b/sched/signal/signal.h @@ -68,13 +68,14 @@ * Public Type Definitions ****************************************************************************/ +/* This enumeration identifies the type of signal data allocation */ + enum sigalloc_e { SIG_ALLOC_FIXED = 0, /* pre-allocated; never freed */ SIG_ALLOC_DYN, /* dynamically allocated; free when unused */ SIG_ALLOC_IRQ /* Preallocated, reserved for interrupt handling */ }; -typedef enum sigalloc_e sigalloc_t; /* The following defines the sigaction queue entry */ @@ -166,6 +167,12 @@ struct task_group_s; void weak_function nxsig_initialize(void); void nxsig_alloc_actionblock(void); +/* sig_notifier.c */ + +#ifdef CONFIG_SIG_NOTIFIER +void nxsig_notifier_initialize(void); +#endif + /* sig_action.c */ void nxsig_release_action(FAR sigactq_t *sigact); diff --git a/sched/timer/timer_settime.c b/sched/timer/timer_settime.c index d7a052f4fe..0f6741d6fc 100644 --- a/sched/timer/timer_settime.c +++ b/sched/timer/timer_settime.c @@ -118,7 +118,7 @@ static inline void timer_signotify(FAR struct posix_timer_s *timer) else if (timer->pt_event.sigev_notify == SIGEV_THREAD) { - DEBUGVERIFY(nxsig_notification(timer->pt_owner, &timer->pt_event)); + DEBUGVERIFY(nxsig_evthread(timer->pt_owner, &timer->pt_event)); } #endif }