Timed MQ I/O
Intermediate
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.
#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) */
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.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:
current time (T_now)
e.g. 5 seconds
T_now + 5s
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 {
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;
}
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.
Return success
→ Return success
→ Return -1
errno = ETIMEDOUT
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;
}
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;
}
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;
}
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;
}
| 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
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.
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.
The function returns -1 and sets errno to ETIMEDOUT.
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.
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; }
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.
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.
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.
