123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371 |
- /****************************************************************************
- * sched/wqueue/kwork_notifier.c
- *
- * Copyright (C) 2018 Gregory Nutt. All rights reserved.
- * Author: Gregory Nutt <gnutt@nuttx.org>
- *
- * 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 <nuttx/config.h>
- #include <sys/types.h>
- #include <stdint.h>
- #include <stdbool.h>
- #include <unistd.h>
- #include <string.h>
- #include <sched.h>
- #include <assert.h>
- #include <nuttx/kmalloc.h>
- #include <nuttx/semaphore.h>
- #include <nuttx/wqueue.h>
- #include "wqueue/wqueue.h"
- #ifdef CONFIG_WQUEUE_NOTIFIER
- /****************************************************************************
- * Private Data
- ****************************************************************************/
- /* This is a doubly linked list of pending notifications. When an event
- * occurs available, *all* of the waiters for that event in this list will
- * be notified and the entry will be freed. If there are multiple waiters
- * for some resource, then only the first to execute thread will get the
- * resource. Lower priority threads will need to call work_notifier_setup()
- * once again.
- */
- static dq_queue_t 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 = SEM_INITIALIZER(1);
- /* Used for lookup key generation */
- static uint16_t g_notifier_key;
- /****************************************************************************
- * Private Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: work_notifier_find
- *
- * Description:
- * Given a unique key for notification, find the corresponding notification
- * structure in the pending notification list.
- *
- ****************************************************************************/
- static FAR struct work_notifier_entry_s *work_notifier_find(int16_t key)
- {
- FAR struct work_notifier_entry_s *notifier;
- FAR dq_entry_t *entry;
- /* Find the entry matching this key in the g_notifier_pending list. */
- for (entry = dq_peek(&g_notifier_pending);
- entry != NULL;
- entry = dq_next(entry))
- {
- notifier = (FAR struct work_notifier_entry_s *)entry;
- /* Is this the one we were looking for? */
- if (notifier->key == key)
- {
- /* Yes.. return a reference to it */
- return notifier;
- }
- }
- return NULL;
- }
- /****************************************************************************
- * Name: work_notifier_key
- *
- * Description:
- * Generate a unique key for this notification.
- *
- ****************************************************************************/
- static int16_t work_notifier_key(void)
- {
- int16_t key;
- /* Loop until a unique key is generated. Range 1-INT16_MAX. */
- do
- {
- if (g_notifier_key >= INT16_MAX)
- {
- g_notifier_key = 0;
- }
- key = (int16_t)++g_notifier_key;
- }
- while (work_notifier_find(key) != NULL);
- return key;
- }
- /****************************************************************************
- * Public Functions
- ****************************************************************************/
- /****************************************************************************
- * Name: work_notifier_setup
- *
- * Description:
- * Set up to provide a notification when an event occurs.
- *
- * Input Parameters:
- * info - Describes the work notification.
- *
- * Returned Value:
- * > 0 - The key which may be used later in a call to
- * work_notifier_teardown().
- * == 0 - Not used (reserved for wrapper functions).
- * < 0 - An unexpected error occurred and no notification will be sent.
- * The returned value is a negated errno value that indicates the
- * nature of the failure.
- *
- ****************************************************************************/
- int work_notifier_setup(FAR struct work_notifier_s *info)
- {
- FAR struct work_notifier_entry_s *notifier;
- int ret;
- DEBUGASSERT(info != NULL && info->worker != NULL);
- DEBUGASSERT(info->qid == HPWORK || info->qid == LPWORK);
- /* Get exclusive access to the notifier data structures */
- ret = nxsem_wait(&g_notifier_sem);
- if (ret < 0)
- {
- return ret;
- }
- /* Allocate a new notification entry */
- notifier = kmm_malloc(sizeof(struct work_notifier_entry_s));
- if (notifier == NULL)
- {
- ret = -ENOMEM;
- }
- else
- {
- /* Duplicate the notification info */
- memcpy(¬ifier->info, info, sizeof(struct work_notifier_s));
- /* Generate a unique key for this notification */
- notifier->key = work_notifier_key();
- /* Add the notification to the tail of the pending list
- *
- * REVISIT: Work will be processed in FIFO order. Perhaps
- * we should should consider saving the notification is the
- * order of the caller's execution priority so that the
- * notifications executed in a saner order?
- */
- dq_addlast((FAR dq_entry_t *)notifier, &g_notifier_pending);
- ret = work_notifier_key();
- }
- (void)nxsem_post(&g_notifier_sem);
- return ret;
- }
- /****************************************************************************
- * Name: work_notifier_teardown
- *
- * Description:
- * Eliminate a notification previously setup by work_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 notification executes.
- *
- * Input Parameters:
- * key - The key value returned from a previous call to
- * work_notifier_setup().
- *
- * Returned Value:
- * Zero (OK) is returned on success; a negated errno value is returned on
- * any failure.
- *
- ****************************************************************************/
- int work_notifier_teardown(int key)
- {
- FAR struct work_notifier_entry_s *notifier;
- int ret;
- DEBUGASSERT(key > 0 && key <= INT16_MAX);
- /* 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 = work_notifier_find(key);
- 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 */
- dq_rem((FAR dq_entry_t *)notifier, &g_notifier_pending);
- /* Free the notification */
- kmm_free(notifier);
- ret = OK;
- }
- (void)nxsem_post(&g_notifier_sem);
- return ret;
- }
- /****************************************************************************
- * Name: work_notifier_signal
- *
- * Description:
- * An event has just occurred. Notify all threads waiting for that event.
- *
- * When an event of interest occurs, *all* of the workers waiting for this
- * event will be executed. If there are multiple workers for a resource
- * then only the first to execute will get the resource. Others will
- * need to call work_notifier_setup() 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 work_notifier_signal(enum work_evtype_e evtype,
- FAR void *qualifier)
- {
- FAR struct work_notifier_entry_s *notifier;
- FAR dq_entry_t *entry;
- FAR dq_entry_t *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 (entry = dq_peek(&g_notifier_pending);
- entry != NULL;
- entry = next)
- {
- FAR struct work_notifier_s *info;
- /* Set up for the next time through the loop (in case the entry is
- * removed from the list).
- */
- next = entry->flink;
- /* Set up some convenience pointers */
- notifier = (FAR struct work_notifier_entry_s *)entry;
- info = ¬ifier->info;
- /* Check if this is the a notification request for the event that
- * just occurred.
- */
- if (info->evtype == evtype && info->qualifier == qualifier)
- {
- /* Yes.. Remove the notification from the pending list */
- dq_rem((FAR dq_entry_t *)notifier, &g_notifier_pending);
- /* Schedule the work. The entire notifier entry is passed as an
- * argument to the work function because that function is
- * responsible for freeing the allocated memory.
- */
- (void)work_queue(info->qid, ¬ifier->work, info->worker,
- entry, 0);
- }
- }
- sched_unlock();
- (void)nxsem_post(&g_notifier_sem);
- }
- #endif /* CONFIG_WQUEUE_NOTIFIER */
|