A port is a system-wide message repository into which any thread can copy a buffer of data, and from which any thread can then retrieve the buffer. This repository is implemented as a first-in/first-out message queue: A port stores its messages in the order in which they're received, and it relinquishes them in the order in which they're stored. Each port has its own message queue.

Creating and Destroying a Port

The create_port() function creates a new port and assigns it a unique, system-wide port_id number. Although ports are accessible to all threads, the port_id numbers aren't disseminated by the operating system—there's no "find_port" function. If you create a port and want some other thread to be able to write to or read from it, you have to broadcast the port_id number to that thread.

A port is owned by the team in which it was created. When a team dies (when all its threads are killed), the ports that belong to the team are deleted. A team can bestow ownership of its ports to some other team through the set_port_owner() function.

If you want explicitly get rid of a port, you call delete_port(). You can delete any port, not just those that are owned by the team of the calling thread. When you delete a port, all of its unread messages are thrown away. If you want to read this messages, but you don't want any new messages to arrive in the meantime, you should call close_port() before deleting the port. Note that you can't reopen a closed port; after you get done reading the port's messages, you're expected to delete the port.

The Message Queue: Reading and Writing Port Messages

The length of a port's message queue—the number of messages that it can hold at a time—is set when the port is created.

The functions write_port() and read_port() manipulate a port's message queue: write_port() places a message at the tail of the port's message queue; read_port() removes the message at the head of the queue and returns it the caller. write_port() blocks if the queue is full; it returns when room is made in the queue by an invocation of read_port(). Similarly, if the queue is empty, read_port() blocks until write_port() is called.

You can provide a timeout for your port-writing and port-reading operations by using the "full-blown" functions write_port_etc() and read_port_etc(). By supplying a timeout, you can ensure that your port operations won't block forever.

Although each port has its own message queue, all ports share a global "queue slot" pool—there are only so many message queue slots that can be used by all ports taken cumulatively. If too many port queues are allowed to fill up, the slot pool will drain, which will cause write_port() calls on less-than-full ports to block. To avoid this situation, you should make sure that your write_port() and read_port() calls are reasonably balanced.

The write_port() and read_port() functions are the only way to traverse a port's message queue. There's no notion of "peeking" at the queue's unread messages, or of erasing messages that are in the queue.

Port Messages

A port message—the data that's sent through a port—consists of a "message code" and a "message buffer." Either of these elements can be used however you like, but they're intended to fit these purposes:

The message that you pass to write_port() is copied into the port. After write_port() returns, you may free the message data without affecting the copy that the port holds.

When you read a port, you have to supply a buffer into which the port mechanism can copy the message. If the buffer that you supply isn't large enough to accommodate the message, the unread portion will be lost—the next call to read_port() won't finish reading the message.

You typically allocate the buffer that you pass to read_port() by first calling port_buffer_size(), as shown below:

char *buf = NULL;
ssize_t size;
int32 code;

/* We'll assume that my_port is valid.
* port_buffer_size() will block until a message shows up.
if ((size = port_buffer_size(my_port)) < B_OK)
   /* Handle the error */

if (size > 0)
   buf = (char *)malloc(size);

if (buf) {
   /* Now we can read the buffer. */
   if (read_port(my_port, &code, (void *)buf, size) < B_OK)
   /* Handle the error */

Obviously, there's a race condition (in the example) between port_buffer_size() and the subsequent read_port() call—some other thread could read the port in the interim. If you're going to use port_buffer_size() as shown in the example, you shouldn't have more than one thread reading the port at a time.

As stated in the example, port_buffer_size() blocks until a message shows up. If you don't want to (potentially) block forever, you should use the port_buffer_size_etc() version of the function. As with the other …etc() functions, port_buffer_size_etc() provides a timeout option.

Creative Commons License
Legal Notice
This work is licensed under a Creative Commons Attribution-Non commercial-No Derivative Works 3.0 License.