diff --git a/include/zephyr/kernel.h b/include/zephyr/kernel.h index a35c453ed8c..82d4b3a6889 100644 --- a/include/zephyr/kernel.h +++ b/include/zephyr/kernel.h @@ -3606,6 +3606,22 @@ int k_work_queue_drain(struct k_work_q *queue, bool plug); */ int k_work_queue_unplug(struct k_work_q *queue); +/** @brief Stop a work queue. + * + * Stops the work queue thread and ensures that no further work will be processed. + * This call is blocking and guarantees that the work queue thread has terminated + * cleanly if successful, no work will be processed past this point. + * + * @param queue Pointer to the queue structure. + * @param timeout Maximum time to wait for the work queue to stop. + * + * @retval 0 if the work queue was stopped + * @retval -EALREADY if the work queue was not started (or already stopped) + * @retval -EBUSY if the work queue is actively processing work items + * @retval -ETIMEDOUT if the work queue did not stop within the stipulated timeout + */ +int k_work_queue_stop(struct k_work_q *queue, k_timeout_t timeout); + /** @brief Initialize a delayable work structure. * * This must be invoked before scheduling a delayable work structure for the @@ -3915,6 +3931,8 @@ enum { K_WORK_QUEUE_DRAIN = BIT(K_WORK_QUEUE_DRAIN_BIT), K_WORK_QUEUE_PLUGGED_BIT = 3, K_WORK_QUEUE_PLUGGED = BIT(K_WORK_QUEUE_PLUGGED_BIT), + K_WORK_QUEUE_STOP_BIT = 4, + K_WORK_QUEUE_STOP = BIT(K_WORK_QUEUE_STOP_BIT), /* Static work queue flags */ K_WORK_QUEUE_NO_YIELD_BIT = 8, diff --git a/kernel/work.c b/kernel/work.c index 5e465767707..ac5ac3eaaae 100644 --- a/kernel/work.c +++ b/kernel/work.c @@ -653,6 +653,12 @@ static void work_queue_main(void *workq_ptr, void *p2, void *p3) * submissions. */ (void)z_sched_wake_all(&queue->drainq, 1, NULL); + } else if (flag_test(&queue->flags, K_WORK_QUEUE_STOP_BIT)) { + /* User has requested that the queue stop. Clear the status flags and exit. + */ + flags_set(&queue->flags, 0); + k_spin_unlock(&lock, key); + return; } else { /* No work is available and no queue state requires * special handling. @@ -812,6 +818,35 @@ int k_work_queue_unplug(struct k_work_q *queue) return ret; } +int k_work_queue_stop(struct k_work_q *queue, k_timeout_t timeout) +{ + __ASSERT_NO_MSG(queue); + + k_spinlock_key_t key = k_spin_lock(&lock); + + if (!flag_test(&queue->flags, K_WORK_QUEUE_STARTED_BIT)) { + k_spin_unlock(&lock, key); + return -EALREADY; + } + + if (!flag_test(&queue->flags, K_WORK_QUEUE_PLUGGED_BIT)) { + k_spin_unlock(&lock, key); + return -EBUSY; + } + + flag_set(&queue->flags, K_WORK_QUEUE_STOP_BIT); + notify_queue_locked(queue); + k_spin_unlock(&lock, key); + if (k_thread_join(&queue->thread, timeout)) { + key = k_spin_lock(&lock); + flag_clear(&queue->flags, K_WORK_QUEUE_STOP_BIT); + k_spin_unlock(&lock, key); + return -ETIMEDOUT; + } + + return 0; +} + #ifdef CONFIG_SYS_CLOCK_EXISTS /* Timeout handler for delayable work.