The Kernel Kit: Port Concepts

The Kernel Kit Table of Contents | The Kernel Kit Index

# Port Concepts

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 code (a single four-byte value) should be a mask, flag, or other predictable value that gives a general representation of the flavor or import of the message. For this to work, the sender and receiver of the message must agree on the meanings of the values that the code can take.

- The data in the message buffer can elaborate upon the code, identify the sender of the message, or otherwise supply additional information. The length of the buffer isn't restricted. To get the length of the message buffer that's at the head of a port's queue, you call the `port_buffer_size()` function.

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.

The Kernel Kit Table of Contents | The Kernel Kit Index

The Be Book,
...in lovely HTML...
for BeOS Release 5.

Copyright © 2000 Be, Inc. All rights reserved..