mq_timedsend(): Do check for time errors if the message queue is not full. Noted by Freddie Chopin
This commit is contained in:
parent
36e88e504a
commit
4c6057eca1
22
TODO
22
TODO
@ -12,7 +12,7 @@ nuttx/
|
||||
(1) Memory Managment (mm/)
|
||||
(3) Signals (sched/signal, arch/)
|
||||
(2) pthreads (sched/pthread)
|
||||
(1) Message Queues (sched/mqueue)
|
||||
(0) Message Queues (sched/mqueue)
|
||||
(4) C++ Support
|
||||
(6) Binary loaders (binfmt/)
|
||||
(12) Network (net/, drivers/net)
|
||||
@ -393,24 +393,8 @@ o pthreads (sched/pthreads)
|
||||
solution. So I discarded a few hours of programming. Not a
|
||||
big loss from the experience I gained."
|
||||
|
||||
Message Queues (sched/mqueue)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Title: mq_timedsend() ERROR DETECTION
|
||||
Description: mq_timedsend() will always return an error an invalid time is
|
||||
provided. However, OpenGroup.org says:
|
||||
|
||||
"Under no circumstance shall the operation fail with a timeout
|
||||
if there is sufficient room in the queue to add the message
|
||||
immediately. The validity of the abstime parameter need not be
|
||||
checked when there is sufficient room in the queue."
|
||||
|
||||
Status: Open
|
||||
Priority: Low. This is a valid POSIX compliance issue, but not thought
|
||||
to be really important in real work programming. It could
|
||||
be used to conditionally block like O_NONBLOCK by providing a
|
||||
bad time to the function. That seeks hokey and I can't think
|
||||
of any other real world use case the demands this functionality.
|
||||
o Message Queues (sched/mqueue)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
o Kernel/Protected Build
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
@ -233,7 +233,11 @@ Configurations
|
||||
Configures the NuttShell (nsh) located at examples/nsh.
|
||||
NOTES:
|
||||
|
||||
1. Default stack sizes are large and should really be tuned to reduce
|
||||
1. The serial console is configured by default for use with and Arduino
|
||||
serial shield (UART3). You will need to reconfigure if you will
|
||||
to use a different U[S]ART.
|
||||
|
||||
2. Default stack sizes are large and should really be tuned to reduce
|
||||
the RAM footprint:
|
||||
|
||||
CONFIG_ARCH_INTERRUPTSTACK=2048
|
||||
@ -242,10 +246,18 @@ Configurations
|
||||
CONFIG_PTHREAD_STACK_DEFAULT=2048
|
||||
... and others ...
|
||||
|
||||
2. NSH built-in applications are supported.
|
||||
3. NSH built-in applications are supported.
|
||||
|
||||
Binary Formats:
|
||||
CONFIG_BUILTIN=y : Enable support for built-in programs
|
||||
|
||||
Application Configuration:
|
||||
CONFIG_NSH_BUILTIN_APPS=y : Enable starting apps from NSH command line
|
||||
|
||||
4. Performance-related Configuration settings:
|
||||
|
||||
# CONFIG_ARMV7M_ICACHE is not set : Can be enabled, not verified
|
||||
# CONFIG_ARMV7M_DCACHE is not set : Can be enabled, not verified
|
||||
# CONFIG_ARCH_FPU is not set : Can be enabled, not verified
|
||||
# CONFIG_ARMV7M_ITCM is not set : Support not yet in place
|
||||
# CONFIG_ARMV7M_DTCM is not set : Support not yet in place
|
||||
|
@ -212,10 +212,10 @@ CONFIG_SAMV7_HAVE_USART2=y
|
||||
# CONFIG_SAMV7_TWIM0 is not set
|
||||
# CONFIG_SAMV7_TWIM1 is not set
|
||||
# CONFIG_SAMV7_TWIM2 is not set
|
||||
CONFIG_SAMV7_UART0=y
|
||||
# CONFIG_SAMV7_UART0 is not set
|
||||
# CONFIG_SAMV7_UART1 is not set
|
||||
# CONFIG_SAMV7_UART2 is not set
|
||||
# CONFIG_SAMV7_UART3 is not set
|
||||
CONFIG_SAMV7_UART3=y
|
||||
# CONFIG_SAMV7_UART4 is not set
|
||||
# CONFIG_SAMV7_USBDEVHS is not set
|
||||
# CONFIG_SAMV7_USBHOSTHS is not set
|
||||
@ -480,10 +480,10 @@ CONFIG_SERIAL=y
|
||||
# CONFIG_DEV_LOWCONSOLE is not set
|
||||
# CONFIG_16550_UART is not set
|
||||
# CONFIG_ARCH_HAVE_UART is not set
|
||||
CONFIG_ARCH_HAVE_UART0=y
|
||||
# CONFIG_ARCH_HAVE_UART0 is not set
|
||||
# CONFIG_ARCH_HAVE_UART1 is not set
|
||||
# CONFIG_ARCH_HAVE_UART2 is not set
|
||||
# CONFIG_ARCH_HAVE_UART3 is not set
|
||||
CONFIG_ARCH_HAVE_UART3=y
|
||||
# CONFIG_ARCH_HAVE_UART4 is not set
|
||||
# CONFIG_ARCH_HAVE_UART5 is not set
|
||||
# CONFIG_ARCH_HAVE_UART6 is not set
|
||||
@ -511,21 +511,22 @@ CONFIG_STANDARD_SERIAL=y
|
||||
# CONFIG_SERIAL_OFLOWCONTROL is not set
|
||||
CONFIG_ARCH_HAVE_SERIAL_TERMIOS=y
|
||||
# CONFIG_SERIAL_TERMIOS is not set
|
||||
CONFIG_UART0_SERIAL_CONSOLE=y
|
||||
# CONFIG_UART0_SERIAL_CONSOLE is not set
|
||||
CONFIG_UART3_SERIAL_CONSOLE=y
|
||||
# CONFIG_OTHER_SERIAL_CONSOLE is not set
|
||||
# CONFIG_NO_SERIAL_CONSOLE is not set
|
||||
|
||||
#
|
||||
# UART0 Configuration
|
||||
# UART3 Configuration
|
||||
#
|
||||
CONFIG_UART0_RXBUFSIZE=256
|
||||
CONFIG_UART0_TXBUFSIZE=256
|
||||
CONFIG_UART0_BAUD=115200
|
||||
CONFIG_UART0_BITS=8
|
||||
CONFIG_UART0_PARITY=0
|
||||
CONFIG_UART0_2STOP=0
|
||||
# CONFIG_UART0_IFLOWCONTROL is not set
|
||||
# CONFIG_UART0_OFLOWCONTROL is not set
|
||||
CONFIG_UART3_RXBUFSIZE=256
|
||||
CONFIG_UART3_TXBUFSIZE=256
|
||||
CONFIG_UART3_BAUD=115200
|
||||
CONFIG_UART3_BITS=8
|
||||
CONFIG_UART3_PARITY=0
|
||||
CONFIG_UART3_2STOP=0
|
||||
# CONFIG_UART3_IFLOWCONTROL is not set
|
||||
# CONFIG_UART3_OFLOWCONTROL is not set
|
||||
# CONFIG_USBDEV is not set
|
||||
# CONFIG_USBHOST is not set
|
||||
# CONFIG_WIRELESS is not set
|
||||
|
@ -180,4 +180,3 @@ int mq_send(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio)
|
||||
sched_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -306,7 +306,7 @@ int mq_waitsend(mqd_t mqdes)
|
||||
*
|
||||
* Description:
|
||||
* This is internal, common logic shared by both mq_send and mq_timesend.
|
||||
* This function adds the specificied message (msg) to the message queue
|
||||
* This function adds the specified message (msg) to the message queue
|
||||
* (mqdes). Then it notifies any tasks that were waiting for message
|
||||
* queue notifications setup by mq_notify. And, finally, it awakens any
|
||||
* tasks that were waiting for the message not empty event.
|
||||
|
@ -1,7 +1,7 @@
|
||||
/****************************************************************************
|
||||
* sched/mqueue/mq_timedsend.c
|
||||
*
|
||||
* Copyright (C) 2007-2009, 2011, 2013-2014 Gregory Nutt. All rights reserved.
|
||||
* Copyright (C) 2007-2009, 2011, 2013-2015 Gregory Nutt. All rights reserved.
|
||||
* Author: Gregory Nutt <gnutt@nuttx.org>
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
@ -187,6 +187,8 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio,
|
||||
FAR struct mqueue_inode_s *msgq;
|
||||
FAR struct mqueue_msg_s *mqmsg = NULL;
|
||||
irqstate_t saved_state;
|
||||
int ticks;
|
||||
int result;
|
||||
int ret = ERROR;
|
||||
|
||||
DEBUGASSERT(up_interrupt_context() == false && rtcb->waitdog == NULL);
|
||||
@ -200,16 +202,42 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio,
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
{
|
||||
set_errno(EINVAL);
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Get a pointer to the message queue */
|
||||
|
||||
msgq = mqdes->msgq;
|
||||
|
||||
/* OpenGroup.org: "Under no circumstance shall the operation fail with a
|
||||
* timeout if there is sufficient room in the queue to add the message
|
||||
* immediately. The validity of the abstime parameter need not be checked
|
||||
* when there is sufficient room in the queue."
|
||||
*
|
||||
* Also ignore the time value if for some crazy reason we were called from
|
||||
* an interrupt handler. This probably really should be an assertion.
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
if (msgq->nmsgs < msgq->maxmsgs || up_interrupt_context())
|
||||
{
|
||||
/* The message queue is not full... Ignore the time parameter and
|
||||
* let mq_send do the work.
|
||||
*/
|
||||
|
||||
ret = mq_send(mqdes, msg, msglen, prio);
|
||||
sched_unlock();
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* The message queue is full... We are going to wait. Now we must have a
|
||||
* valid time value.
|
||||
*/
|
||||
|
||||
if (!abstime || abstime->tv_nsec < 0 || abstime->tv_nsec >= 1000000000)
|
||||
{
|
||||
set_errno(EINVAL);
|
||||
sched_unlock();
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Create a watchdog. We will not actually need this watchdog
|
||||
* unless the queue is full, but we will reserve it up front
|
||||
* before we enter the following critical section.
|
||||
@ -219,98 +247,81 @@ int mq_timedsend(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio,
|
||||
if (!rtcb->waitdog)
|
||||
{
|
||||
set_errno(EINVAL);
|
||||
sched_unlock();
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
/* Allocate a message structure:
|
||||
* - If we are called from an interrupt handler, or
|
||||
* - If the message queue is not full, or
|
||||
/* We are not in an interrupt handler and the message queue is full.
|
||||
* Set up a timed wait for the message queue to become non-full.
|
||||
*
|
||||
* Convert the timespec to clock ticks. We must have interrupts
|
||||
* disabled here so that this time stays valid until the wait begins.
|
||||
*/
|
||||
|
||||
sched_lock();
|
||||
saved_state = irqsave();
|
||||
if (up_interrupt_context() || /* In an interrupt handler */
|
||||
msgq->nmsgs < msgq->maxmsgs) /* OR Message queue not full */
|
||||
{
|
||||
/* Allocate the message */
|
||||
result = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);
|
||||
|
||||
irqrestore(saved_state);
|
||||
mqmsg = mq_msgalloc();
|
||||
/* If the time has already expired and the message queue is empty,
|
||||
* return immediately.
|
||||
*/
|
||||
|
||||
if (result == OK && ticks <= 0)
|
||||
{
|
||||
result = ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Handle any time-related errors */
|
||||
|
||||
if (result != OK)
|
||||
{
|
||||
set_errno(result);
|
||||
ret = ERROR;
|
||||
}
|
||||
|
||||
/* Start the watchdog and begin the wait for MQ not full */
|
||||
|
||||
else
|
||||
{
|
||||
int ticks;
|
||||
/* Start the watchdog */
|
||||
|
||||
/* We are not in an interupt handler and the message queue is full.
|
||||
* set up a timed wait for the message queue to become non-full.
|
||||
*
|
||||
* Convert the timespec to clock ticks. We must have interrupts
|
||||
* disabled here so that this time stays valid until the wait begins.
|
||||
wd_start(rtcb->waitdog, ticks, (wdentry_t)mq_sndtimeout, 1, getpid());
|
||||
|
||||
/* And wait for the message queue to be non-empty */
|
||||
|
||||
ret = mq_waitsend(mqdes);
|
||||
|
||||
/* This may return with an error and errno set to either EINTR
|
||||
* or ETIMEOUT. Cancel the watchdog timer in any event.
|
||||
*/
|
||||
|
||||
int result = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks);
|
||||
|
||||
/* If the time has already expired and the message queue is empty,
|
||||
* return immediately.
|
||||
*/
|
||||
|
||||
if (result == OK && ticks <= 0)
|
||||
{
|
||||
result = ETIMEDOUT;
|
||||
}
|
||||
|
||||
/* Handle any time-related errors */
|
||||
|
||||
if (result != OK)
|
||||
{
|
||||
set_errno(result);
|
||||
ret = ERROR;
|
||||
}
|
||||
|
||||
/* Start the watchdog and begin the wait for MQ not full */
|
||||
|
||||
if (result == OK)
|
||||
{
|
||||
/* Start the watchdog */
|
||||
|
||||
wd_start(rtcb->waitdog, ticks, (wdentry_t)mq_sndtimeout, 1, getpid());
|
||||
|
||||
/* And wait for the message queue to be non-empty */
|
||||
|
||||
ret = mq_waitsend(mqdes);
|
||||
|
||||
/* This may return with an error and errno set to either EINTR
|
||||
* or ETIMEOUT. Cancel the watchdog timer in any event.
|
||||
*/
|
||||
|
||||
wd_cancel(rtcb->waitdog);
|
||||
}
|
||||
|
||||
/* That is the end of the atomic operations */
|
||||
|
||||
irqrestore(saved_state);
|
||||
|
||||
/* If any of the above failed, set the errno. Otherwise, there should
|
||||
* be space for another message in the message queue. NOW we can allocate
|
||||
* the message structure.
|
||||
*/
|
||||
|
||||
if (ret == OK)
|
||||
{
|
||||
mqmsg = mq_msgalloc();
|
||||
}
|
||||
wd_cancel(rtcb->waitdog);
|
||||
}
|
||||
|
||||
/* Check if we were able to get a message structure -- this can fail
|
||||
* either because we cannot send the message (and didn't bother trying
|
||||
* to allocate it) or because the allocation failed.
|
||||
/* That is the end of the atomic operations */
|
||||
|
||||
irqrestore(saved_state);
|
||||
|
||||
/* If any of the above failed, set the errno. Otherwise, there should
|
||||
* be space for another message in the message queue. NOW we can allocate
|
||||
* the message structure.
|
||||
*/
|
||||
|
||||
if (mqmsg)
|
||||
if (ret == OK)
|
||||
{
|
||||
/* Yes, peform the message send. */
|
||||
mqmsg = mq_msgalloc();
|
||||
if (!mqmsg)
|
||||
{
|
||||
/* Failed to allocate the message */
|
||||
|
||||
ret = mq_dosend(mqdes, mqmsg, msg, msglen, prio);
|
||||
set_errno(ENOMEM);
|
||||
ret = ERROR;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Perform the message send. */
|
||||
|
||||
ret = mq_dosend(mqdes, mqmsg, msg, msglen, prio);
|
||||
}
|
||||
}
|
||||
|
||||
sched_unlock();
|
||||
|
Loading…
Reference in New Issue
Block a user