mq_timedsend(): Do check for time errors if the message queue is not full. Noted by Freddie Chopin

This commit is contained in:
Gregory Nutt 2015-03-10 09:42:35 -06:00
parent 36e88e504a
commit 4c6057eca1
6 changed files with 124 additions and 117 deletions

22
TODO
View File

@ -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
^^^^^^^^^^^^^^^^^^^^^^

View File

@ -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

View File

@ -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

View File

@ -180,4 +180,3 @@ int mq_send(mqd_t mqdes, FAR const char *msg, size_t msglen, int prio)
sched_unlock();
return ret;
}

View File

@ -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.

View File

@ -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();