Declared in: <kernel/OS.h>
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 is represented by a unique, system-wide port_id number (a positive 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 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.
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; long size; long 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 * sizeof(char)); else buf = 0; /* Now we can read the buffer. */ if (read_port(my_port, &code, (void *)buf, size) < B_NO_ERROR) /* 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.
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_id create_port(long 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 (here), it can't be changed.
This function also 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.
The function returns B_BAD_VALUE if queue_length is out of bounds (less than one or greater than the maximum capacity). It returns B_NO_MORE_PORTS if all port_id numbers are currently being used.
See also: delete_port() , set_port_owner()
long 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.
The function returns B_BAD_PORT_ID if port isn't a valid port; otherwise it returns B_NO_ERROR.
See also: create_port()
port_id find_port(const char *port_name)
Returns the port_id of the named port. If the argument doesn't name an existing port, B_NAME_NOT_FOUND is returned.
See also: create_port()
long get_port_info(port_id port, port_info *info) long get_nth_port_info(team_id team, long n, port_info *info)
These functions copy, into the info argument, the port_info structure for a particular port:
The port_info structure is defined as:
typedef struct port_info { port_id port; team_id team; char name[B_OS_NAME_LENGTH]; long capacity; long queue_count; long total_count; } port_info
The structure's fields are:
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.
The functions return B_NO_ERROR if the designated port is successfully found. Otherwise, they return B_BAD_PORT_ID, B_BAD_TEAM_ID, or B_BAD_INDEX.
long port_buffer_size(port_id port) long port_buffer_size_etc(port_id port, long flags, double 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.
If port doesn't identify an existing port (or if the port is deleted while the function is blocked), B_BAD_PORT_ID is returned. If the timeout limit is exceeded, B_TIMED_OUT is returned. If you specify a timeout value of 0.0 (and set the B_TIMEOUT flag), the function immediately returns B_WOULD_BLOCK if there's no message in the queue.
See also: read_port()
long 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).
If port isn't a valid port identifier, B_BAD_PORT_ID is returned.
See also: get_port_info()
long read_port(port_id port, long *msg_code, void *msg_buffer, long buffer_size) long read_port_etc(port_id port, long *msg_code, void *msg_buffer, long buffer_size, long flags, double 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.
The functions returns B_BAD_PORT_ID if port isn't valid (this includes the case where the port is deleted during a blocked read_port() call), otherwise they return the number of bytes that were written into the msg_buffer argument. If the timeout value is exceeded, B_TIMED_OUT is returned. If you specify a timeout value of 0.0 (and set the B_TIMEOUT flag), the function immediately returns B_WOULD_BLOCK if there's no message in the queue.
See also: write_port() , port_buffer_size()
long 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 deleted. Ownership, otherwise, has no significance--it carries no special privileges or obligations.
To discover a port's owner, use the get_port_info() function.
set_port_owner() fails and returns B_BAD_PORT_ID or B_BAD_TEAM_ID if one or the other argument is invalid. Otherwise it returns B_NO_ERROR.
See also: get_port_info()
long write_port(port_id port, long msg_code, void *msg_buffer, long buffer_size) long write_port_etc(port_id port, long msg_code, void *msg_buffer, long buffer_size, long flags, double timeout)
These functions place a message at the tail of port's message queue. The message consists of msg_code and msg_buffer:
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 queue slot to become free. 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.
If port isn't valid B_BAD_PORT_ID is returned (this includes the case where the port is deleted during a blocked read_port() call). Otherwise, the functions returns B_NO_ERROR. If the timeout value is exceeded, B_TIMED_OUT is returned. If you specify a timeout value of 0.0 (and set the B_TIMEOUT flag), the function immediately returns B_WOULD_BLOCK if the target port's queue is full.
See also: read_port()
The Be Book, HTML Edition, for Developer Release 8 of the Be Operating System.
Copyright © 1996 Be, Inc. All rights reserved.
Be, the Be logo, BeBox, BeOS, BeWare, and GeekPort are trademarks of Be, Inc.
Last modified September 6, 1996.