/**************************************************************************** * sched/mqueue/msgsnd.c * * SPDX-License-Identifier: Apache-2.0 * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include "sched/sched.h" #include "mqueue/msg.h" /**************************************************************************** * Private Functions ****************************************************************************/ /**************************************************************************** * Name: msgsnd_wait ****************************************************************************/ static int msgsnd_wait(FAR struct msgq_s *msgq, int msgflg) { FAR struct tcb_s *rtcb; bool switch_needed; #ifdef CONFIG_CANCELLATION_POINTS /* msgsnd_wait() is not a cancellation point, but may be called via * msgsnd() which are cancellation points. */ if (check_cancellation_point()) { /* If there is a pending cancellation, then do not perform * the wait. Exit now with ECANCELED. */ return -ECANCELED; } #endif /* Verify that the queue is indeed full as the caller thinks * Loop until there are fewer than max allowable messages in the * receiving message queue */ while (msgq->nmsgs >= msgq->maxmsgs) { /* Should we block until there is sufficient space in the * message queue? */ if ((msgflg & IPC_NOWAIT) != 0) { return -EAGAIN; } /* Block until the message queue is no longer full. * When we are unblocked, we will try again */ rtcb = this_task(); rtcb->waitobj = msgq; msgq->cmn.nwaitnotfull++; /* Initialize the errcode used to communication wake-up error * conditions. */ rtcb->errcode = OK; /* Make sure this is not the idle task, descheduling that * isn't going to end well. */ DEBUGASSERT(NULL != rtcb->flink); /* Remove the tcb task from the ready-to-run list. */ switch_needed = nxsched_remove_readytorun(rtcb, true); /* Add the task to the specified blocked task list */ rtcb->task_state = TSTATE_WAIT_MQNOTFULL; nxsched_add_prioritized(rtcb, MQ_WNFLIST(msgq->cmn)); /* Now, perform the context switch if one is needed */ if (switch_needed) { up_switch_context(this_task(), rtcb); } /* When we resume at this point, either (1) the message queue * is no longer empty, or (2) the wait has been interrupted by * a signal. We can detect the latter case be examining the * per-task errno value (should be EINTR or ETIMEOUT). */ if (rtcb->errcode != OK) { return -rtcb->errcode; } } return OK; } /**************************************************************************** * Public Functions ****************************************************************************/ /**************************************************************************** * Name: msgsnd * * Description: * The msgsnd() function is used to send a message to the queue * associated with the message queue identifier specified by msqid. * The msgp argument points to a user-defined buffer that must contain * first a field of type long int that will specify the type of the * message, and then a data portion that will hold the data bytes of * the message. * * Input Parameters: * msqid - Message queue identifier * msgp - Pointer to a buffer with the message to be sent * msgsz - Length of the data part of the message to be sent * msgflg - Operations flags * * Returned Value: * On success, mq_send() returns 0 (OK); on error, -1 (ERROR) * is returned, with errno set to indicate the error: * * EAGAIN The queue was full and the O_NONBLOCK flag was set for the * message queue description referred to by mqdes. * EINVAL Either msg or mqdes is NULL or the value of prio is invalid. * EPERM Message queue opened not opened for writing. * EMSGSIZE 'msglen' was greater than the maxmsgsize attribute of the * message queue. * EINTR The call was interrupted by a signal handler. * ****************************************************************************/ int msgsnd(int msqid, FAR const void *msgp, size_t msgsz, int msgflg) { FAR const struct mymsg *buf = msgp; FAR struct msgbuf_s *msg; FAR struct msgq_s *msgq; FAR struct tcb_s *btcb; irqstate_t flags; int ret = OK; if (msgp == NULL) { ret = -EFAULT; goto errout; } flags = enter_critical_section(); msgq = nxmsg_lookup(msqid); if (msgq == NULL) { ret = -EINVAL; goto errout_with_critical; } if (msgsz > msgq->maxmsgsize) { ret = -EMSGSIZE; goto errout_with_critical; } /* Is the message queue FULL? */ if (msgq->nmsgs >= msgq->maxmsgs) { if (!up_interrupt_context()) /* In an interrupt handler? */ { /* Yes.. the message queue is full. Wait for space to become * available in the message queue. */ ret = msgsnd_wait(msgq, msgflg); } else if ((msgflg & IPC_NOWAIT) != 0) { ret = -EAGAIN; } } if (ret == OK) { /* Now allocate the message. */ msg = (FAR struct msgbuf_s *)list_remove_head(&g_msgfreelist); if (msg == NULL) { ret = -ENOMEM; goto errout_with_critical; } /* Check if the message was successfully allocated */ msg->msize = msgsz; msg->mtype = buf->mtype; memcpy(msg->mtext, buf->mtext, msgsz); /* Insert the new message in the message queue */ list_add_tail(&msgq->msglist, &msg->node); msgq->nmsgs++; if (msgq->cmn.nwaitnotempty > 0) { FAR struct tcb_s *rtcb = this_task(); /* Find the highest priority task that is waiting for * this queue to be non-empty in g_waitingformqnotempty * list. enter_critical_section() should give us sufficient * protection since interrupts should never cause a change * in this list */ btcb = (FAR struct tcb_s *)dq_remfirst(MQ_WNELIST(msgq->cmn)); /* If one was found, unblock it */ DEBUGASSERT(btcb); if (WDOG_ISACTIVE(&btcb->waitdog)) { wd_cancel(&btcb->waitdog); } msgq->cmn.nwaitnotempty--; /* Indicate that the wait is over. */ btcb->waitobj = NULL; /* Add the task to ready-to-run task list and * perform the context switch if one is needed */ if (nxsched_add_readytorun(btcb)) { up_switch_context(btcb, rtcb); } } } errout_with_critical: leave_critical_section(flags); errout: if (ret < 0) { set_errno(-ret); return ERROR; } return OK; }