/* * Copyright (C) 2005 Philippe Gerum . * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include #include #include #include #include #include #include #include "internal.h" /** * @ingroup cobalt_api * @defgroup cobalt_api_mq Message queues * * Cobalt/POSIX message queue services * * A message queue allow exchanging data between real-time * threads. For a POSIX message queue, maximum message length and * maximum number of messages are fixed when it is created with * mq_open(). * *@{ */ /** * @brief Open a message queue * * This service opens the message queue named @a name. * * One of the following values should be set in @a oflags: * - O_RDONLY, meaning that the returned queue descriptor may only be used for * receiving messages; * - O_WRONLY, meaning that the returned queue descriptor may only be used for * sending messages; * - O_RDWR, meaning that the returned queue descriptor may be used for both * sending and receiving messages. * * If no message queue named @a name exists, and @a oflags has the @a O_CREAT * bit set, the message queue is created by this function, taking two more * arguments: * - a @a mode argument, of type @b mode_t, currently ignored; * - an @a attr argument, pointer to an @b mq_attr structure, specifying the * attributes of the new message queue. * * If @a oflags has the two bits @a O_CREAT and @a O_EXCL set and the message * queue alread exists, this service fails. * * If the O_NONBLOCK bit is set in @a oflags, the mq_send(), mq_receive(), * mq_timedsend() and mq_timedreceive() services return @a -1 with @a errno set * to EAGAIN instead of blocking their caller. * * The following arguments of the @b mq_attr structure at the address @a attr * are used when creating a message queue: * - @a mq_maxmsg is the maximum number of messages in the queue (128 by * default); * - @a mq_msgsize is the maximum size of each message (128 by default). * * @a name may be any arbitrary string, in which slashes have no particular * meaning. However, for portability, using a name which starts with a slash and * contains no other slash is recommended. * * @param name name of the message queue to open; * * @param oflags flags. * * @return a message queue descriptor on success; * @return -1 with @a errno set if: * - ENAMETOOLONG, the length of the @a name argument exceeds 64 characters; * - EEXIST, the bits @a O_CREAT and @a O_EXCL were set in @a oflags and the * message queue already exists; * - ENOENT, the bit @a O_CREAT is not set in @a oflags and the message queue * does not exist; * - ENOSPC, allocation of system memory failed, or insufficient memory available * from the system heap to create the queue, try increasing * CONFIG_XENO_OPT_SYS_HEAPSZ; * - EPERM, attempting to create a message queue from an invalid context; * - EINVAL, the @a attr argument is invalid; * - EMFILE, too many descriptors are currently open. * - EAGAIN, no registry slot available, check/raise CONFIG_XENO_OPT_REGISTRY_NRSLOTS. * * @see * * Specification. * * @apitags{thread-unrestricted, switch-secondary} */ COBALT_IMPL(mqd_t, mq_open, (const char *name, int oflags, ...)) { struct mq_attr *attr = NULL; mode_t mode = 0; va_list ap; int fd; if ((oflags & O_CREAT) != 0) { va_start(ap, oflags); mode = va_arg(ap, int); /* unused */ attr = va_arg(ap, struct mq_attr *); va_end(ap); } fd = XENOMAI_SYSCALL4(sc_cobalt_mq_open, name, oflags, mode, attr); if (fd < 0) { errno = -fd; return (mqd_t)-1; } return (mqd_t)fd; } /** * @brief Close a message queue * * This service closes the message queue descriptor @a mqd. The * message queue is destroyed only when all open descriptors are * closed, and when unlinked with a call to the mq_unlink() service. * * @param mqd message queue descriptor. * * @retval 0 on success; * @retval -1 with @a errno set if: * - EBADF, @a mqd is an invalid message queue descriptor; * - EPERM, the caller context is invalid. * * @see * * Specification. * * @apitags{thread-unrestricted, switch-secondary} */ COBALT_IMPL(int, mq_close, (mqd_t mqd)) { int err; err = XENOMAI_SYSCALL1(sc_cobalt_mq_close, mqd); if (err) { errno = -err; return -1; } return 0; } /** * @brief Unlink a message queue * * This service unlinks the message queue named @a name. The message queue is * not destroyed until all queue descriptors obtained with the mq_open() service * are closed with the mq_close() service. However, after a call to this * service, the unlinked queue may no longer be reached with the mq_open() * service. * * @param name name of the message queue to be unlinked. * * @retval 0 on success; * @retval -1 with @a errno set if: * - EPERM, the caller context is invalid; * - ENAMETOOLONG, the length of the @a name argument exceeds 64 characters; * - ENOENT, the message queue does not exist. * * @see * * Specification. * * @apitags{thread-unrestricted, switch-secondary} */ COBALT_IMPL(int, mq_unlink, (const char *name)) { int err; err = XENOMAI_SYSCALL1(sc_cobalt_mq_unlink, name); if (!err) return 0; errno = -err; return -1; } /** * @brief Get message queue attributes * * This service stores, at the address @a attr, the attributes of the messages * queue descriptor @a mqd. * * The following attributes are set: * - @a mq_flags, flags of the message queue descriptor @a mqd; * - @a mq_maxmsg, maximum number of messages in the message queue; * - @a mq_msgsize, maximum message size; * - @a mq_curmsgs, number of messages currently in the queue. * * @param mqd message queue descriptor; * * @param attr address where the message queue attributes will be stored on * success. * * @retval 0 on success; * @retval -1 with @a errno set if: * - EBADF, @a mqd is not a valid descriptor. * * @see * * Specification. * * @apitags{thread-unrestricted} */ COBALT_IMPL(int, mq_getattr, (mqd_t mqd, struct mq_attr *attr)) { int err; err = XENOMAI_SYSCALL2(sc_cobalt_mq_getattr, mqd, attr); if (!err) return 0; errno = -err; return -1; } /** * @brief Set message queue attributes * * This service sets the flags of the @a mqd descriptor to the value * of the member @a mq_flags of the @b mq_attr structure pointed to by * @a attr. * * The previous value of the message queue attributes are stored at the address * @a oattr if it is not @a NULL. * * Only setting or clearing the O_NONBLOCK flag has an effect. * * @param mqd message queue descriptor; * * @param attr pointer to new attributes (only @a mq_flags is used); * * @param oattr if not @a NULL, address where previous message queue attributes * will be stored on success. * * @retval 0 on success; * @retval -1 with @a errno set if: * - EBADF, @a mqd is not a valid message queue descriptor. * * @see * * Specification. * * @apitags{thread-unrestricted} */ COBALT_IMPL(int, mq_setattr, (mqd_t mqd, const struct mq_attr *__restrict__ attr, struct mq_attr *__restrict__ oattr)) { int err = 0, flags; if (oattr) { err = XENOMAI_SYSCALL2(sc_cobalt_mq_getattr, mqd, oattr); if (err < 0) goto out_err; flags = oattr->mq_flags; } else { err = __WRAP(fcntl(mqd, F_GETFL)); if (err < 0) goto out_err; flags = err; } flags = (flags & ~O_NONBLOCK) | (attr->mq_flags & O_NONBLOCK); err = __WRAP(fcntl(mqd, F_SETFL, flags)); if (!err) return 0; out_err: errno = -err; return -1; } /** * Send a message to a message queue. * * If the message queue @a fd is not full, this service sends the message of * length @a len pointed to by the argument @a buffer, with priority @a prio. A * message with greater priority is inserted in the queue before a message with * lower priority. * * If the message queue is full and the flag @a O_NONBLOCK is not set, the * calling thread is suspended until the queue is not full. If the message queue * is full and the flag @a O_NONBLOCK is set, the message is not sent and the * service returns immediately a value of -1 with @a errno set to EAGAIN. * * @param q message queue descriptor; * * @param buffer pointer to the message to be sent; * * @param len length of the message; * * @param prio priority of the message. * * @return 0 and send a message on success; * @return -1 with no message sent and @a errno set if: * - EBADF, @a fd is not a valid message queue descriptor open for writing; * - EMSGSIZE, the message length @a len exceeds the @a mq_msgsize attribute of * the message queue; * - EAGAIN, the flag O_NONBLOCK is set for the descriptor @a fd and the message * queue is full; * - EPERM, the caller context is invalid; * - EINTR, the service was interrupted by a signal. * * @see * * Specification. * * @apitags{xthread-only, switch-primary} */ COBALT_IMPL(int, mq_send, (mqd_t q, const char *buffer, size_t len, unsigned prio)) { int err, oldtype; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); #ifdef __USE_TIME_BITS64 err = XENOMAI_SYSCALL5(sc_cobalt_mq_timedsend64, q, buffer, len, prio, NULL); #else err = XENOMAI_SYSCALL5(sc_cobalt_mq_timedsend, q, buffer, len, prio, NULL); #endif pthread_setcanceltype(oldtype, NULL); if (!err) return 0; errno = -err; return -1; } /** * Attempt, during a bounded time, to send a message to a message queue. * * This service is equivalent to mq_send(), except that if the message queue is * full and the flag @a O_NONBLOCK is not set for the descriptor @a fd, the * calling thread is only suspended until the timeout specified by @a * abs_timeout expires. * * @param q message queue descriptor; * * @param buffer pointer to the message to be sent; * * @param len length of the message; * * @param prio priority of the message; * * @param timeout the timeout, expressed as an absolute value of the * CLOCK_REALTIME clock. * * @return 0 and send a message on success; * @return -1 with no message sent and @a errno set if: * - EBADF, @a fd is not a valid message queue descriptor open for writing; * - EMSGSIZE, the message length exceeds the @a mq_msgsize attribute of the * message queue; * - EAGAIN, the flag O_NONBLOCK is set for the descriptor @a fd and the message * queue is full; * - EPERM, the caller context is invalid; * - ETIMEDOUT, the specified timeout expired; * - EINTR, the service was interrupted by a signal. * * @see * * Specification. * * @apitags{xthread-only, switch-primary} */ COBALT_IMPL(int, mq_timedsend, (mqd_t q, const char *buffer, size_t len, unsigned prio, const struct timespec *timeout)) { int err, oldtype; if (timeout == NULL) return -EFAULT; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); err = XENOMAI_SYSCALL5(sc_cobalt_mq_timedsend, q, buffer, len, prio, timeout); pthread_setcanceltype(oldtype, NULL); if (!err) return 0; errno = -err; return -1; } /** * Receive a message from a message queue. * * If the message queue @a fd is not empty and if @a len is greater than the @a * mq_msgsize of the message queue, this service copies, at the address * @a buffer, the queued message with the highest priority. * * If the queue is empty and the flag @a O_NONBLOCK is not set for the * descriptor @a fd, the calling thread is suspended until some message is sent * to the queue. If the queue is empty and the flag @a O_NONBLOCK is set for the * descriptor @a fd, this service returns immediately a value of -1 with @a * errno set to EAGAIN. * * @param q the queue descriptor; * * @param buffer the address where the received message will be stored on * success; * * @param len @a buffer length; * * @param prio address where the priority of the received message will be * stored on success. * * @return the message length, and copy a message at the address @a buffer on * success; * @return -1 with no message unqueued and @a errno set if: * - EBADF, @a fd is not a valid descriptor open for reading; * - EMSGSIZE, the length @a len is lesser than the message queue @a mq_msgsize * attribute; * - EAGAIN, the queue is empty, and the flag @a O_NONBLOCK is set for the * descriptor @a fd; * - EPERM, the caller context is invalid; * - EINTR, the service was interrupted by a signal. * * @see * * Specification. * * @apitags{xthread-only, switch-primary} */ COBALT_IMPL(ssize_t, mq_receive, (mqd_t q, char *buffer, size_t len, unsigned *prio)) { ssize_t rlen = (ssize_t) len; int err, oldtype; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); err = XENOMAI_SYSCALL5(sc_cobalt_mq_timedreceive, q, buffer, &rlen, prio, NULL); pthread_setcanceltype(oldtype, NULL); if (!err) return rlen; errno = -err; return -1; } /** * Attempt, during a bounded time, to receive a message from a message queue. * * This service is equivalent to mq_receive(), except that if the flag @a * O_NONBLOCK is not set for the descriptor @a fd and the message queue is * empty, the calling thread is only suspended until the timeout @a abs_timeout * expires. * * @param q the queue descriptor; * * @param buffer the address where the received message will be stored on * success; * * @param len @a buffer length; * * @param prio address where the priority of the received message will be * stored on success. * * @param timeout the timeout, expressed as an absolute value of the * CLOCK_REALTIME clock. * * @return the message length, and copy a message at the address @a buffer on * success; * @return -1 with no message unqueued and @a errno set if: * - EBADF, @a fd is not a valid descriptor open for reading; * - EMSGSIZE, the length @a len is lesser than the message queue @a mq_msgsize * attribute; * - EAGAIN, the queue is empty, and the flag @a O_NONBLOCK is set for the * descriptor @a fd; * - EPERM, the caller context is invalid; * - EINTR, the service was interrupted by a signal; * - ETIMEDOUT, the specified timeout expired. * * @see * * Specification. * * @apitags{xthread-only, switch-primary} */ COBALT_IMPL(ssize_t, mq_timedreceive, (mqd_t q, char *__restrict__ buffer, size_t len, unsigned *__restrict__ prio, const struct timespec * __restrict__ timeout)) { ssize_t rlen = (ssize_t) len; int err, oldtype; if (timeout == NULL) return -EFAULT; pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype); #ifdef __USE_TIME_BITS64 err = XENOMAI_SYSCALL5(sc_cobalt_mq_timedreceive64, q, buffer, &rlen, prio, timeout); #else err = XENOMAI_SYSCALL5(sc_cobalt_mq_timedreceive, q, buffer, &rlen, prio, timeout); #endif pthread_setcanceltype(oldtype, NULL); if (!err) return rlen; errno = -err; return -1; } /** * @brief Enable notification on message arrival * * If @a evp is not @a NULL and is the address of a @b sigevent * structure with the @a sigev_notify member set to SIGEV_SIGNAL, the * current thread will be notified by a signal when a message is sent * to the message queue @a mqd, the queue is empty, and no thread is * blocked in call to mq_receive() or mq_timedreceive(). After the * notification, the thread is unregistered. * * If @a evp is @a NULL or the @a sigev_notify member is SIGEV_NONE, * the current thread is unregistered. * * Only one thread may be registered at a time. * * If the current thread is not a Cobalt thread (created with * pthread_create()), this service fails. * * @param mqd message queue descriptor; * * @param evp pointer to an event notification structure. * * @retval 0 on success; * @retval -1 with @a errno set if: * - EINVAL, @a evp is invalid; * - EPERM, the caller context is invalid; * - EBADF, @a mqd is not a valid message queue descriptor; * - EBUSY, another thread is already registered. * * @see * * Specification. * * @apitags{xthread-only, switch-primary} */ COBALT_IMPL(int, mq_notify, (mqd_t mqd, const struct sigevent *evp)) { int err; err = XENOMAI_SYSCALL2(sc_cobalt_mq_notify, mqd, evp); if (err) { errno = -err; return -1; } return 0; } /** @}*/