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
-1
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;
}
+1 -1
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.
+91 -80
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();