mirror of
https://github.com/apache/nuttx.git
synced 2026-06-07 17:33:08 +08:00
mq_timedsend(): Do check for time errors if the message queue is not full. Noted by Freddie Chopin
This commit is contained in:
@@ -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.
|
||||
|
||||
+91
-80
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user