Why Timed Operations? timed Operations: mq_timedsend() & mq_timedreceive()

 

POSIX Message Queues
Part 3 – Timed Operations: mq_timedsend() & mq_timedreceive()
Topic
Timed MQ I/O
Level
Intermediate
Chapter
52 – TLPI

Why Timed Operations?

Plain mq_send() and mq_receive() give you two extremes: block forever or return immediately (with O_NONBLOCK). Neither is ideal for real-world embedded or system programs where you want to wait for a reasonable amount of time and then give up gracefully.

mq_timedsend() and mq_timedreceive() solve this. They behave exactly like their non-timed counterparts but accept an absolute timeout (as a struct timespec). If the operation cannot complete before the timeout expires, the call returns with ETIMEDOUT.

Think of it like ordering food at a counter. You wait up to 10 minutes; if the food is not ready by then, you walk away — you do not wait forever, and you do not leave immediately either.

Function Signatures
#define _XOPEN_SOURCE 600
#include <mqueue.h>
#include <time.h>

int mq_timedsend(
    mqd_t               mqdes,
    const char         *msg_ptr,
    size_t              msg_len,
    unsigned int        msg_prio,
    const struct timespec *abs_timeout   /* absolute deadline */
);
/* Returns: 0 on success, -1 on error (errno=ETIMEDOUT if timed out) */

ssize_t mq_timedreceive(
    mqd_t               mqdes,
    char               *msg_ptr,
    size_t              msg_len,
    unsigned int       *msg_prio,
    const struct timespec *abs_timeout   /* absolute deadline */
);
/* Returns: bytes received on success, -1 on error (errno=ETIMEDOUT) */
ⓘ Feature Macro Required
Define _XOPEN_SOURCE 600 (or _POSIX_C_SOURCE 200112L) before your includes. Without it, the compiler will not see these function declarations on some Linux systems.

Absolute Timeout — What It Means

The abs_timeout is an absolute point in time (seconds + nanoseconds since the Unix Epoch, Jan 1 1970), not a relative duration like “wait 5 seconds.”

To get a relative timeout (the common case), read the current time with clock_gettime(CLOCK_REALTIME, ...) and add your desired wait:

clock_gettime()
current time (T_now)
+
desired wait
e.g. 5 seconds
=
abs_timeout
T_now + 5s
pass to
mq_timedsend()
/ mq_timedreceive()
/* How to compute an absolute timeout 5 seconds from now */
struct timespec deadline;

/* Step 1: Get current real-world clock time */
clock_gettime(CLOCK_REALTIME, &deadline);

/* Step 2: Add 5 seconds */
deadline.tv_sec += 5;

/* Now pass &deadline to mq_timedsend() or mq_timedreceive() */

struct timespec — Quick Reference
struct timespec {
    time_t  tv_sec;   /* seconds since Epoch (Jan 1 1970 00:00:00 UTC) */
    long    tv_nsec;  /* additional nanoseconds (0 .. 999,999,999)      */
};

/* Example: represent time "3.5 seconds from now" */
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec  += 3;
ts.tv_nsec += 500000000L;  /* 500 ms = 500,000,000 ns */

/* Handle nanosecond overflow (when tv_nsec >= 1,000,000,000) */
if (ts.tv_nsec >= 1000000000L) {
    ts.tv_sec++;
    ts.tv_nsec -= 1000000000L;
}

⚠ Linux-Specific: NULL Timeout

On Linux, passing abs_timeout = NULL to the timed functions means an infinite timeout (equivalent to the non-timed blocking call). However, this behavior is not defined by POSIX (SUSv3). Portable code must always provide a valid timespec.

What Happens on Timeout
mq_timedreceive() / mq_timedsend()
Can operation complete immediately?
YES → Do it
Return success
NO → Block & wait
Condition met before timeout
→ Return success
Deadline expires
→ Return -1
errno = ETIMEDOUT

Example 1 – mq_timedreceive() with 5-Second Timeout

Wait up to 5 seconds for a message. If none arrives, print a timeout message and exit cleanly.

/* timed_receive.c
 * Compile: gcc timed_receive.c -o timed_receive -lrt
 * Run:     ./timed_receive /myqueue
 */
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <mqueue.h>
#include <fcntl.h>

int main(int argc, char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s /queue-name\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    /* Open queue read-only (blocking mode — timed functions handle waiting) */
    mqd_t mqd = mq_open(argv[1], O_RDONLY);
    if (mqd == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }

    struct mq_attr attr;
    mq_getattr(mqd, &attr);

    char *buf = malloc(attr.mq_msgsize);
    if (!buf) { perror("malloc"); exit(EXIT_FAILURE); }

    /* Build absolute timeout: now + 5 seconds */
    struct timespec deadline;
    clock_gettime(CLOCK_REALTIME, &deadline);
    deadline.tv_sec += 5;

    unsigned int prio;
    ssize_t n = mq_timedreceive(mqd, buf, attr.mq_msgsize,
                                 &prio, &deadline);
    if (n == -1) {
        if (errno == ETIMEDOUT) {
            printf("No message arrived within 5 seconds. Giving up.\n");
        } else {
            perror("mq_timedreceive");
        }
    } else {
        printf("Received in time! priority=%u bytes=%zd msg=%.*s\n",
               prio, n, (int)n, buf);
    }

    free(buf);
    mq_close(mqd);
    return 0;
}

Example 2 – mq_timedsend() with 3-Second Timeout

Try to send a message. If the queue is full, wait up to 3 seconds for space to free up.

/* timed_send.c
 * Compile: gcc timed_send.c -o timed_send -lrt
 */
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>

int main(void)
{
    const char *qname = "/full_queue_test";

    /* Create a tiny queue — only 2 message slots */
    struct mq_attr attr = { 0, 2, 64, 0 };
    mqd_t mqd = mq_open(qname, O_CREAT | O_WRONLY, 0644, &attr);
    if (mqd == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }

    const char *msg = "hello";

    /* Fill the queue (2 slots) */
    mq_send(mqd, msg, strlen(msg)+1, 1);
    mq_send(mqd, msg, strlen(msg)+1, 1);
    printf("Queue is now full (2/2 messages).\n");

    /* Try to send a third — queue is full, wait up to 3 seconds */
    struct timespec deadline;
    clock_gettime(CLOCK_REALTIME, &deadline);
    deadline.tv_sec += 3;

    printf("Attempting timed send (will timeout in 3s)...\n");
    int ret = mq_timedsend(mqd, msg, strlen(msg)+1, 1, &deadline);

    if (ret == -1) {
        if (errno == ETIMEDOUT) {
            printf("Queue still full after 3 seconds. Send aborted.\n");
        } else {
            perror("mq_timedsend");
        }
    } else {
        printf("Send succeeded unexpectedly.\n");
    }

    mq_close(mqd);
    mq_unlink(qname);
    return 0;
}

Example 3 – Reusable Helper: Relative Timeout Receive

In practice, you always want a relative timeout (e.g., “wait 2 seconds from now”). Wrap the absolute-time conversion in a helper function.

/* mq_helper.c — Reusable helper for relative timeout receive.
 * Compile: gcc mq_helper.c -o mq_helper -lrt
 */
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <mqueue.h>
#include <fcntl.h>
#include <sys/stat.h>

/**
 * mq_receive_timeout - receive with a relative timeout.
 * @mqd:      open queue descriptor
 * @buf:      message buffer
 * @buf_size: buffer size (must be >= mq_msgsize)
 * @prio:     output: priority of received message (or NULL)
 * @wait_sec: how many seconds to wait
 *
 * Returns: bytes received, or -1 on error/timeout.
 * Check errno == ETIMEDOUT to distinguish timeout from error.
 */
ssize_t mq_receive_timeout(mqd_t mqd, char *buf, size_t buf_size,
                            unsigned int *prio, int wait_sec)
{
    struct timespec deadline;
    clock_gettime(CLOCK_REALTIME, &deadline);
    deadline.tv_sec += wait_sec;
    return mq_timedreceive(mqd, buf, buf_size, prio, &deadline);
}

int main(void)
{
    const char *qname = "/helper_test";
    struct mq_attr attr = { 0, 5, 128, 0 };

    /* Create queue and send one test message */
    mqd_t wq = mq_open(qname, O_CREAT | O_WRONLY, 0644, &attr);
    mq_send(wq, "hello world", 12, 5);
    mq_close(wq);

    /* Open for reading */
    mqd_t rq = mq_open(qname, O_RDONLY);
    if (rq == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }

    char buf[128];
    unsigned int prio;

    /* First call: message is there, should succeed immediately */
    ssize_t n = mq_receive_timeout(rq, buf, sizeof(buf), &prio, 2);
    if (n > 0)
        printf("Got message [p=%u]: %.*s\n", prio, (int)n, buf);

    /* Second call: queue is now empty, should timeout after 2 seconds */
    printf("Waiting up to 2 seconds for next message...\n");
    n = mq_receive_timeout(rq, buf, sizeof(buf), &prio, 2);
    if (n == -1 && errno == ETIMEDOUT)
        printf("Timeout! No more messages.\n");

    mq_close(rq);
    mq_unlink(qname);
    return 0;
}

Example 4 – Millisecond-Precision Timeout

For embedded/real-time systems you often need sub-second timeouts. Here is how to set a 500-millisecond deadline with nanosecond precision handling.

/* timed_receive_ms.c — 500 ms timeout
 * Compile: gcc timed_receive_ms.c -o timed_receive_ms -lrt
 */
#define _XOPEN_SOURCE 600
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
#include <mqueue.h>
#include <fcntl.h>

/* Build a timespec: current time + ms milliseconds */
static struct timespec make_deadline_ms(long ms)
{
    struct timespec ts;
    clock_gettime(CLOCK_REALTIME, &ts);

    ts.tv_nsec += ms * 1000000L;   /* convert ms to ns and add */

    /* Normalise: carry overflow nanoseconds into seconds */
    if (ts.tv_nsec >= 1000000000L) {
        ts.tv_sec  += ts.tv_nsec / 1000000000L;
        ts.tv_nsec  = ts.tv_nsec % 1000000000L;
    }
    return ts;
}

int main(int argc, char *argv[])
{
    if (argc != 2) {
        fprintf(stderr, "Usage: %s /queue-name\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    mqd_t mqd = mq_open(argv[1], O_RDONLY);
    if (mqd == (mqd_t)-1) { perror("mq_open"); exit(EXIT_FAILURE); }

    struct mq_attr attr;
    mq_getattr(mqd, &attr);
    char *buf = malloc(attr.mq_msgsize);

    /* 500 ms deadline */
    struct timespec deadline = make_deadline_ms(500);

    unsigned int prio;
    ssize_t n = mq_timedreceive(mqd, buf, attr.mq_msgsize, &prio, &deadline);

    if (n == -1) {
        if (errno == ETIMEDOUT)
            printf("No message in 500 ms.\n");
        else
            perror("mq_timedreceive");
    } else {
        printf("Received: %.*s (prio=%u)\n", (int)n, buf, prio);
    }

    free(buf);
    mq_close(mqd);
    return 0;
}

Comparison: Three Flavours of Send/Receive
Function Queue Full/Empty Behaviour Use When
mq_send / mq_receive Blocking (default) Waits indefinitely Simple producer-consumer
mq_send / mq_receive + O_NONBLOCK Returns immediately EAGAIN if not ready Polling loops, event loops
mq_timedsend / mq_timedreceive Blocks up to deadline ETIMEDOUT if expired Real-time, watchdogs, deadlines

Key Terms

mq_timedsend() mq_timedreceive() struct timespec ETIMEDOUT clock_gettime() CLOCK_REALTIME Absolute timeout _XOPEN_SOURCE 600 POSIX.1d tv_nsec overflow

Interview Questions & Answers
Q1. What is the key difference between mq_receive() and mq_timedreceive()?

Both functions read the highest-priority message from a queue. mq_timedreceive() adds an abs_timeout parameter: if the queue is empty and no message arrives before the absolute deadline, the call fails with errno = ETIMEDOUT instead of blocking forever. Otherwise, the two functions are identical.

Q2. Is the timeout in mq_timedreceive() relative or absolute?

It is absolute — it specifies a point in real time (seconds and nanoseconds since the Unix Epoch), not a duration. To express “wait 5 seconds from now,” you must call clock_gettime(CLOCK_REALTIME, &ts) and add 5 to ts.tv_sec.

Q3. What error code is returned when the timed operation expires?

The function returns -1 and sets errno to ETIMEDOUT.

Q4. What feature macro do you need to expose mq_timedsend() on Linux?

Define _XOPEN_SOURCE 600 (or equivalently _POSIX_C_SOURCE 200112L) before any includes. Without this, the declarations may not be visible and the linker will report undefined symbol errors.

Q5. How do you specify a 250 ms timeout using struct timespec?

Read the current time, add 250,000,000 nanoseconds to tv_nsec, then normalise any overflow into tv_sec: ts.tv_nsec += 250000000L; if (ts.tv_nsec >= 1000000000L) { ts.tv_sec++; ts.tv_nsec -= 1000000000L; }

Q6. What happens on Linux when abs_timeout is NULL?

On Linux it means infinite timeout (same as the blocking non-timed call). However, SUSv3 does not define this behaviour, so portable code must always pass a valid, non-NULL timespec.

Q7. Do the timed functions care about O_NONBLOCK on the queue descriptor?

If O_NONBLOCK is set, the call will not block even if a timeout is provided — it returns EAGAIN immediately if the operation cannot be done right away. The timeout only applies to the case where the descriptor is in blocking mode.

Q8. From which POSIX standard do mq_timedsend() and mq_timedreceive() originate?

They were introduced in POSIX.1d (1999) and are therefore not available on all UNIX implementations. Always check for availability with _POSIX_MESSAGE_PASSING and _POSIX_TIMEOUTS feature-test macros.

Leave a Reply

Your email address will not be published. Required fields are marked *