Next: , Previous: Message Send, Up: Messaging Interface

4.2.6 Message Receive

The receive operation dequeues a message from a port. The receiving task acquires the port rights and out-of-line memory regions carried in the message.

The rcv_name argument specifies a port or port set from which to receive. If a port is specified, the caller must possess the receive right for the port and the port must not be a member of a port set. If no message is present, then the call blocks, subject to the MACH_RCV_TIMEOUT option.

If a port set is specified, the call will receive a message sent to any of the member ports. It is permissible for the port set to have no member ports, and ports may be added and removed while a receive from the port set is in progress. The received message can come from any of the member ports which have messages, with the proviso that a member port with messages will not be indefinitely starved. The msgh_local_port field in the received message header specifies from which port in the port set the message came.

The rcv_size argument specifies the size of the caller's message buffer. The mach_msg call will not receive a message larger than rcv_size. Messages that are too large are destroyed, unless the MACH_RCV_LARGE option is used.

The destination and reply ports are reversed in a received message header. The msgh_local_port field names the destination port, from which the message was received, and the msgh_remote_port field names the reply port right. The bits in msgh_bits are also reversed. The MACH_MSGH_BITS_LOCAL bits have the value MACH_MSG_TYPE_PORT_SEND if the message was sent to a send right, and the value MACH_MSG_TYPE_PORT_SEND_ONCE if was sent to a send-once right. The MACH_MSGH_BITS_REMOTE bits describe the reply port right.

A received message can contain port rights and out-of-line memory. The msgh_local_port field does not receive a port right; the act of receiving the message destroys the send or send-once right for the destination port. The msgh_remote_port field does name a received port right, the reply port right, and the message body can carry port rights and memory if MACH_MSGH_BITS_COMPLEX is present in msgh_bits. Received port rights and memory should be consumed or deallocated in some fashion.

In almost all cases, msgh_local_port will specify the name of a receive right, either rcv_name or if rcv_name is a port set, a member of rcv_name. If other threads are concurrently manipulating the receive right, the situation is more complicated. If the receive right is renamed during the call, then msgh_local_port specifies the right's new name. If the caller loses the receive right after the message was dequeued from it, then mach_msg will proceed instead of returning MACH_RCV_PORT_DIED. If the receive right was destroyed, then msgh_local_port specifies MACH_PORT_DEAD. If the receive right still exists, but isn't held by the caller, then msgh_local_port specifies MACH_PORT_NULL.

Received messages are stamped with a sequence number, taken from the port from which the message was received. (Messages received from a port set are stamped with a sequence number from the appropriate member port.) Newly created ports start with a zero sequence number, and the sequence number is reset to zero whenever the port's receive right moves between tasks. When a message is dequeued from the port, it is stamped with the port's sequence number and the port's sequence number is then incremented. The dequeue and increment operations are atomic, so that multiple threads receiving messages from a port can use the msgh_seqno field to reconstruct the original order of the messages.

These options modify MACH_RCV_MSG. If MACH_RCV_MSG is not also specified, they are ignored.

The timeout argument should specify a maximum time (in milliseconds) for the call to block before giving up.1 If no message arrives before the timeout interval elapses, then the call returns MACH_RCV_TIMED_OUT. A zero timeout is legitimate.
The notify argument should specify a receive right for a notify port. If receiving the reply port creates a new port right in the caller, then the notify port is used to request a dead-name notification for the new port right.
If specified, the mach_msg call will return MACH_RCV_INTERRUPTED if a software interrupt aborts the call. Otherwise, the receive operation will be retried.
If the message is larger than rcv_size, then the message remains queued instead of being destroyed. The call returns MACH_RCV_TOO_LARGE and the actual size of the message is returned in the msgh_size field of the message header.

The receive operation can generate the following return codes. These return codes imply that the call did not dequeue a message:

The specified rcv_name was invalid.
The specified port was a member of a port set.
The timeout interval expired.
A software interrupt occurred.
The caller lost the rights specified by rcv_name.
rcv_name specified a receive right which was moved into a port set during the call.
When using MACH_RCV_LARGE, and the message was larger than rcv_size. The message is left queued, and its actual size is returned in the msgh_size field of the message buffer.

These return codes imply that a message was dequeued and destroyed:

A resource shortage prevented the reception of the port rights in the message header.
When using MACH_RCV_NOTIFY, the notify argument did not denote a valid receive right.
When not using MACH_RCV_LARGE, a message larger than rcv_size was dequeued and destroyed.

In these situations, when a message is dequeued and then destroyed, the reply port and all port rights and memory in the message body are destroyed. However, the caller receives the message's header, with all fields correct, including the destination port but excepting the reply port, which is MACH_PORT_NULL.

These return codes imply that a message was received:

A resource shortage prevented the reception of a port right or out-of-line memory region in the message body. The message header, including the reply port, is correct. The kernel attempts to transfer all port rights and memory regions in the body, and only destroys those that can't be transferred.
The specified message buffer was not writable. The calling task did successfully receive the port rights and out-of-line memory regions in the message.
A message was received.

Resource shortages can occur after a message is dequeued, while transferring port rights and out-of-line memory regions to the receiving task. The mach_msg call returns MACH_RCV_HEADER_ERROR or MACH_RCV_BODY_ERROR in this situation. These return codes always carry extra bits (bitwise-ored) that indicate the nature of the resource shortage:

There was no room in the task's IPC name space for another port name.
There was no room in the task's VM address space for an out-of-line memory region.
A kernel resource shortage prevented the reception of a port right.
A kernel resource shortage prevented the reception of an out-of-line memory region.

If a resource shortage prevents the reception of a port right, the port right is destroyed and the caller sees the name MACH_PORT_NULL. If a resource shortage prevents the reception of an out-of-line memory region, the region is destroyed and the caller receives a zero address. In addition, the msgt_size (msgtl_size) field in the data's type descriptor is changed to zero. If a resource shortage prevents the reception of out-of-line memory carrying port rights, then the port rights are always destroyed if the memory region can not be received. A task never receives port rights or memory regions that it isn't told about.


[1] If MACH_RCV_TIMEOUT is used without MACH_RCV_INTERRUPT, then the timeout duration might not be accurate. When the call is interrupted and automatically retried, the original timeout is used. If interrupts occur frequently enough, the timeout interval might never expire.