/**************************************************************************** * libs/libc/wqueue/work_usrthread.c * * 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 * * http://www.apache.org/licenses/LICENSE-2.0 * * 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. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "wqueue/wqueue.h" #if defined(CONFIG_LIBC_USRWORK) && !defined(__KERNEL__) /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #ifdef CONFIG_SYSTEM_TIME64 # define WORK_DELAY_MAX UINT64_MAX #else # define WORK_DELAY_MAX UINT32_MAX #endif /**************************************************************************** * Private Type Declarations ****************************************************************************/ /**************************************************************************** * Public Data ****************************************************************************/ /* The state of the user mode work queue. */ struct usr_wqueue_s g_usrwork = { {NULL, NULL}, NXMUTEX_INITIALIZER, SEM_INITIALIZER(0), }; /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: work_process * * Description: * This is the logic that performs actions placed on any work list. This * logic is the common underlying logic to all work queues. This logic is * part of the internal implementation of each work queue; it should not * be called from application level logic. * * Input Parameters: * wqueue - Describes the work queue to be processed * * Returned Value: * None * ****************************************************************************/ static void work_process(FAR struct usr_wqueue_s *wqueue) { volatile FAR struct work_s *work; worker_t worker; FAR void *arg; sclock_t elapsed; clock_t next; int ret; /* Then process queued work. Lock the work queue while we process items * in the work list. */ next = WORK_DELAY_MAX; ret = nxmutex_lock(&wqueue->lock); if (ret < 0) { /* Break out earlier if we were awakened by a signal */ return; } /* 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 */ work = (FAR struct work_s *)wqueue->q.head; while (work) { /* 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. */ elapsed = clock() - work->u.s.qtime; /* Is this delay work ready? */ if (elapsed >= 0) { /* Remove the ready-to-execute work from the list */ dq_remfirst(&wqueue->q); /* Extract the work description from the entry (in case the work * instance by the re-used after it has been de-queued). */ worker = work->worker; /* Check for a race condition where the work may be nullified * before it is removed from the queue. */ if (worker != NULL) { /* Extract the work argument before unlocking the work queue */ arg = work->arg; /* Mark the work as no longer being queued */ work->worker = NULL; /* 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); worker(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. */ ret = nxmutex_lock(&wqueue->lock); if (ret < 0) { /* Break out earlier if we were awakened by a signal */ return; } } work = (FAR struct work_s *)wqueue->q.head; } else { next = work->u.s.qtime - clock(); break; } } /* Unlock the work queue before waiting. */ nxmutex_unlock(&wqueue->lock); if (next == WORK_DELAY_MAX) { /* Wait indefinitely until work_queue has new items */ nxmutex_lock(&wqueue->wake); } else { 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(next, &delay); clock_timespec_add(&now, &delay, &rqtp); _SEM_TIMEDWAIT(&wqueue->wake, &rqtp); } } /**************************************************************************** * Name: work_usrthread * * Description: * This is the worker thread that performs the actions placed on the user * work queue. * * This is a user mode work queue. It must be used by applications for * miscellaneous operations. The user work thread must be started by * application start-up logic by calling work_usrstart(). * * Input Parameters: * argc, argv (not used) * * Returned Value: * Does not return * ****************************************************************************/ #ifdef CONFIG_BUILD_PROTECTED static int work_usrthread(int argc, char *argv[]) #else static pthread_addr_t work_usrthread(pthread_addr_t arg) #endif { /* Loop forever */ for (; ; ) { /* Then process queued work. We need to keep the work queue locked * while we process items in the work list. */ work_process(&g_usrwork); } #ifdef CONFIG_BUILD_PROTECTED return OK; /* To keep some compilers happy */ #else return NULL; /* To keep some compilers happy */ #endif } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: work_usrstart * * Description: * Start the user mode work queue. * * Input Parameters: * None * * Returned Value: * The task ID of the worker thread is returned on success. A negated * errno value is returned on failure. * ****************************************************************************/ int work_usrstart(void) { int ret; #ifndef CONFIG_BUILD_PROTECTED pthread_t usrwork; pthread_attr_t attr; struct sched_param param; #endif /* Initialize the work queue */ dq_init(&g_usrwork.q); #ifdef CONFIG_BUILD_PROTECTED /* Start a user-mode worker thread for use by applications. */ ret = task_create("uwork", CONFIG_LIBC_USRWORKPRIORITY, CONFIG_LIBC_USRWORKSTACKSIZE, work_usrthread, NULL); if (ret < 0) { int errcode = get_errno(); DEBUGASSERT(errcode > 0); return -errcode; } return ret; #else /* Start a user-mode worker thread for use by applications. */ pthread_attr_init(&attr); pthread_attr_setstacksize(&attr, CONFIG_LIBC_USRWORKSTACKSIZE); pthread_attr_getschedparam(&attr, ¶m); param.sched_priority = CONFIG_LIBC_USRWORKPRIORITY; pthread_attr_setschedparam(&attr, ¶m); ret = pthread_create(&usrwork, &attr, work_usrthread, NULL); if (ret != 0) { return -ret; } /* Detach because the return value and completion status will not be * requested. */ pthread_detach(usrwork); return (pid_t)usrwork; #endif } #endif /* CONFIG_LIBC_USRWORK && !__KERNEL__*/