diff --git a/sched/wqueue/kwork_queue.c b/sched/wqueue/kwork_queue.c index 4e36159a66b..6e9b4ac1a07 100644 --- a/sched/wqueue/kwork_queue.c +++ b/sched/wqueue/kwork_queue.c @@ -40,6 +40,122 @@ #ifdef CONFIG_SCHED_WORKQUEUE +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: work_qqueue + * + * Description: + * Queue work on a kernel-mode work queue. Regular work uses an absolute + * expiration calculated before taking the queue lock. Periodic work + * advances the previous expiration while holding the lock. + * + * Input Parameters: + * wqueue - The work queue handle + * work - The work structure to queue + * worker - The worker callback to be invoked. The callback will be + * invoked on the worker thread of execution. + * arg - The argument that will be passed to the worker callback when + * it is invoked. + * delay - Delay (in clock ticks) from the time queue until the worker + * is invoked. Zero means to perform the work immediately. + * period - Use the previous expiration as the scheduling reference + * + * Returned Value: + * Zero on success, a negated errno on failure + * + ****************************************************************************/ + +static int work_qqueue(FAR struct kwork_wqueue_s *wqueue, + FAR struct work_s *work, worker_t worker, + FAR void *arg, clock_t delay, bool period) +{ + irqstate_t flags; + clock_t expected = 0; + bool retimer; + + if (wqueue == NULL || work == NULL || worker == NULL || + delay < 0 || delay > WDOG_MAX_DELAY) + { + return -EINVAL; + } + + /* Preserve regular queue timing across lock contention. */ + + if (!period) + { + expected = clock_delay2abstick(delay); + } + + /* Interrupts are disabled so that this logic can be called from task + * logic or interrupt handling logic. + */ + + flags = spin_lock_irqsave(&wqueue->lock); + + if (wqueue->exit) + { + spin_unlock_irqrestore(&wqueue->lock, flags); + return -ESHUTDOWN; + } + + /* Remove a previous pending instance before requeueing it. */ + + retimer = work_available(work) ? false : work_remove(wqueue, work); + + /* Initialize the work structure. */ + + work->worker = worker; /* Work callback. non-NULL means queued */ + work->arg = arg; /* Callback argument */ + + if (period) + { + work->qtime += delay; + } + else + { + work->qtime = expected; + } + + if (delay > 0) + { + /* Insert to the pending list of the wqueue. */ + + if (work_insert_pending(wqueue, work)) + { + /* Start the timer if the work is the earliest expired work. */ + + retimer = false; + wd_start_abstick(&wqueue->timer, work->qtime, + work_timer_expired, (wdparm_t)wqueue); + } + } + else + { + /* Insert to the expired list of the wqueue. */ + + list_add_tail(&wqueue->expired, &work->node); + } + + if (retimer) + { + work_timer_reset(wqueue); + } + + spin_unlock_irqrestore(&wqueue->lock, flags); + + if (delay == 0) + { + /* Immediately wake up the worker thread. */ + + nxsem_post(&wqueue->sem); + } + + return OK; +} + /**************************************************************************** * Public Functions ****************************************************************************/ @@ -71,70 +187,7 @@ int work_queue_next_wq(FAR struct kwork_wqueue_s *wqueue, FAR struct work_s *work, worker_t worker, FAR void *arg, clock_t delay) { - irqstate_t flags; - bool retimer; - int ret = OK; - - if (wqueue == NULL || work == NULL || worker == NULL || - delay < 0 || delay > WDOG_MAX_DELAY) - { - return -EINVAL; - } - - flags = spin_lock_irqsave(&wqueue->lock); - - if (wqueue->exit) - { - ret = -ESHUTDOWN; - goto out; - } - - /* Remove a previous pending instance before requeueing it. */ - - retimer = work_available(work) ? false : work_remove(wqueue, work); - - /* Initialize the work structure. */ - - work->worker = worker; /* Work callback. non-NULL means queued */ - work->arg = arg; /* Callback argument */ - work->qtime += delay; /* Expected time based on last expiration time */ - - if (delay) - { - /* Insert to the pending list of the wqueue. */ - - if (work_insert_pending(wqueue, work)) - { - /* Start the timer if the work is the earliest expired work. */ - - retimer = false; - wd_start_abstick(&wqueue->timer, work->qtime, - work_timer_expired, (wdparm_t)wqueue); - } - } - else - { - /* Insert to the expired list of the wqueue. */ - - list_add_tail(&wqueue->expired, &work->node); - } - - if (retimer) - { - work_timer_reset(wqueue); - } - -out: - spin_unlock_irqrestore(&wqueue->lock, flags); - - if (ret == OK && !delay) - { - /* Immediately wake up the worker thread. */ - - nxsem_post(&wqueue->sem); - } - - return ret; + return work_qqueue(wqueue, work, worker, arg, delay, true); } int work_queue_next(int qid, FAR struct work_s *work, worker_t worker, @@ -177,75 +230,7 @@ int work_queue_wq(FAR struct kwork_wqueue_s *wqueue, FAR struct work_s *work, worker_t worker, FAR void *arg, clock_t delay) { - irqstate_t flags; - clock_t expected; - bool retimer; - - if (wqueue == NULL || work == NULL || worker == NULL || - delay < 0 || delay > WDOG_MAX_DELAY) - { - return -EINVAL; - } - - expected = clock_delay2abstick(delay); - - /* Interrupts are disabled so that this logic can be called from with - * task logic or from interrupt handling logic. - */ - - flags = spin_lock_irqsave(&wqueue->lock); - - if (wqueue->exit) - { - spin_unlock_irqrestore(&wqueue->lock, flags); - return -ESHUTDOWN; - } - - /* Ensure the work has been removed. */ - - retimer = work_available(work) ? false : work_remove(wqueue, work); - - /* Initialize the work structure. */ - - work->worker = worker; /* Work callback. non-NULL means queued */ - work->arg = arg; /* Callback argument */ - work->qtime = expected; /* Expected time */ - - if (delay) - { - /* Insert to the pending list of the wqueue. */ - - if (work_insert_pending(wqueue, work)) - { - /* Start the timer if the work is the earliest expired work. */ - - retimer = false; - wd_start_abstick(&wqueue->timer, work->qtime, - work_timer_expired, (wdparm_t)wqueue); - } - } - else - { - /* Insert to the expired list of the wqueue. */ - - list_add_tail(&wqueue->expired, &work->node); - } - - if (retimer) - { - work_timer_reset(wqueue); - } - - spin_unlock_irqrestore(&wqueue->lock, flags); - - if (!delay) - { - /* Immediately wake up the worker thread. */ - - nxsem_post(&wqueue->sem); - } - - return 0; + return work_qqueue(wqueue, work, worker, arg, delay, false); } int work_queue(int qid, FAR struct work_s *work, worker_t worker,