# The Kernel Kit: Ports

Declared in: be/kernel/OS.h

Library: libroot.so

## Overview

A port is a system-wide message repository into which a thread can copy a buffer of data, and from which some other 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.

There are other ways to send data between threads. Most notably, the data-sending and -receiving mechanism provided by the`send_data()` and`receive_data()` functions can also transmit data between threads. But note these differences between using a port and using the`send_data()`/`receive_data()` functions:

- A port can hold more than one message at a time. A thread can only hold one at a time. Because of this, the function that writes data to a port (`write_port()`) rarely blocks. Sending data to a thread will block if the thread has a previous, unread message.

- The messages that are transmitted through a port aren't directed at a specific recipient--they're not addressed to a specific thread. A message that's been written to a port can be read by any thread. `send_data()`, by definition, has a specific thread as its target.

Ports are largely subsumed by the Application Kit's BMessage class (and relatives). The two features of ports that you can't get at the BMessage level are:

- Ports let you set the length of the message queue.

- Ports can be used in C code (as opposed to C++).

For most applications, these are inessential additions.

### Creating a Port

A port is represented by a unique, system-wide `port_id` number (a positive 32-bit integer). The `create_port()` function creates a new port and assigns it a `port_id` number. Although ports are accessible to all threads, the `port_id` numbers aren't disseminated by the operating system; 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. Typically, ports are used within a single team. The easiest way to broadcast a `port_id` number to the threads in a team is to declare it as a global variable.

A port is owned by the team in which it was created. When a team dies (when all its threads are killed, by whatever hand), 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 can call `delete_port()`. You can delete any port, not just those that are owned by the team of the calling thread.

### 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 `B_MAX_PORT_COUNT` constant provides a reasonable queue length.``

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. When a thread is waiting in a `write_port()` or`read_port()` call, its state is `B_THREAD_SEM_WAIT` (it's waiting to acquire a system-defined, port-specific semaphore).

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_NO_ERROR)
          /* 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.

## Port Functions

### create_port()

          port_id create_port(int32 queue_length, const char *name)

Creates a new port and returns its `port_id` number. The port's name is set to name and the length of its message queue is set to queue_length. Neither the name nor the queue length can be changed once they're set. The name shouldn't exceed `B_OS_NAME_LENGTH` (32) characters.

In setting the length of a port's message queue, you're telling it how many messages it can hold at a time. When the queue is filled--when it's holding queue_length messages--subsequent invocations of `write_port()` (on that port) block until room is made in the queue (through calls to `read_port()`) for the additional messages. As a convenience, you can use the `B_MAX_PORT_COUNT` constant as the queue_length value; this constant represents the (ostensible) maximum port queue length. Once the queue length is set by `create_port()`, it can't be changed.

This function sets the owner of the port to be the team of the calling thread. Ownership can subsequently be transferred through the `set_port_owner()` function. When a port's owner dies (when all the threads in the team are dead), the port is automatically deleted. If you want to delete a port prior to its owner's death, use the `delete_port()` function.

RETURN CODES

- `B_BAD_VALUE.,queue_length` is too big or less than zero.
- `B_NO_MORE_PORTS`. The system couldn't allocate another port.

### delete_port()

          status_t delete_port(port_id port)

Deletes the given port. The port's message queue doesn't have to be empty--you can delete a port that's holding unread messages. Threads that are blocked in `read_port()` or `write_port()` calls on the port are automatically unblocked (and return `B_BAD_SEM_ID`).

The thread that calls `delete_port()` doesn't have to be a member of the team that owns the port; any thread can delete any port.

RETURN CODES

- `B_NO_ERROR.` The port was deleted.
- `B_BAD_PORT_ID`. port isn't a valid port.

### find_port()

          port_id find_port(const char *port_name)

Returns the `port_id` of the named port. port_name should be no longer than 32 characters (`B_OS_NAME_LENGTH`).

RETURN CODES

- `B_NAME_NOT_FOUND`. port_name doesn't name an existing port.

### get_port_info(), get_next_port_info(), port_info

          status_t get_port_info(port_id port, port_info *info)
          status_t get_next_port_info(team_id team,
             uint32 *cookie,
             port_info *info)

          struct {} port_info

Copies information about a particular port into the `port_info` structure designated by info. The first version of the function designates the port directly, by `port_id`.

The `get_next_port_info()` version lets you step through the list of a team's ports through iterated calls on the function. The team argument identifies the team you want to look at; a team value of 0 means the team of the calling thread. The cookie argument is a placemark; you set it to 0 on your first call, and let the function do the rest. The function returns `B_BAD_VALUE` when there are no more ports to visit:

       /* Get the port_info for every port in this team. */
       port_info info;
       int32 cookie = 0;

       while (get_next_port_info(0, &cookie, &info) == B_OK)
          ...

The `port_info` structure is defined as:

          typedef struct port_info {
                port_id port;
                team_id team;
                char name[B_OS_NAME_LENGTH];
                int32 capacity;
                int32 queue_count;
                int32 total_count;
             } port_info

The structure's fields are:

- `port`. The `port_id` number of the port.
- `team`. The`team_id` of the port's owner.
- `name`. The name assigned to the port.
- `capacity`. The length of the port's message queue.
- `queue_count`. The number of messages currently in the queue.
- `total_count`. The total number of message that have been read from the port.

Note that the `total_count` number doesn't include the messages that are currently in the queue.

The information in the `port_info` structure is guaranteed to be internally consistent, but the structure as a whole should be consider to be out-of-date as soon as you receive it. It provides a picture of a port as it exists just before the info-retrieving function returns.

RETURN CODES

- `B_NO_ERROR`. The port was found; info contains valid information.
- `B_BAD_VALUE`. port doesn't identify an existing port, team doesn't identify an existing team, or there are no more ports to visit.

### port_buffer_size(), port_buffer_size_etc()

          ssize_t port_buffer_size(port_id port)

          ssize_t port_buffer_size_etc(port_id port,
             uint32 flags,
             bigtime_t timeout)

These functions return the length (in bytes) of the message buffer that's at the head of port's message queue. You call this function in order to allocate a sufficiently large buffer in which to retrieve the message data.

The `port_buffer_size()` function blocks if the port is currently empty. It unblocks when a `write_port()` call gives this function a buffer to measure (even if the buffer is 0 bytes long), or when the port is deleted.

The `port_buffer_size_etc()` function lets you set a limit on the amount of time the function will wait for a message to show up. To set the limit, you pass `B_TIMEOUT` as the flags argument, and set timeout to the amount of time, in microseconds, that you're willing to wait.

RETURN CODES

- `B_BAD_PORT_ID`. port doesn't identify an existing port, or the port was deleted while the function was blocked.

- `B_TIMED_OUT`. The timeout limit expired.

- `B_WOULD_BLOCK`. You asked for a timeout of 0, but there are no messages in the queue.

See also: `read_port()`

### port_count()

          int32 port_count(port_id port)

Returns the number of messages that are currently in port's message queue. This is the number of messages that have been written to the port through calls to `write_port()` but that haven't yet been picked up through corresponding `read_port()` calls.

|  | This function is provided mostly as a convenience and a semi-accurate debugging tool. The value that it returns is inherently undependable: There's no guarantee that additional `read_port()` or `write_port()` calls won't change the count as this function is returning.

RETURN CODES

- `B_BAD_PORT_ID`. port doesn't identify an existing port.

See also: `get_port_info()`

### read_port(), read_port_etc()

          ssize_t read_port(port_id port,
             int32 *msg_code,
             void *msg_buffer,
             size_t buffer_size)

          ssize_t read_port_etc(port_id port,
             int32 *msg_code,
             void *msg_buffer,
             size_t buffer_size,
             uint32 flags,
             bigtime_t timeout)

These functions remove the message at the head of port's message queue and copy the messages's contents into the msg_code and msg_buffer arguments. The size of the msg_buffer buffer, in bytes, is given by buffer_size. It's up to the caller to ensure that the message buffer is large enough to accommodate the message that's being read. If you want a hint about the message's size, you should call `port_buffer_size()` before calling this function.

If port's message queue is empty when you call`read_port()`, the function will block. It returns when some other thread writes a message to the port through `write_port()`. A blocked read is also unblocked if the port is deleted.

The `read_port_etc()` function lets you set a limit on the amount of time the function will wait for a message to show up. To set the limit, you pass `B_TIMEOUT` as the flags argument, and set timeout to the amount of time, in microseconds, that you're willing to wait.

RETURN CODES

A successful call returns the number of bytes that were written into the msg_buffer argument.

- `B_BAD_PORT_ID`. port doesn't identify an existing port, or the port was deleted while the function was blocked.

- `B_TIMED_OUT`. The timeout limit expired.

- `B_WOULD_BLOCK`. You asked for a timeout of 0, but there are no messages in the queue.

See also: `write_port()`, `port_buffer_size()`

### set_port_owner()

          status_t set_port_owner(port_id port, team_id team)

Transfers ownership of the designated port to team. A port can only be owned by one team at a time; by setting a port's owner, you remove it from its current owner.

There are no restrictions on who can own a port, or on who can transfer ownership. In other words, the thread that calls `set_port_owner()` needn't be part of the team that currently owns the port, nor must you only assign ports to the team that owns the calling thread (although these two are the most likely scenarios).

Port ownership is meaningful for one reason: When a team dies (when all its threads are dead), the ports that are owned by that team are freed. Ownership, otherwise, has no significance--it carries no special privileges or obligations.

To discover a port's owner, use the `get_port_info()` function.

RETURN CODES

- `B_NO_ERROR`. Ownership was successfully transferred.
- `B_BAD_PORT_ID`. port doesn't identify a valid port.
- `B_BAD_TEAM_ID`. team doesn't identify a valid team.

See also: `get_port_info()`

### write_port(), write_port_etc()

          status_t write_port(port_id port,
             int32 msg_code,
             void *msg_buffer,
             size_t buffer_size)

          status_t write_port_etc(port_id port,
             int32 msg_code,
             void *msg_buffer,
             size_t buffer_size,
             uint32 flags,
             bigtime_t timeout)

These functions place a message at the tail of port's message queue. The message consists of msg_code and msg_buffer:

- msg_code holds the "message code." This is a mask, flag, or other predictable value that gives a general representation of the message.

- msg_buffer is a pointer to a buffer that can be used to supply additional information. You pass the length of the buffer, in bytes, as the value of the buffer_size argument. The buffer can be arbitrarily long.

If the port's queue is full when you call `write_port()`, the function will block. It returns when a `read_port()` call frees a slot in the queue for the new message. A blocked `write_port()` will also return if the target port is deleted.

The `write_port_etc()` function lets you set a limit on the amount of time the function will wait for a free queue slot. To set the limit, you pass `B_TIMEOUT` as the flags argument, and set timeout to the amount of time, in microseconds, that you're willing to wait.

RETURN CODES

- `B_NO_ERROR`. The port was successully written to.

- `B_BAD_PORT_ID`. port doesn't identify an existing port, or the port was deleted while the function was blocked.

- `B_TIMED_OUT`. The timeout limit expired.

- `B_WOULD_BLOCK`. You asked for a timeout of 0, but there are no free slots in the message queue.

See also: `read_port()`

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

Copyright © 1998 Be, Inc. All rights reserved.

Last modified March 26, 1998.