mq_notify() API & sigevent Structure POSIX Message Queue

 

mq_notify() API & sigevent Structure
s POSIX Message Queue— Chapter 52.6 | File 1 of 3

Series Navigation: Index File 1: API & sigevent File 2: Signal Notification File 3: Thread Notification

Why Do We Need Notification?

Without notification, a process that wants to receive messages from a POSIX message queue has two choices: block on mq_receive() (which ties up a thread) or poll repeatedly (wasteful CPU usage). Both approaches are inefficient when messages arrive infrequently.

mq_notify() solves this. You register once, then go about other work. The kernel notifies you asynchronously the moment a message arrives on a previously empty queue — either via a signal or by spawning a thread.

This is the same philosophy as epoll for file descriptors, but for message queues.

The mq_notify() Function
#include <mqueue.h>

int mq_notify(mqd_t mqdes, const struct sigevent *notification);

/* Returns: 0 on success, -1 on error */

mqdes is the message queue descriptor (returned by mq_open()). notification is a pointer to a struct sigevent that describes how to notify the process. Passing NULL removes any existing registration.

Registration Rules — How It Works
Rule Details Why It Matters
One process at a time Only ONE process can be registered for notification on a given queue at any moment Trying to register when another is already registered fails with EBUSY
Auto-deregistration Registration is automatically removed after ONE notification is delivered You MUST call mq_notify() again after each notification to keep receiving them
Blocked receiver wins If another process is blocked in mq_receive(), it gets the message instead Notification is only for queues that were empty — a blocked receiver is prioritized
Trigger: empty → nonempty Notification fires only when queue transitions from empty to having at least one message If the queue already has messages when you register, no notification is sent
NULL deregisters Passing NULL as the second argument cancels any existing registration Useful for cleanup before exiting

Notification Flow Diagram

Process calls
mq_notify()
Kernel registers
process for queue
Process does
other work

Message arrives
(queue was empty)
Kernel notifies
process (deregisters)

Process reads messages
and re-registers

The struct sigevent Structure

This structure tells the kernel how to notify the process. You fill in the relevant fields and pass a pointer to mq_notify().

/* Defined in <signal.h> */

struct sigevent {
    int    sigev_notify;              /* Notification method: SIGEV_NONE,
                                         SIGEV_SIGNAL, or SIGEV_THREAD    */

    int    sigev_signo;               /* Signal number (used with SIGEV_SIGNAL) */

    union sigval sigev_value;         /* Data passed to signal handler or
                                         thread function                   */

    void (*sigev_notify_function)(union sigval);
                                      /* Thread function (used with SIGEV_THREAD) */

    void  *sigev_notify_attributes;  /* Thread attributes — cast to pthread_attr_t*
                                         or pass NULL for defaults          */
};

union sigval — what is it?

union sigval {
    int   sival_int;   /* Integer value — use for simple numeric data */
    void *sival_ptr;   /* Pointer value — use to pass a struct/object  */
};

You choose one member. For thread notification, sival_ptr is commonly used to pass a pointer to the message queue descriptor or a context struct. For signal notification with realtime signals, this value is accessible in the signal handler via siginfo_t.si_value.

sigev_notify Values Explained
Value What Happens Fields Used
SIGEV_NONE Process is registered but kernel does nothing when a message arrives. The registration is still consumed (removed) on message arrival. None — only sigev_notify
SIGEV_SIGNAL Kernel sends the signal specified in sigev_signo to the process. If it is a realtime signal, the value in sigev_value is also delivered. sigev_signo, optionally sigev_value
SIGEV_THREAD Kernel creates a new thread and calls the function pointed to by sigev_notify_function as if it were the thread’s start function. sigev_notify_function, sigev_value, optionally sigev_notify_attributes

SIGEV_SIGNAL — siginfo_t Fields Filled In

When SIGEV_SIGNAL is used with a realtime signal and a SA_SIGINFO handler, the kernel fills in the following fields of the siginfo_t structure passed to the handler:

siginfo_t Field Value Set By Kernel
si_code SI_MESGQ — identifies this as a message queue notification
si_signo The signal number
si_value The value from sigev_value — programmer-defined data
si_pid PID of the process that sent (wrote) the message
si_uid Real user ID of the sending process
Note: The si_pid and si_uid fields are filled in on Linux but are not guaranteed on all POSIX implementations. Do not rely on them for portability.

Code Example 1 — Setting Up sigevent for Signal Notification

How to fill the sigevent structure for signal-based notification:

#include <signal.h>
#include <mqueue.h>

int main(void)
{
    mqd_t mqd;
    struct sigevent sev;

    /* Open an existing queue in read-only mode */
    mqd = mq_open("/myqueue", O_RDONLY);
    if (mqd == (mqd_t)-1) {
        perror("mq_open");
        return 1;
    }

    /* Fill in sigevent for signal notification */
    sev.sigev_notify = SIGEV_SIGNAL;
    sev.sigev_signo  = SIGUSR1;          /* Which signal to send */
    sev.sigev_value.sival_int = 42;      /* Optional data (only useful with
                                            realtime signals) */

    /* Register for notification */
    if (mq_notify(mqd, &sev) == -1) {
        perror("mq_notify");
        return 1;
    }

    /* ... process does other work ...
       When a message arrives on an empty queue, SIGUSR1 is sent.
       Registration is removed after first notification — must call
       mq_notify() again inside the signal handler / loop. */

    return 0;
}

Code Example 2 — Using SIGEV_NONE (Reserve Registration)

SIGEV_NONE is used when you want to block other processes from registering but you don’t actually want a signal or thread. You might do this while switching notification methods.

#include <signal.h>
#include <mqueue.h>
#include <stdio.h>

void register_placeholder(mqd_t mqd)
{
    struct sigevent sev;

    sev.sigev_notify = SIGEV_NONE;
    /* sigev_signo and other fields are ignored for SIGEV_NONE */

    if (mq_notify(mqd, &sev) == -1)
        perror("mq_notify SIGEV_NONE");
    else
        printf("Placeholder registration set — no other process can register\n");
}

void deregister(mqd_t mqd)
{
    /* Pass NULL to remove registration entirely */
    if (mq_notify(mqd, NULL) == -1)
        perror("mq_notify deregister");
    else
        printf("Registration removed\n");
}

Code Example 3 — Setting Up sigevent for Thread Notification

For SIGEV_THREAD, you provide a function pointer. The kernel spawns a thread and calls it.

#include <signal.h>
#include <mqueue.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

/* This function is called in a new thread when notification arrives */
void notify_handler(union sigval sv)
{
    mqd_t *mqdp = (mqd_t *)sv.sival_ptr;
    printf("Notification received! Queue descriptor: %d\n", *mqdp);
    /* Read messages from *mqdp here */
    pthread_exit(NULL);
}

int main(void)
{
    mqd_t mqd;
    struct sigevent sev;

    mqd = mq_open("/myqueue", O_RDONLY | O_NONBLOCK);
    if (mqd == (mqd_t)-1) { perror("mq_open"); return 1; }

    /* Pass a pointer to mqd as the argument to the thread function */
    sev.sigev_notify            = SIGEV_THREAD;
    sev.sigev_notify_function   = notify_handler;
    sev.sigev_notify_attributes = NULL;         /* Default thread attrs */
    sev.sigev_value.sival_ptr   = &mqd;         /* Passed to handler */

    if (mq_notify(mqd, &sev) == -1) {
        perror("mq_notify");
        return 1;
    }

    /* Main thread sleeps; notification thread will wake up */
    pause();
    return 0;
}

Common Errors from mq_notify()
Error Meaning Fix
EBUSY Another process is already registered Wait or use a different queue
EBADF Invalid queue descriptor Check mq_open() return value
EINVAL Invalid sigev_notify value or bad signal number Use valid SIGEV_* constants and valid signal numbers
ENOMEM Out of memory for notification data Check system resources

Interview Questions & Answers
Q1. What does mq_notify() do and when is it useful?
mq_notify() lets a process register for asynchronous notification when a message arrives on a previously empty POSIX message queue. It is useful when a process should not block waiting for messages — it can do other work and be notified only when needed. This avoids busy-polling and saves CPU time.
Q2. What is auto-deregistration and why does it matter?
After delivering one notification, the kernel automatically removes the registration. This means the process must call mq_notify() again before the next notification can be received. Failure to re-register means the process becomes “deaf” to future messages after the first one.
Q3. What are the three values of sigev_notify?
SIGEV_NONE — Register without actually sending any notification (used to block other registrations). SIGEV_SIGNAL — Send a signal specified in sigev_signo to the process. SIGEV_THREAD — Spawn a new thread and call the function in sigev_notify_function.
Q4. What happens if you call mq_notify() when another process is already registered?
The call fails with errno set to EBUSY. Only one process at a time can be registered for notification on a given queue.
Q5. Does mq_notify() fire if the queue already has messages when you register?
No. Notification only triggers when the queue transitions from empty to nonempty. If the queue already has messages at registration time, no notification is sent. The process should check the queue immediately after registering to handle this race condition.
Q6. What is union sigval and how is it used with mq_notify()?
union sigval has two members: sival_int (integer) and sival_ptr (pointer). With SIGEV_SIGNAL and a realtime signal, this value is passed into the signal handler via siginfo_t.si_value. With SIGEV_THREAD, it is passed as the argument to the notification thread function. It lets you carry context (like a queue descriptor pointer) into the handler.
Q7. Can a process cancel a notification registration before it fires?
Yes. Call mq_notify(mqdes, NULL) to remove the current registration. This is useful for cleanup. Only the process that originally registered can deregister.
Q8. If a process is blocked in mq_receive() and another process registered for notification, who gets the message?
The process blocked in mq_receive() gets the message. The notification mechanism is bypassed. Notification only fires when no process is actively waiting on the queue.

Leave a Reply

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