From 605bd84ced2cf37e267eccf604bdff1a985a65d8 Mon Sep 17 00:00:00 2001 From: patacongo Date: Thu, 29 Mar 2007 13:25:18 +0000 Subject: Added mq_timedsend() and mq_timedreceive() git-svn-id: svn://svn.code.sf.net/p/nuttx/code/trunk@166 42af7a65-404d-4744-a932-0658087f49c3 --- nuttx/sched/mq_timedreceive.c | 308 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 308 insertions(+) create mode 100644 nuttx/sched/mq_timedreceive.c (limited to 'nuttx/sched/mq_timedreceive.c') diff --git a/nuttx/sched/mq_timedreceive.c b/nuttx/sched/mq_timedreceive.c new file mode 100644 index 000000000..39fafdc7d --- /dev/null +++ b/nuttx/sched/mq_timedreceive.c @@ -0,0 +1,308 @@ +/**************************************************************************** + * mq_timedreceive.c + * + * Copyright (C) 2007 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt + * + * 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 Gregory Nutt 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 +#include +#include +#include +#include +#include +#include +#include "os_internal.h" +#include "clock_internal.h" +#include "mq_internal.h" + +/**************************************************************************** + * Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Type Declarations + ****************************************************************************/ + +/**************************************************************************** + * Global Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Variables + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: mq_rcvtimeout + * + * Description: + * This function is called if the timeout elapses before the message queue + * becomes non-empty. + * + * Parameters: + * argc - the number of arguments (should be 1) + * pid - the task ID of the task to wakeup + * + * Return Value: + * None + * + * Assumptions: + * + ****************************************************************************/ + +static void mq_rcvtimeout(int argc, uint32 pid, ...) +{ + FAR _TCB *wtcb; + irqstate_t saved_state; + + /* Disable interrupts. This is necessary because an + * interrupt handler may attempt to send a message while we are + * doing this. + */ + + saved_state = irqsave(); + + /* Get the TCB associated with this pid. It is possible that + * task may no longer be active when this watchdog goes off. + */ + + wtcb = sched_gettcb((pid_t)pid); + + /* It is also possible that an interrupt/context switch beat us to the + * punch and already changed the task's state. + */ + + if (wtcb && wtcb->task_state == TSTATE_WAIT_MQNOTEMPTY) + { + /* Mark the errno value for the thread. */ + + wtcb->errno = ETIMEDOUT; + + /* Restart the the task. */ + + up_unblock_task(wtcb); + } + + /* Interrupts may now be enabled. */ + + irqrestore(saved_state); +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Function: mq_timedreceive + * + * Description: + * This function receives the oldest of the highest + * priority messages from the message queue specified by + * "mqdes." If the size of the buffer in bytes (msglen) is + * less than the "mq_msgsize" attribute of the message + * queue, mq_timedreceive will return an error. Otherwise, the + * selected message is removed from the queue and copied to + * "msg." + * + * If the message queue is empty and O_NONBLOCK was not + * set, mq_timedreceive() will block until a message is added + * to the message queue (or until a timeout occurs). If more + * than one task is waiting to receive a message, only the + * task with the highest priority that has waited the longest + * will be unblocked. + * + * mq_timedreceive() behaves just like mq_receive(), except + * that if the queue is empty and the O_NONBLOCK flag is not + * enabled for the message queue description, then abstime + * points to a structure which specifies a ceiling on the time + * for which the call will block. This ceiling is an absolute + * timeout in seconds and nanoseconds since the Epoch (midnight + * on the morning of 1 January 1970). + * + * If no message is available, and the timeout has already + * expired by the time of the call, mq_timedreceive() returns + * immediately. + * + * Parameters: + * mqdes - Message Queue Descriptor + * msg - Buffer to receive the message + * msglen - Size of the buffer in bytes + * prio - If not NULL, the location to store message priority. + * abstime - the absolute time to wait until a timeout is declared. + * + * Return Value: + * One success, the length of the selected message in bytes.is + * returned. On failure, -1 (ERROR) is returned and the errno + * is set appropriately: + * + * EAGAIN The queue was empty, and the O_NONBLOCK flag was set + * for the message queue description referred to by 'mqdes'. + * EPERM Message queue opened not opened for reading. + * EMSGSIZE 'msglen' was less than the maxmsgsize attribute of the + * message queue. + * EINTR The call was interrupted by a signal handler. + * EINVAL Invalid 'msg' or 'mqdes' or 'abstime' + * ETIMEDOUT The call timed out before a message could be transferred. + * + * Assumptions: + * + ****************************************************************************/ + +ssize_t mq_timedreceive(mqd_t mqdes, void *msg, size_t msglen, + int *prio, const struct timespec *abstime) +{ + WDOG_ID wdog; + FAR mqmsg_t *mqmsg; + irqstate_t saved_state; + int ret = ERROR; + + DEBUGASSERT(!up_interrupt_context()); + + /* Verify the input parameters and, in case of an error, set + * errno appropriately. + */ + + if (mq_verifyreceive(mqdes, msg, msglen) != OK) + { + return ERROR; + } + + if (!abstime || abstime->tv_sec < 0 || abstime->tv_nsec > 1000000000) + { + *get_errno_ptr() = EINVAL; + return ERROR; + } + + /* Create a watchdog. We will not actually need this watchdog + * unless the queue is not empty, but we will reserve it up front + * before we enter the following critical section. + */ + + wdog = wd_create(); + if (!wdog) + { + *get_errno_ptr() = EINVAL; + return ERROR; + } + + /* Get the next mesage from the message queue. We will disable + * pre-emption until we have completed the message received. This + * is not too bad because if the receipt takes a long time, it will + * be because we are blocked waiting for a message and pre-emption + * will be re-enabled while we are blocked + */ + + sched_lock(); + + /* Furthermore, mq_waitreceive() expects to have interrupts disabled + * because messages can be sent from interrupt level. + */ + + saved_state = irqsave(); + + /* Check if the message queue is empty. If it is NOT empty, then we + * will not need to start timer. + */ + + if (mqdes->msgq->msglist.head == NULL) + { + sint32 ticks; + + /* Convert the timespec to clock ticks. We must have interrupts + * disabled here so that this time stays valid until the wait begins. + */ + + ret = clock_abstime2ticks(CLOCK_REALTIME, abstime, &ticks); + + /* If the time has already expired and the message queue is empty, + * return immediately. + */ + + if (ret == OK && ticks <= 0) + { + ret = ETIMEDOUT; + } + + /* Handle any time-related errors */ + + if (ret != OK) + { + *get_errno_ptr() = ret; + irqrestore(saved_state); + sched_unlock(); + wd_delete(wdog); + return ERROR; + } + + /* Start the watchdog */ + + wd_start(wdog, ticks, (wdentry_t)mq_rcvtimeout, 1, getpid()); + } + + /* Get the message from the message queue */ + + mqmsg = mq_waitreceive(mqdes); + + /* Stop the watchdog timer (this is not harmful in the case where + * it was never started) + */ + + wd_cancel(wdog); + + /* We can now restore interrupts */ + + irqrestore(saved_state); + + /* Check if we got a message from the message queue. We might + * not have a message if: + * + * - The message queue is empty and O_NONBLOCK is set in the mqdes + * - The wait was interrupted by a signal + * - The watchdog timeout expired + */ + + if (mqmsg) + { + ret = mq_doreceive(mqdes, mqmsg, msg, prio); + } + + sched_unlock(); + wd_delete(wdog); + return ret; +} -- cgit v1.2.3