2014-10-10 06:22:51 -06:00
|
|
|
/****************************************************************************
|
2018-05-29 13:21:26 -06:00
|
|
|
* libs/libc/wqueue/work_usrthread.c
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
2024-09-25 14:05:00 +02:00
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
*
|
2021-03-02 15:54:21 +01:00
|
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
|
|
* contributor license agreements. See the NOTICE file distributed with
|
|
|
|
|
* this work for additional information regarding copyright ownership. The
|
|
|
|
|
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
|
|
|
|
* "License"); you may not use this file except in compliance with the
|
|
|
|
|
* License. You may obtain a copy of the License at
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
2021-03-02 15:54:21 +01:00
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
2021-03-02 15:54:21 +01:00
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
|
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
|
|
|
* License for the specific language governing permissions and limitations
|
|
|
|
|
* under the License.
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Included Files
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
#include <nuttx/config.h>
|
|
|
|
|
|
2014-10-10 14:52:04 -06:00
|
|
|
#include <stdint.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
#include <pthread.h>
|
|
|
|
|
#include <sched.h>
|
2026-08-27 20:52:26 +08:00
|
|
|
#include <stdlib.h>
|
2014-10-10 12:27:11 -06:00
|
|
|
#include <errno.h>
|
2014-10-10 14:52:04 -06:00
|
|
|
#include <assert.h>
|
2014-10-10 09:34:03 -06:00
|
|
|
|
2014-10-10 13:21:37 -06:00
|
|
|
#include <nuttx/clock.h>
|
2025-04-16 19:44:35 +08:00
|
|
|
#include <nuttx/list.h>
|
2017-10-08 11:52:32 -06:00
|
|
|
#include <nuttx/wqueue.h>
|
2014-10-10 06:22:51 -06:00
|
|
|
|
2014-10-10 14:52:04 -06:00
|
|
|
#include "wqueue/wqueue.h"
|
|
|
|
|
|
2021-08-01 15:27:08 +08:00
|
|
|
#if defined(CONFIG_LIBC_USRWORK) && !defined(__KERNEL__)
|
2014-10-10 06:22:51 -06:00
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Pre-processor Definitions
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2026-05-03 15:54:17 +08:00
|
|
|
#define WORK_DELAY_MAX UINT64_MAX
|
2018-08-25 14:58:07 -06:00
|
|
|
|
2014-10-10 06:22:51 -06:00
|
|
|
/****************************************************************************
|
2025-05-27 18:15:37 +02:00
|
|
|
* Private Types
|
2014-10-10 06:22:51 -06:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Public Data
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2014-10-10 14:52:04 -06:00
|
|
|
/* The state of the user mode work queue. */
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
static struct usr_worker_s g_usrworker =
|
|
|
|
|
{
|
|
|
|
|
0,
|
|
|
|
|
NULL,
|
|
|
|
|
&g_usrwork,
|
|
|
|
|
SEM_INITIALIZER(0),
|
|
|
|
|
0,
|
|
|
|
|
};
|
|
|
|
|
|
2022-09-06 14:18:45 +08:00
|
|
|
struct usr_wqueue_s g_usrwork =
|
|
|
|
|
{
|
2025-04-16 19:44:35 +08:00
|
|
|
LIST_INITIAL_VALUE(g_usrwork.q),
|
2022-09-06 14:18:45 +08:00
|
|
|
NXMUTEX_INITIALIZER,
|
|
|
|
|
SEM_INITIALIZER(0),
|
2026-08-27 20:52:26 +08:00
|
|
|
&g_usrworker,
|
|
|
|
|
1,
|
|
|
|
|
false,
|
|
|
|
|
false,
|
2022-09-06 14:18:45 +08:00
|
|
|
};
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2014-10-10 06:22:51 -06:00
|
|
|
/****************************************************************************
|
|
|
|
|
* Private Functions
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2014-10-10 14:52:04 -06:00
|
|
|
/****************************************************************************
|
2026-08-27 20:52:26 +08:00
|
|
|
* Name: work_pthread
|
2014-10-10 14:52:04 -06:00
|
|
|
*
|
|
|
|
|
* Description:
|
2026-08-27 20:52:26 +08:00
|
|
|
* This is the worker thread that performs the actions placed on the user
|
|
|
|
|
* work queue.
|
|
|
|
|
*
|
|
|
|
|
* This is a user-mode work queue. The predefined queue is started by
|
|
|
|
|
* application start-up logic through work_usrstart(); custom queues are
|
|
|
|
|
* started by work_queue_create().
|
2014-10-10 14:52:04 -06:00
|
|
|
*
|
2018-02-01 10:00:02 -06:00
|
|
|
* Input Parameters:
|
2026-08-27 20:52:26 +08:00
|
|
|
* arg - Describes this worker and its parent queue
|
2014-10-10 14:52:04 -06:00
|
|
|
*
|
|
|
|
|
* Returned Value:
|
2026-08-27 20:52:26 +08:00
|
|
|
* NULL
|
2014-10-10 14:52:04 -06:00
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
static pthread_addr_t work_pthread(pthread_addr_t arg)
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
FAR struct usr_worker_s *worker =
|
|
|
|
|
(FAR struct usr_worker_s *)arg;
|
|
|
|
|
FAR struct usr_wqueue_s *wqueue = worker->wqueue;
|
2025-04-16 19:44:35 +08:00
|
|
|
FAR struct work_s *work;
|
2026-08-27 20:52:26 +08:00
|
|
|
worker_t callback;
|
|
|
|
|
FAR void *work_arg;
|
2025-04-17 11:41:22 +08:00
|
|
|
clock_t tick;
|
2018-06-16 12:16:13 -06:00
|
|
|
clock_t next;
|
2014-10-10 14:52:04 -06:00
|
|
|
int ret;
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
for (; ; )
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Then process queued work. Lock the work queue while we process
|
|
|
|
|
* items in the work list.
|
|
|
|
|
*/
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
next = WORK_DELAY_MAX;
|
|
|
|
|
ret = nxmutex_lock(&wqueue->lock);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
/* Restart if we were awakened by a signal. */
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
continue;
|
|
|
|
|
}
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
if (wqueue->exit)
|
|
|
|
|
{
|
|
|
|
|
nxmutex_unlock(&wqueue->lock);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-04-16 19:44:35 +08:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
/* And check each entry in the work queue. Since we have locked the
|
|
|
|
|
* work queue we know: (1) we will not be suspended unless we do
|
|
|
|
|
* so ourselves, and (2) there will be no changes to the work queue
|
2014-10-10 14:52:04 -06:00
|
|
|
*/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
while (!list_is_empty(&wqueue->q))
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
work = list_first_entry(&wqueue->q, struct work_s, node);
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Is this work ready? It is ready if there is no delay or if
|
|
|
|
|
* the delay has elapsed. is the time that the work was added
|
|
|
|
|
* to the work queue. Therefore a delay of equal or less than
|
|
|
|
|
* zero will always execute immediately.
|
2014-10-10 14:52:04 -06:00
|
|
|
*/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
tick = clock();
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Is this delay work ready? */
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
if (clock_compare(work->qtime, tick))
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Remove the ready-to-execute work from the list */
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
list_delete(&work->node);
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Extract the work description from the entry (in case the
|
|
|
|
|
* work instance may be reused after it has been de-queued).
|
2014-10-10 14:52:04 -06:00
|
|
|
*/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
callback = work->worker;
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Check for a race condition where the work may be nullified
|
|
|
|
|
* before it is removed from the queue.
|
2014-10-10 14:52:04 -06:00
|
|
|
*/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
if (callback != NULL)
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
/* Extract the work argument before unlocking the queue. */
|
|
|
|
|
|
|
|
|
|
work_arg = work->arg;
|
|
|
|
|
|
|
|
|
|
/* Mark the work as no longer being queued */
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
work->worker = NULL;
|
|
|
|
|
worker->work = work;
|
|
|
|
|
|
|
|
|
|
/* Let another worker process the next ready entry. */
|
|
|
|
|
|
|
|
|
|
if (!list_is_empty(&wqueue->q))
|
|
|
|
|
{
|
|
|
|
|
FAR struct work_s *next_work =
|
|
|
|
|
list_first_entry(&wqueue->q, struct work_s, node);
|
|
|
|
|
|
|
|
|
|
if (clock_compare(next_work->qtime, tick))
|
|
|
|
|
{
|
|
|
|
|
work_wake(wqueue);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Do the work. Unlock the work queue while the work is
|
|
|
|
|
* being performed... we don't have any idea how long
|
|
|
|
|
* this will take!
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
nxmutex_unlock(&wqueue->lock);
|
|
|
|
|
callback(work_arg);
|
|
|
|
|
|
|
|
|
|
/* Now, unfortunately, since we unlocked the work queue
|
|
|
|
|
* we don't know the state of the work list and we will
|
|
|
|
|
* have to start back at the head of the list.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
ret = nxmutex_lock(&wqueue->lock);
|
|
|
|
|
}
|
|
|
|
|
while (ret < 0);
|
|
|
|
|
|
|
|
|
|
worker->work = NULL;
|
|
|
|
|
|
|
|
|
|
while (worker->wait_count > 0)
|
|
|
|
|
{
|
|
|
|
|
worker->wait_count--;
|
|
|
|
|
nxsem_post(&worker->wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (wqueue->exit)
|
|
|
|
|
{
|
|
|
|
|
nxmutex_unlock(&wqueue->lock);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2014-10-10 14:52:04 -06:00
|
|
|
}
|
|
|
|
|
}
|
2026-08-27 20:52:26 +08:00
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
next = work->qtime - tick;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Unlock the work queue before waiting. */
|
|
|
|
|
|
|
|
|
|
nxmutex_unlock(&wqueue->lock);
|
|
|
|
|
|
|
|
|
|
if (next == WORK_DELAY_MAX)
|
|
|
|
|
{
|
|
|
|
|
/* Wait indefinitely until work_queue has new items */
|
|
|
|
|
|
|
|
|
|
nxsem_wait(&wqueue->wake);
|
2014-10-10 14:52:04 -06:00
|
|
|
}
|
2021-07-26 10:36:13 +08:00
|
|
|
else
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
struct timespec now;
|
|
|
|
|
struct timespec delay;
|
|
|
|
|
struct timespec rqtp;
|
|
|
|
|
|
|
|
|
|
/* Wait awhile to check the work list. We will wait here until
|
|
|
|
|
* either the time elapses or until we are awakened by a semaphore.
|
|
|
|
|
* Interrupts will be re-enabled while we wait.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
clock_gettime(CLOCK_REALTIME, &now);
|
|
|
|
|
clock_ticks2time(&delay, next);
|
|
|
|
|
clock_timespec_add(&now, &delay, &rqtp);
|
|
|
|
|
|
|
|
|
|
nxsem_timedwait(&wqueue->wake, &rqtp);
|
2014-10-10 14:52:04 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
return NULL;
|
|
|
|
|
}
|
2018-09-14 07:51:31 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
#ifdef CONFIG_BUILD_PROTECTED
|
|
|
|
|
static int work_usrtask(int argc, char *argv[])
|
|
|
|
|
{
|
|
|
|
|
work_pthread(&g_usrworker);
|
|
|
|
|
return OK;
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#ifndef CONFIG_DISABLE_PTHREAD
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Name: work_thread_create
|
|
|
|
|
*
|
|
|
|
|
* Description:
|
|
|
|
|
* Create the worker threads for a dynamically allocated user work queue.
|
|
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
static int work_thread_create(FAR const char *name, int priority,
|
|
|
|
|
FAR void *stack_addr, int stack_size,
|
|
|
|
|
FAR struct usr_wqueue_s *wqueue)
|
|
|
|
|
{
|
|
|
|
|
pthread_attr_t attr;
|
|
|
|
|
struct sched_param param;
|
|
|
|
|
int created = 0;
|
|
|
|
|
int lockret;
|
|
|
|
|
int ret;
|
|
|
|
|
int wndx;
|
2018-09-14 07:51:31 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
ret = pthread_attr_init(&attr);
|
|
|
|
|
if (ret != 0)
|
2014-10-10 14:52:04 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
return -ret;
|
|
|
|
|
}
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
ret = pthread_attr_setstacksize(&attr, stack_size);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
goto errout_with_attr;
|
2018-08-25 14:58:07 -06:00
|
|
|
}
|
2026-08-27 20:52:26 +08:00
|
|
|
|
|
|
|
|
ret = pthread_attr_setschedpolicy(&attr, SCHED_FIFO);
|
|
|
|
|
if (ret != 0)
|
2018-08-25 14:58:07 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
goto errout_with_attr;
|
|
|
|
|
}
|
2018-09-14 09:02:03 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
ret = pthread_attr_getschedparam(&attr, ¶m);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
goto errout_with_attr;
|
|
|
|
|
}
|
2014-10-12 09:40:26 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
param.sched_priority = priority;
|
|
|
|
|
ret = pthread_attr_setschedparam(&attr, ¶m);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
goto errout_with_attr;
|
|
|
|
|
}
|
2018-09-14 09:02:03 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
ret = pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
goto errout_with_attr;
|
2014-10-10 14:52:04 -06:00
|
|
|
}
|
2026-08-27 20:52:26 +08:00
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < wqueue->nthreads; wndx++)
|
|
|
|
|
{
|
|
|
|
|
FAR struct usr_worker_s *worker = &wqueue->worker[wndx];
|
|
|
|
|
|
|
|
|
|
if (stack_addr != NULL)
|
|
|
|
|
{
|
|
|
|
|
FAR void *stack = (FAR void *)
|
|
|
|
|
((uintptr_t)stack_addr + wndx * stack_size);
|
|
|
|
|
|
|
|
|
|
ret = pthread_attr_setstack(&attr, stack, stack_size);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
goto errout_with_threads;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = pthread_create(&worker->tid, &attr, work_pthread, worker);
|
|
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
goto errout_with_threads;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
created++;
|
|
|
|
|
pthread_setname_np(worker->tid, name);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
|
return OK;
|
|
|
|
|
|
|
|
|
|
errout_with_threads:
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
lockret = nxmutex_lock(&wqueue->lock);
|
|
|
|
|
}
|
|
|
|
|
while (lockret < 0);
|
|
|
|
|
|
|
|
|
|
wqueue->exit = true;
|
|
|
|
|
nxmutex_unlock(&wqueue->lock);
|
|
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < created; wndx++)
|
|
|
|
|
{
|
|
|
|
|
nxsem_post(&wqueue->wake);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < created; wndx++)
|
|
|
|
|
{
|
|
|
|
|
pthread_join(wqueue->worker[wndx].tid, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
errout_with_attr:
|
|
|
|
|
pthread_attr_destroy(&attr);
|
|
|
|
|
return -ret;
|
2014-10-10 14:52:04 -06:00
|
|
|
}
|
|
|
|
|
|
2014-10-10 06:22:51 -06:00
|
|
|
/****************************************************************************
|
2026-08-27 20:52:26 +08:00
|
|
|
* Public Functions
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Name: work_queue_create
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
|
|
|
|
* Description:
|
2026-08-27 20:52:26 +08:00
|
|
|
* Create a user-mode custom work queue. This function must only be
|
|
|
|
|
* called from task context.
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
2026-08-27 20:52:26 +08:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
FAR struct kwork_wqueue_s *work_queue_create(FAR const char *name,
|
|
|
|
|
int priority,
|
|
|
|
|
FAR void *stack_addr,
|
|
|
|
|
int stack_size, int nthreads)
|
|
|
|
|
{
|
|
|
|
|
FAR struct usr_wqueue_s *wqueue;
|
|
|
|
|
size_t allocsize;
|
|
|
|
|
int ret;
|
|
|
|
|
int wndx;
|
|
|
|
|
|
|
|
|
|
if (name == NULL || stack_size <= 0 || nthreads < 1 ||
|
|
|
|
|
nthreads > (SIZE_MAX - sizeof(*wqueue)) / sizeof(struct usr_worker_s))
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allocsize = sizeof(*wqueue) +
|
|
|
|
|
nthreads * sizeof(struct usr_worker_s);
|
|
|
|
|
wqueue = calloc(1, allocsize);
|
|
|
|
|
if (wqueue == NULL)
|
|
|
|
|
{
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
list_initialize(&wqueue->q);
|
|
|
|
|
nxmutex_init(&wqueue->lock);
|
|
|
|
|
nxsem_init(&wqueue->wake, 0, 0);
|
|
|
|
|
wqueue->worker = (FAR struct usr_worker_s *)(wqueue + 1);
|
|
|
|
|
wqueue->nthreads = nthreads;
|
|
|
|
|
wqueue->dynamic = true;
|
|
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < nthreads; wndx++)
|
|
|
|
|
{
|
|
|
|
|
wqueue->worker[wndx].wqueue = wqueue;
|
|
|
|
|
nxsem_init(&wqueue->worker[wndx].wait, 0, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = work_thread_create(name, priority, stack_addr, stack_size, wqueue);
|
|
|
|
|
if (ret < 0)
|
|
|
|
|
{
|
|
|
|
|
for (wndx = 0; wndx < nthreads; wndx++)
|
|
|
|
|
{
|
|
|
|
|
nxsem_destroy(&wqueue->worker[wndx].wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nxsem_destroy(&wqueue->wake);
|
|
|
|
|
nxmutex_destroy(&wqueue->lock);
|
|
|
|
|
free(wqueue);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (FAR struct kwork_wqueue_s *)wqueue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
|
* Name: work_queue_free
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
2026-08-27 20:52:26 +08:00
|
|
|
* Description:
|
|
|
|
|
* Destroy a user-mode custom work queue. This function must only be
|
|
|
|
|
* called from task context and must not be called by one of the queue's
|
|
|
|
|
* own worker threads.
|
2014-10-10 06:22:51 -06:00
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
int work_queue_free(FAR struct kwork_wqueue_s *handle)
|
2014-10-10 06:22:51 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
FAR struct usr_wqueue_s *wqueue = (FAR struct usr_wqueue_s *)handle;
|
|
|
|
|
FAR struct work_s *work;
|
|
|
|
|
FAR struct work_s *next;
|
|
|
|
|
pid_t self = gettid();
|
|
|
|
|
int ret;
|
|
|
|
|
int wndx;
|
2014-10-10 06:22:51 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
if (wqueue == NULL || !wqueue->dynamic)
|
2014-10-10 06:22:51 -06:00
|
|
|
{
|
2026-08-27 20:52:26 +08:00
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < wqueue->nthreads; wndx++)
|
|
|
|
|
{
|
|
|
|
|
if (self == wqueue->worker[wndx].tid)
|
|
|
|
|
{
|
|
|
|
|
return -EDEADLK;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-10-10 06:22:51 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
ret = nxmutex_lock(&wqueue->lock);
|
2014-10-10 06:22:51 -06:00
|
|
|
}
|
2026-08-27 20:52:26 +08:00
|
|
|
while (ret < 0);
|
2014-10-10 06:22:51 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
wqueue->exit = true;
|
|
|
|
|
|
|
|
|
|
list_for_every_entry_safe(&wqueue->q, work, next, struct work_s, node)
|
|
|
|
|
{
|
|
|
|
|
list_delete(&work->node);
|
|
|
|
|
work->worker = NULL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nxmutex_unlock(&wqueue->lock);
|
|
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < wqueue->nthreads; wndx++)
|
|
|
|
|
{
|
|
|
|
|
nxsem_post(&wqueue->wake);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (wndx = 0; wndx < wqueue->nthreads; wndx++)
|
|
|
|
|
{
|
|
|
|
|
pthread_join(wqueue->worker[wndx].tid, NULL);
|
|
|
|
|
nxsem_destroy(&wqueue->worker[wndx].wait);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nxsem_destroy(&wqueue->wake);
|
|
|
|
|
nxmutex_destroy(&wqueue->lock);
|
|
|
|
|
free(wqueue);
|
|
|
|
|
return OK;
|
2014-10-10 06:22:51 -06:00
|
|
|
}
|
2026-08-27 20:52:26 +08:00
|
|
|
#endif
|
2014-10-10 06:22:51 -06:00
|
|
|
|
2014-10-10 09:34:03 -06:00
|
|
|
/****************************************************************************
|
2026-08-27 20:52:26 +08:00
|
|
|
* Name: work_queue_priority_wq
|
2014-10-10 09:34:03 -06:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
int work_queue_priority_wq(FAR struct kwork_wqueue_s *handle)
|
|
|
|
|
{
|
|
|
|
|
FAR struct usr_wqueue_s *wqueue = (FAR struct usr_wqueue_s *)handle;
|
|
|
|
|
struct sched_param param;
|
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
|
|
if (wqueue == NULL || wqueue->nthreads < 1)
|
|
|
|
|
{
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ret = sched_getparam(wqueue->worker[0].tid, ¶m);
|
|
|
|
|
return ret == OK ? param.sched_priority : -get_errno();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int work_queue_priority(int qid)
|
|
|
|
|
{
|
|
|
|
|
if (qid != USRWORK)
|
|
|
|
|
{
|
|
|
|
|
return -EINVAL;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return work_queue_priority_wq((FAR struct kwork_wqueue_s *)&g_usrwork);
|
|
|
|
|
}
|
|
|
|
|
|
2014-10-10 09:34:03 -06:00
|
|
|
/****************************************************************************
|
|
|
|
|
* Name: work_usrstart
|
|
|
|
|
*
|
|
|
|
|
* Description:
|
2026-08-27 20:52:26 +08:00
|
|
|
* Start the predefined user work queue.
|
2014-10-10 09:34:03 -06:00
|
|
|
*
|
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
|
|
int work_usrstart(void)
|
|
|
|
|
{
|
2021-07-26 10:36:13 +08:00
|
|
|
int ret;
|
|
|
|
|
#ifndef CONFIG_BUILD_PROTECTED
|
|
|
|
|
pthread_attr_t attr;
|
|
|
|
|
struct sched_param param;
|
|
|
|
|
#endif
|
|
|
|
|
|
2021-07-12 20:58:03 +08:00
|
|
|
#ifdef CONFIG_BUILD_PROTECTED
|
2021-07-26 10:36:13 +08:00
|
|
|
ret = task_create("uwork",
|
2021-08-01 15:27:08 +08:00
|
|
|
CONFIG_LIBC_USRWORKPRIORITY,
|
|
|
|
|
CONFIG_LIBC_USRWORKSTACKSIZE,
|
2026-08-27 20:52:26 +08:00
|
|
|
work_usrtask, NULL);
|
2021-07-26 10:36:13 +08:00
|
|
|
if (ret < 0)
|
2020-08-14 02:59:16 +08:00
|
|
|
{
|
|
|
|
|
int errcode = get_errno();
|
2026-08-27 20:52:26 +08:00
|
|
|
|
2020-08-14 02:59:16 +08:00
|
|
|
DEBUGASSERT(errcode > 0);
|
|
|
|
|
return -errcode;
|
|
|
|
|
}
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
g_usrworker.tid = ret;
|
2014-10-10 14:52:04 -06:00
|
|
|
#else
|
2020-08-14 02:59:16 +08:00
|
|
|
pthread_attr_init(&attr);
|
2026-08-27 20:52:26 +08:00
|
|
|
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
|
2021-08-01 15:27:08 +08:00
|
|
|
pthread_attr_setstacksize(&attr, CONFIG_LIBC_USRWORKSTACKSIZE);
|
2021-07-26 10:36:13 +08:00
|
|
|
pthread_attr_getschedparam(&attr, ¶m);
|
2021-08-01 15:27:08 +08:00
|
|
|
param.sched_priority = CONFIG_LIBC_USRWORKPRIORITY;
|
2020-08-14 02:59:16 +08:00
|
|
|
pthread_attr_setschedparam(&attr, ¶m);
|
2014-10-10 14:52:04 -06:00
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
ret = pthread_create(&g_usrworker.tid, &attr, work_pthread,
|
|
|
|
|
&g_usrworker);
|
|
|
|
|
pthread_attr_destroy(&attr);
|
2020-08-14 02:59:16 +08:00
|
|
|
if (ret != 0)
|
|
|
|
|
{
|
|
|
|
|
return -ret;
|
|
|
|
|
}
|
2014-10-10 14:52:04 -06:00
|
|
|
#endif
|
2026-08-27 20:52:26 +08:00
|
|
|
|
|
|
|
|
return g_usrworker.tid;
|
2014-10-10 09:34:03 -06:00
|
|
|
}
|
|
|
|
|
|
2026-08-27 20:52:26 +08:00
|
|
|
#endif /* CONFIG_LIBC_USRWORK && !__KERNEL__ */
|