Add support for multiple work queues

git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@5081 42af7a65-404d-4744-a932-0658087f49c3
This commit is contained in:
patacongo 2012-09-04 00:54:09 +00:00
parent 6105b42d39
commit 561bea0899
28 changed files with 475 additions and 176 deletions

View File

@ -3250,3 +3250,5 @@
* sched/work_cancel.c: Fix a bad assertion (reported by Mike Smith)
* configs/stm3210e-eval/src/up_idle.c: Correct some power management
compilation errors (reported by Diego Sanchez).
* include/nuttx/wqueue.h, sched/work*, and others: Added logic to support
a second, lower priority work queue (CONFIG_SCHED_LPWORK).

View File

@ -4068,6 +4068,20 @@ build
<code>CONFIG_SIG_SIGWORK</code>: The signal number that will be used to wake-up
the worker thread. Default: 4
</li>
<li>
<code>CONFIG_SCHED_LPWORK</code>: If CONFIG_SCHED_WORKQUEUE</code> is defined, then a single work queue is created by default.
If <code>CONFIG_SCHED_LPWORK</code> is also defined then an additional, lower-priority work queue will also be created.
This lower priority work queue is better suited for more extended processing (such as file system clean-up operations)
</li>
<li>
<code>CONFIG_SCHED_LPWORKPRIORITY</code>: The execution priority of the lower priority worker thread. Default: 50
</li>
<li>
<code>CONFIG_SCHED_LPWORKPERIOD</code>: How often the lower priority worker thread checks for work in units of microseconds. Default: 50*1000 (50 MS).
</li>
<li>
<code>CONFIG_SCHED_LPWORKSTACKSIZE - The stack size allocated for the lower priority worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE.
</li>
<li>
<code>CONFIG_SCHED_WAITPID</code>: Enables the <a href="NuttxUserGuide.html#waitpid"><code>waitpid()</code><a> API
</li>

View File

@ -1,8 +1,8 @@
/****************************************************************************
* arch/arm/src/kinetis/kinetis_sdhc.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
* Copyright (C) 2011-2012 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
@ -2733,7 +2733,7 @@ static void kinetis_callback(void *arg)
/* Yes.. queue it */
fvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg);
(void)work_queue(&priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
(void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
}
else
{

View File

@ -1,8 +1,8 @@
/****************************************************************************
* arch/arm/src/sam3u/sam3u_sdio.c
*
* Copyright (C) 2010 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
* Copyright (C) 2010, 2012 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
@ -2343,7 +2343,7 @@ static void sam3u_callback(void *arg)
/* Yes.. queue it */
fvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg);
(void)work_queue(&priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
(void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
}
else
{

View File

@ -2660,7 +2660,7 @@ static void stm32_callback(void *arg)
/* Yes.. queue it */
fvdbg("Queuing callback to %p(%p)\n", priv->callback, priv->cbarg);
(void)work_queue(&priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
(void)work_queue(HPWORK, &priv->cbwork, (worker_t)priv->callback, priv->cbarg, 0);
}
else
{

View File

@ -361,13 +361,24 @@ defconfig -- This is a configuration file similar to the Linux
if memory reclamation is of high priority). If CONFIG_SCHED_WORKQUEUE
is enabled, then the following options can also be used:
CONFIG_SCHED_WORKPRIORITY - The execution priority of the worker
thread. Default: 50
thread. Default: 192
CONFIG_SCHED_WORKPERIOD - How often the worker thread checks for
work in units of microseconds. Default: 50*1000 (50 MS).
CONFIG_SCHED_WORKSTACKSIZE - The stack size allocated for the worker
thread. Default: CONFIG_IDLETHREAD_STACKSIZE.
CONFIG_SIG_SIGWORK - The signal number that will be used to wake-up
the worker thread. Default: 4
CONFIG_SCHED_LPWORK. If CONFIG_SCHED_WORKQUEUE is defined, then a single
work queue is created by default. If CONFIG_SCHED_LPWORK is also defined
then an additional, lower-priority work queue will also be created. This
lower priority work queue is better suited for more extended processing
(such as file system clean-up operations)
CONFIG_SCHED_LPWORKPRIORITY - The execution priority of the lower priority
worker thread. Default: 50
CONFIG_SCHED_LPWORKPERIOD - How often the lower priority worker thread
checks for work in units of microseconds. Default: 50*1000 (50 MS).
CONFIG_SCHED_LPWORKSTACKSIZE - The stack size allocated for the lower
priority worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE.
CONFIG_SCHED_WAITPID - Enables the waitpid() API
CONFIG_SCHED_ATEXIT - Enables the atexit() API
CONFIG_SCHED_ATEXIT_MAX - By default if CONFIG_SCHED_ATEXIT is

View File

@ -972,7 +972,7 @@ static void tc_worker(FAR void *arg)
/* Set up the next sample event */
ret = work_queue(&priv->work, tc_worker, priv, delay);
ret = work_queue(HPWORK, &priv->work, tc_worker, priv, delay);
ASSERT(ret == 0);
}
@ -1420,7 +1420,7 @@ int arch_tcinitialize(int minor)
*/
priv->state = TC_READY;
ret = work_queue(&priv->work, tc_worker, priv, 0);
ret = work_queue(HPWORK, &priv->work, tc_worker, priv, 0);
if (ret != 0)
{
idbg("Failed to queue work: %d\n", ret);

View File

@ -521,7 +521,7 @@ static int ads7843e_schedule(FAR struct ads7843e_dev_s *priv)
*/
DEBUGASSERT(priv->work.worker == NULL);
ret = work_queue(&priv->work, ads7843e_worker, priv, 0);
ret = work_queue(HPWORK, &priv->work, ads7843e_worker, priv, 0);
if (ret != 0)
{
illdbg("Failed to queue work: %d\n", ret);
@ -1179,7 +1179,7 @@ int ads7843e_register(FAR struct spi_dev_s *dev,
* availability conditions.
*/
ret = work_queue(&priv->work, ads7843e_worker, priv, 0);
ret = work_queue(HPWORK, &priv->work, ads7843e_worker, priv, 0);
if (ret != 0)
{
idbg("Failed to queue work: %d\n", ret);

View File

@ -195,7 +195,7 @@ static int stmpe811_interrupt(int irq, FAR void *context)
* action should be required to protect the work queue.
*/
ret = work_queue(&priv->work, stmpe811_worker, priv, 0);
ret = work_queue(HPWORK, &priv->work, stmpe811_worker, priv, 0);
if (ret != 0)
{
illdbg("Failed to queue work: %d\n", ret);

View File

@ -783,7 +783,7 @@ static void stmpe811_timeout(int argc, uint32_t arg1, ...)
* action should be required to protect the work queue.
*/
ret = work_queue(&priv->timeout, stmpe811_timeoutworker, priv, 0);
ret = work_queue(HPWORK, &priv->timeout, stmpe811_timeoutworker, priv, 0);
if (ret != 0)
{
illdbg("Failed to queue work: %d\n", ret);

View File

@ -775,7 +775,7 @@ static int tsc2007_interrupt(int irq, FAR void *context)
*/
DEBUGASSERT(priv->work.worker == NULL);
ret = work_queue(&priv->work, tsc2007_worker, priv, 0);
ret = work_queue(HPWORK, &priv->work, tsc2007_worker, priv, 0);
if (ret != 0)
{
illdbg("Failed to queue work: %d\n", ret);
@ -1316,7 +1316,7 @@ int tsc2007_register(FAR struct i2c_dev_s *dev,
* availability conditions.
*/
ret = work_queue(&priv->work, tsc2007_worker, priv, 0);
ret = work_queue(HPWORK, &priv->work, tsc2007_worker, priv, 0);
if (ret != 0)
{
idbg("Failed to queue work: %d\n", ret);

View File

@ -1509,7 +1509,7 @@ static int enc_interrupt(int irq, FAR void *context)
* a good thing to do in any event.
*/
return work_queue(&priv->work, enc_worker, (FAR void *)priv, 0);
return work_queue(HPWORK, &priv->work, enc_worker, (FAR void *)priv, 0);
#endif
}

View File

@ -328,7 +328,7 @@ void pm_update(int16_t accum)
/* The work will be performed on the worker thread */
DEBUGASSERT(g_pmglobals.work.worker == NULL);
(void)work_queue(&g_pmglobals.work, pm_worker, (FAR void*)((intptr_t)accum), 0);
(void)work_queue(HPWORK, &g_pmglobals.work, pm_worker, (FAR void*)((intptr_t)accum), 0);
}
#endif /* CONFIG_PM */
#endif /* CONFIG_PM */

View File

@ -213,7 +213,7 @@ static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb)
*/
int ticks = (CONFIG_FS_WRDELAY + CLK_TCK/2) / CLK_TCK;
(void)work_queue(&rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks);
(void)work_queue(LPWORK, &rwb->work, rwb_wrtimeout, (FAR void *)rwb, ticks);
}
/****************************************************************************
@ -222,7 +222,7 @@ static void rwb_wrstarttimeout(FAR struct rwbuffer_s *rwb)
static inline void rwb_wrcanceltimeout(struct rwbuffer_s *rwb)
{
(void)work_cancel(&rwb->work);
(void)work_cancel(LPWORK, &rwb->work);
}
/****************************************************************************

View File

@ -1674,7 +1674,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class)
*/
DEBUGASSERT(priv->work.worker == NULL);
(void)work_queue(&priv->work, usbhost_destroy, priv, 0);
(void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
}
return OK;

View File

@ -1,8 +1,8 @@
/****************************************************************************
* drivers/usbhost/usbhost_skeleton.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <spudmonkey@racsa.co.cr>
* Copyright (C) 2012 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
@ -1015,7 +1015,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class)
uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy);
DEBUGASSERT(priv->work.worker == NULL);
(void)work_queue(&priv->work, usbhost_destroy, priv, 0);
(void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
}
else
{

View File

@ -1786,7 +1786,7 @@ static int usbhost_disconnected(struct usbhost_class_s *class)
uvdbg("Queuing destruction: worker %p->%p\n", priv->work.worker, usbhost_destroy);
DEBUGASSERT(priv->work.worker == NULL);
(void)work_queue(&priv->work, usbhost_destroy, priv, 0);
(void)work_queue(HPWORK, &priv->work, usbhost_destroy, priv, 0);
}
else
{

View File

@ -50,6 +50,75 @@
/****************************************************************************
* Pre-Processor Definitions
****************************************************************************/
/* Configuration ************************************************************/
/* CONFIG_SCHED_WORKQUEUE. Create a dedicated "worker" thread to
* handle delayed processing from interrupt handlers. This feature
* is required for some drivers but, if there are not complaints,
* can be safely disabled. The worker thread also performs
* garbage collection -- completing any delayed memory deallocations
* from interrupt handlers. If the worker thread is disabled,
* then that clean will be performed by the IDLE thread instead
* (which runs at the lowest of priority and may not be appropriate
* if memory reclamation is of high priority). If CONFIG_SCHED_WORKQUEUE
* is enabled, then the following options can also be used:
* CONFIG_SCHED_WORKPRIORITY - The execution priority of the worker
* thread. Default: 192
* CONFIG_SCHED_WORKPERIOD - How often the worker thread checks for
* work in units of microseconds. Default: 50*1000 (50 MS).
* CONFIG_SCHED_WORKSTACKSIZE - The stack size allocated for the worker
* thread. Default: CONFIG_IDLETHREAD_STACKSIZE.
* CONFIG_SIG_SIGWORK - The signal number that will be used to wake-up
* the worker thread. Default: 4
*
* CONFIG_SCHED_LPWORK. If CONFIG_SCHED_WORKQUEUE is defined, then a single
* work queue is created by default. If CONFIG_SCHED_LPWORK is also defined
* then an additional, lower-priority work queue will also be created. This
* lower priority work queue is better suited for more extended processing
* (such as file system clean-up operations)
* CONFIG_SCHED_LPWORKPRIORITY - The execution priority of the lower priority
* worker thread. Default: 50
* CONFIG_SCHED_LPWORKPERIOD - How often the lower priority worker thread
* checks for work in units of microseconds. Default: 50*1000 (50 MS).
* CONFIG_SCHED_LPWORKSTACKSIZE - The stack size allocated for the lower
* priority worker thread. Default: CONFIG_IDLETHREAD_STACKSIZE.
*/
#ifndef CONFIG_SCHED_WORKPRIORITY
# define CONFIG_SCHED_WORKPRIORITY 192
#endif
#ifndef CONFIG_SCHED_WORKPERIOD
# define CONFIG_SCHED_WORKPERIOD (50*1000) /* 50 milliseconds */
#endif
#ifndef CONFIG_SCHED_WORKSTACKSIZE
# define CONFIG_SCHED_WORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE
#endif
#ifdef CONFIG_SCHED_LPWORK
# ifndef CONFIG_SCHED_LPWORKPRIORITY
# define CONFIG_SCHED_LPWORKPRIORITY 50
# endif
# ifndef CONFIG_SCHED_LPWORKPERIOD
# define CONFIG_SCHED_LPWORKPERIOD (50*1000) /* 50 milliseconds */
# endif
# ifndef CONFIG_SCHED_LPWORKSTACKSIZE
# define CONFIG_SCHED_LPWORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE
# endif
#endif
/* Work queue IDs (indices). These are both zero if there is only one work
* queue.
*/
#define HPWORK 0
#ifdef CONFIG_SCHED_LPWORK
# define LPWORK 1
#else
# define LPWORK HPWORK
#endif
/****************************************************************************
* Public Types
@ -86,10 +155,6 @@ extern "C" {
#define EXTERN extern
#endif
/* The task ID of the worker thread */
EXTERN pid_t g_worker;
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
@ -109,6 +174,7 @@ EXTERN pid_t g_worker;
* and remove it from the work queue.
*
* Input parameters:
* qid - The work queue ID
* work - The work structure to queue
* worker - The worker callback to be invoked. The callback will invoked
* on the worker thread of execution.
@ -122,7 +188,8 @@ EXTERN pid_t g_worker;
*
****************************************************************************/
EXTERN int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint32_t delay);
EXTERN int work_queue(int qid, FAR struct work_s *work, worker_t worker,
FAR void *arg, uint32_t delay);
/****************************************************************************
* Name: work_cancel
@ -133,6 +200,7 @@ EXTERN int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint3
* again.
*
* Input parameters:
* qid - The work queue ID
* work - The previously queue work structure to cancel
*
* Returned Value:
@ -140,7 +208,7 @@ EXTERN int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint3
*
****************************************************************************/
EXTERN int work_cancel(struct work_s *work);
EXTERN int work_cancel(int qid, FAR struct work_s *work);
/****************************************************************************
* Name: work_signal
@ -151,14 +219,14 @@ EXTERN int work_cancel(struct work_s *work);
* user to force an immediate re-assessment of pending work.
*
* Input parameters:
* None
* qid - The work queue ID
*
* Returned Value:
* Zero on success, a negated errno on failure
*
****************************************************************************/
#define work_signal() kill(g_worker, SIGWORK)
EXTERN int work_signal(int qid);
/****************************************************************************
* Name: work_available

View File

@ -166,6 +166,39 @@ config SIG_SIGWORK
The signal number that will be used to wake-up the worker thread.
Default: 4
config SCHED_LPWORK
bool "Enable a lower priority worker thread"
default n
depends on SCHED_WORKQUEUE
---help---
If SCHED_WORKQUEUE is defined, then a single work queue is created by
default. If SCHED_LPWORK is also defined then an additional, lower-
priority work queue will also be created. This lower priority work
queue is better suited for more extended processing (such as file system
clean-up operations)
config SCHED_LPWORKPRIORITY
int "Lower priority worker thread priority"
default 192
depends on SCHED_LPWORK
---help---
The execution priority of the lopwer priority worker thread. Default: 192
config SCHED_LPWORKPERIOD
int "Lower priority worker thread period"
default 50000
depends on SCHED_LPWORK
---help---
How often the lower priority worker thread checks for work in units
of microseconds. Default: 50*1000 (50 MS).
config SCHED_LPWORKSTACKSIZE
int "Lower priority worker thread stack size"
default 2048
depends on SCHED_LPWORK
---help---
The stack size allocated for the lower priority worker thread. Default: 2K.
config SCHED_WAITPID
bool "Enable waitpid() API"
default n

View File

@ -141,7 +141,7 @@ TIMER_SRCS = timer_initialize.c timer_create.c timer_delete.c timer_getoverrun.c
endif
ifeq ($(CONFIG_SCHED_WORKQUEUE),y)
WORK_SRCS = work_thread.c work_queue.c work_cancel.c
WORK_SRCS = work_thread.c work_queue.c work_cancel.c work_signal.c
endif
ifeq ($(CONFIG_PAGING),y)

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/os_bringup.c
*
* Copyright (C) 2011 Gregory Nutt. All rights reserved.
* Copyright (C) 2011-2012 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* With extensions by:
@ -47,6 +47,7 @@
#include <debug.h>
#include <nuttx/init.h>
#include <nuttx/wqueue.h>
#include "os_internal.h"
#ifdef CONFIG_PAGING
@ -149,10 +150,23 @@ int os_bringup(void)
#ifdef CONFIG_SCHED_WORKQUEUE
svdbg("Starting worker thread\n");
g_worker = KERNEL_THREAD("work", CONFIG_SCHED_WORKPRIORITY,
CONFIG_SCHED_WORKSTACKSIZE,
(main_t)work_thread, (const char **)NULL);
ASSERT(g_worker != ERROR);
g_work[HPWORK].pid = KERNEL_THREAD("work0", CONFIG_SCHED_WORKPRIORITY,
CONFIG_SCHED_WORKSTACKSIZE,
(main_t)work_hpthread, (const char **)NULL);
ASSERT(g_work[HPWORK].pid != ERROR);
/* Start a lower priority worker thread for other, non-critical continuation
* tasks
*/
#ifdef CONFIG_SCHED_LPWORK
svdbg("Starting worker thread\n");
g_work[LPWORK].pid = KERNEL_THREAD("work1", CONFIG_SCHED_LPWORKPRIORITY,
CONFIG_SCHED_LPWORKSTACKSIZE,
(main_t)work_lpthread, (const char **)NULL);
ASSERT(g_work[LPWORK].pid != ERROR);
#endif
#endif
/* Once the operating system has been initialized, the system must be

View File

@ -1,7 +1,7 @@
/************************************************************************
* sched/sched_free.c
*
* Copyright (C) 2007, 2009 Gregory Nutt. All rights reserved.
* Copyright (C) 2007, 2009, 2012 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -99,7 +99,7 @@ void sched_free(FAR void *address)
/* Signal the worker thread that is has some clean up to do */
#ifdef CONFIG_SCHED_WORKQUEUE
work_signal();
work_signal(LPWORK);
#endif
irqrestore(saved_state);
}

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/work_cancel.c
*
* Copyright (C) 2009-2010 Gregory Nutt. All rights reserved.
* Copyright (C) 2009-2010, 2012 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -74,6 +74,7 @@
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: work_cancel
*
@ -83,6 +84,7 @@
* again.
*
* Input parameters:
* qid - The work queue ID
* work - The previously queue work structure to cancel
*
* Returned Value:
@ -90,11 +92,12 @@
*
****************************************************************************/
int work_cancel(struct work_s *work)
int work_cancel(int qid, FAR struct work_s *work)
{
FAR struct wqueue_s *wqueue = &g_work[qid];
irqstate_t flags;
DEBUGASSERT(work != NULL);
DEBUGASSERT(work != NULL && (unsigned)qid < NWORKERS);
/* Cancelling the work is simply a matter of removing the work structure
* from the work queue. This must be done with interrupts disabled because
@ -106,18 +109,19 @@ int work_cancel(struct work_s *work)
{
/* A little test of the integrity of the work queue */
DEBUGASSERT(work->dq.flink ||(FAR dq_entry_t *)work == g_work.tail);
DEBUGASSERT(work->dq.blink ||(FAR dq_entry_t *)work == g_work.head);
DEBUGASSERT(work->dq.flink ||(FAR dq_entry_t *)work == wqueue->q.tail);
DEBUGASSERT(work->dq.blink ||(FAR dq_entry_t *)work == wqueue->q.head);
/* Remove the entry from the work queue and make sure that it is
* mark as availalbe (i.e., the worker field is nullified).
*/
dq_rem((FAR dq_entry_t *)work, &g_work);
dq_rem((FAR dq_entry_t *)work, &wqueue->q);
work->worker = NULL;
}
irqrestore(flags);
return OK;
}
#endif /* CONFIG_SCHED_WORKQUEUE */

View File

@ -51,51 +51,49 @@
/* Configuration ************************************************************/
#ifndef CONFIG_SCHED_WORKPRIORITY
# define CONFIG_SCHED_WORKPRIORITY 50
#endif
#ifndef CONFIG_SCHED_WORKPERIOD
# define CONFIG_SCHED_WORKPERIOD (50*1000) /* 50 milliseconds */
#endif
#ifndef CONFIG_SCHED_WORKSTACKSIZE
# define CONFIG_SCHED_WORKSTACKSIZE CONFIG_IDLETHREAD_STACKSIZE
#endif
#ifdef CONFIG_DISABLE_SIGNALS
# warning "Worker thread support requires signals"
#endif
#ifdef CONFIG_SCHED_LPWORK
# define NWORKERS 2
#else
# define NWORKERS 1
#endif
/****************************************************************************
* Public Types
****************************************************************************/
#ifndef __ASSEMBLY__
/* This structure defines the state on one work queue */
struct wqueue_s
{
pid_t pid; /* The task ID of the worker thread */
struct dq_queue_s q; /* The queue of pending work */
};
/****************************************************************************
* Public Data
****************************************************************************/
/* The queue of pending work */
/* The state of each work queue */
extern struct dq_queue_s g_work;
/* The task ID of the worker thread */
extern pid_t g_worker;
extern struct wqueue_s g_work[NWORKERS];
/****************************************************************************
* Public Function Prototypes
****************************************************************************/
/****************************************************************************
* Name: work_thread
* Name: work_hpthread and work_lpthread
*
* Description:
* This is the main worker thread that performs actions placed on the work
* queue. It also performs periodic garbage collection (that is performed
* by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined).
* These are the main worker threads that performs actions placed on the
* work lists. One thread also performs periodic garbage collection (that
* is performed by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined).
*
* Input parameters:
* argc, argv (not used)
@ -105,7 +103,11 @@ extern pid_t g_worker;
*
****************************************************************************/
int work_thread(int argc, char *argv[]);
int work_hpthread(int argc, char *argv[]);
#ifdef CONFIG_SCHED_LPWORK
int work_lpthread(int argc, char *argv[]);
#endif
#endif /* __ASSEMBLY__ */
#endif /* CONFIG_SCHED_WORKQUEUE */

View File

@ -76,6 +76,7 @@
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: work_queue
*
@ -91,6 +92,7 @@
* and remove it from the work queue.
*
* Input parameters:
* qid - The work queue ID (index)
* work - The work structure to queue
* worker - The worker callback to be invoked. The callback will invoked
* on the worker thread of execution.
@ -104,11 +106,13 @@
*
****************************************************************************/
int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint32_t delay)
int work_queue(int qid, FAR struct work_s *work, worker_t worker,
FAR void *arg, uint32_t delay)
{
FAR struct wqueue_s *wqueue = &g_work[qid];
irqstate_t flags;
DEBUGASSERT(work != NULL);
DEBUGASSERT(work != NULL && (unsigned)qid < NWORKERS);
/* First, initialize the work structure */
@ -123,8 +127,10 @@ int work_queue(struct work_s *work, worker_t worker, FAR void *arg, uint32_t del
flags = irqsave();
work->qtime = clock_systimer(); /* Time work queued */
dq_addlast((FAR dq_entry_t *)work, &g_work);
work_signal(); /* Wake up the worker thread */
dq_addlast((FAR dq_entry_t *)work, &wqueue->q);
kill(wqueue->pid, SIGWORK); /* Wake up the worker thread */
irqrestore(flags);
return OK;
}

96
sched/work_signal.c Normal file
View File

@ -0,0 +1,96 @@
/****************************************************************************
* sched/work_signal.c
*
* Copyright (C) 2009-2012 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 <signal.h>
#include <assert.h>
#include <nuttx/wqueue.h>
#include "work_internal.h"
#ifdef CONFIG_SCHED_WORKQUEUE
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/****************************************************************************
* Private Type Declarations
****************************************************************************/
/****************************************************************************
* Public Variables
****************************************************************************/
/****************************************************************************
* Private Variables
****************************************************************************/
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: work_signal
*
* Description:
* Signal the worker thread to process the work queue now. This function
* is used internally by the work logic but could also be used by the
* user to force an immediate re-assessment of pending work.
*
* Input parameters:
* qid - The work queue ID
*
* Returned Value:
* Zero on success, a negated errno on failure
*
****************************************************************************/
int work_signal(int qid)
{
DEBUGASSERT((unsigned)qid < NWORKERS);
return kill(g_work[qid].pid, SIGWORK);
}
#endif /* CONFIG_SCHED_WORKQUEUE */

View File

@ -1,7 +1,7 @@
/****************************************************************************
* sched/work_thread.c
*
* Copyright (C) 2009-2011 Gregory Nutt. All rights reserved.
* Copyright (C) 2009-2012 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* Redistribution and use in source and binary forms, with or without
@ -67,15 +67,9 @@
* Public Variables
****************************************************************************/
/* The queue of pending work */
/* The state of each work queue */
struct dq_queue_s g_work;
/* The task ID of the worker thread */
#ifdef CONFIG_SCHED_WORKQUEUE
pid_t g_worker;
#endif
struct wqueue_s g_work[NWORKERS];
/****************************************************************************
* Private Variables
@ -85,16 +79,116 @@ pid_t g_worker;
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: work_process
*
* Description:
* This is the logic that performs actions placed on any work list.
*
* Input parameters:
* wqueue - Describes the work queue to be processed
*
* Returned Value:
* None
*
****************************************************************************/
static void work_process(FAR struct wqueue_s *wqueue)
{
volatile FAR struct work_s *work;
worker_t worker;
irqstate_t flags;
FAR void *arg;
uint32_t elapsed;
uint32_t remaining;
uint32_t next;
/* Then process queued work. We need to keep interrupts disabled while
* we process items in the work list.
*/
next = CONFIG_SCHED_WORKPERIOD / USEC_PER_TICK;
flags = irqsave();
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. qtime is the time that the work was added
* to the work queue. It will always be greater than or equal to
* zero. Therefore a delay of zero will always execute immediately.
*/
elapsed = clock_systimer() - work->qtime;
if (elapsed >= work->delay)
{
/* Remove the ready-to-execute work from the list */
(void)dq_rem((struct dq_entry_s *)work, &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;
arg = work->arg;
/* Mark the work as no longer being queued */
work->worker = NULL;
/* Do the work. Re-enable interrupts while the work is being
* performed... we don't have any idea how long that will take!
*/
irqrestore(flags);
worker(arg);
/* Now, unfortunately, since we re-enabled interrupts we don't
* know the state of the work list and we will have to start
* back at the head of the list.
*/
flags = irqsave();
work = (FAR struct work_s *)wqueue->q.head;
}
else
{
/* This one is not ready.. will it be ready before the next
* scheduled wakeup interval?
*/
remaining = elapsed - work->delay;
if (remaining < next)
{
/* Yes.. Then schedule to wake up when the work is ready */
next = remaining;
}
/* Then try the next in the list. */
work = (FAR struct work_s *)work->dq.flink;
}
}
/* Wait awhile to check the work list. We will wait here until either
* the time elapses or until we are awakened by a signal.
*/
usleep(next * USEC_PER_TICK);
irqrestore(flags);
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: work_thread
* Name: work_hpthread and work_lpthread
*
* Description:
* This is the main worker thread that performs actions placed on the work
* list. It also performs periodic garbage collection (that is performed
* by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined).
* These are the main worker threads that performs actions placed on the
* work lists. One thread also performs periodic garbage collection (that
* is performed by the idle thread if CONFIG_SCHED_WORKQUEUE is not defined).
*
* Input parameters:
* argc, argv (not used)
@ -104,30 +198,40 @@ pid_t g_worker;
*
****************************************************************************/
int work_thread(int argc, char *argv[])
int work_hpthread(int argc, char *argv[])
{
volatile FAR struct work_s *work;
worker_t worker;
FAR void *arg;
uint32_t elapsed;
uint32_t remaining;
uint32_t next;
int usec;
irqstate_t flags;
/* Loop forever */
usec = CONFIG_SCHED_WORKPERIOD;
flags = irqsave();
for (;;)
{
/* Wait awhile to check the work list. We will wait here until either
* the time elapses or until we are awakened by a signal.
/* First, perform garbage collection. This cleans-up memory de-allocations
* that were queued because they could not be freed in that execution
* context (for example, if the memory was freed from an interrupt handler).
* NOTE: If the work thread is disabled, this clean-up is performed by
* the IDLE thread (at a very, very lower priority).
*/
usleep(usec);
irqrestore(flags);
#ifdef CONFIG_SCHED_LPWORK
sched_garbagecollection();
#endif
/* Then process queued work. We need to keep interrupts disabled while
* we process items in the work list.
*/
work_process(&g_work[HPWORK]);
}
return OK; /* To keep some compilers happy */
}
#ifdef CONFIG_SCHED_LPWORK
int work_lpthread(int argc, char *argv[])
{
/* Loop forever */
for (;;)
{
/* First, perform garbage collection. This cleans-up memory de-allocations
* that were queued because they could not be freed in that execution
* context (for example, if the memory was freed from an interrupt handler).
@ -141,75 +245,12 @@ int work_thread(int argc, char *argv[])
* we process items in the work list.
*/
next = CONFIG_SCHED_WORKPERIOD / USEC_PER_TICK;
flags = irqsave();
work = (FAR struct work_s *)g_work.head;
while (work)
{
/* Is this work ready? It is ready if there is no delay or if
* the delay has elapsed. qtime is the time that the work was added
* to the work queue. It will always be greater than or equal to
* zero. Therefore a delay of zero will always execute immediately.
*/
elapsed = clock_systimer() - work->qtime;
if (elapsed >= work->delay)
{
/* Remove the ready-to-execute work from the list */
(void)dq_rem((struct dq_entry_s *)work, &g_work);
/* 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;
arg = work->arg;
/* Mark the work as no longer being queued */
work->worker = NULL;
/* Do the work. Re-enable interrupts while the work is being
* performed... we don't have any idea how long that will take!
*/
irqrestore(flags);
worker(arg);
/* Now, unfortunately, since we re-enabled interrupts we don't
* know the state of the work list and we will have to start
* back at the head of the list.
*/
flags = irqsave();
work = (FAR struct work_s *)g_work.head;
}
else
{
/* This one is not ready.. will it be ready before the next
* scheduled wakeup interval?
*/
remaining = elapsed - work->delay;
if (remaining < next)
{
/* Yes.. Then schedule to wake up when the work is ready */
next = remaining;
}
/* Then try the next in the list. */
work = (FAR struct work_s *)work->dq.flink;
}
}
/* Now calculate the microsecond delay we should wait */
usec = next * USEC_PER_TICK;
work_process(&g_work[LPWORK]);
}
return OK; /* To keep some compilers happy */
}
#endif /* CONFIG_SCHED_LPWORK */
#endif /* CONFIG_SCHED_WORKQUEUE */

View File

@ -101,17 +101,17 @@ if [ ! -d "${configpath}" ]; then
fi
if [ ! -r "${configpath}/Make.defs" ]; then
echo "File ${configpath}/Make.defs does not exist"
echo "File \"${configpath}/Make.defs\" does not exist"
exit 4
fi
if [ ! -r "${configpath}/setenv.sh" ]; then
echo "File ${configpath}/setenv.sh does not exist"
echo "File \"${configpath}/setenv.sh\" does not exist"
exit 5
fi
if [ ! -r "${configpath}/defconfig" ]; then
echo "File ${configpath}/defconfig does not exist"
echo "File \"${configpath}/defconfig\" does not exist"
exit 6
fi
@ -127,7 +127,7 @@ if [ -z "${appdir}" ]; then
appdir=`grep CONFIG_APPS_DIR= "${configpath}/defconfig" | cut -d'=' -f2`
fi
# Check for the apps/ dir in the usual place if appdir was not provided
# Check for the apps/ directory in the usual place if appdir was not provided
if [ -z "${appdir}" ]; then
@ -152,7 +152,15 @@ if [ -z "${appdir}" ]; then
fi
fi
# Okay... setup the configuration
# If appsdir was provided (or discovered) then make sure that the apps/
# directory exists
if [ ! -z "${appdir}" -a ! -d "${TOPDIR}/${appdir}" ]; then
echo "Directory \"${TOPDIR}/${appdir}\" does not exist"
exit 7
fi
# Okay... Everything looks good. Setup the configuration
install -C "${configpath}/Make.defs" "${TOPDIR}/." || \
{ echo "Failed to copy ${configpath}/Make.defs" ; exit 7 ; }