Ядро делает эту блокировку за вас.
Посмотрите на реализацию в ipc / mqueue.c:
SYSCALL_DEFINE5(mq_timedreceive, mqd_t, mqdes, char __user *, u_msg_ptr,
size_t, msg_len, unsigned int __user *, u_msg_prio,
const struct timespec __user *, u_abs_timeout)
{
...
struct mqueue_inode_info *info;
...
filp = fget(mqdes);
if (unlikely(!filp)) {
ret = -EBADF;
goto out;
}
inode = filp->f_path.dentry->d_inode;
...
spin_lock(&info->lock);
if (info->attr.mq_curmsgs == 0) {
if (filp->f_flags & O_NONBLOCK) {
spin_unlock(&info->lock);
...
} else {
msg_ptr = msg_get(info);
inode->i_atime = inode->i_mtime = inode->i_ctime =
CURRENT_TIME;
/* There is now free space in queue. */
pipelined_receive(info);
spin_unlock(&info->lock);
ret = 0;
}
Каждый mqueue имеет спин-блокировку, которая получается перед проверкой новых сообщений.
Последнее (pipelined_receive) - это то место, где сообщение удалено. Это защищено блокировкой info->, поэтому два потока не могут получить одно и то же сообщение.