Be Driven
  Device Drivers in the Be Os
     
    Styles of Interprocess Communication

Styles of message based Inter Process Communication

Large sections are parragraphed from.

Be Newsletter, Issue 13, March 6, 1996
Moving Data Around the BeBox
By Peter Potrebic

Information on Thread scheduling techniques compiled from qnx.

Thread Messages

Thread messages use the simple API of send_data and receive_data. This is perhaps the most basic messaging mechanism in the system. These messages aren't buffered, so a call to send_data will block until it's read by the target thread. This mechanism is used in several places in the system, including the initial bootstrapping of applications with the app_server. When an application launches, it uses send_data to send a message to the app_server's 'picasso' thread. This message contains the IDs of a couple of ports; further messaging between the application and the app_server will use these ports.

This can be used as a form of Synchronization between applications.

<-- To Do : Steal QNX examples -->

Areas

Shared Memory Techniques.
If you have jumped directly here, read the chapters on memory.

Port Mechanism

The third kernel facility that supports messaging is the port mechanism. Ports provide a system-wide buffered messaging system. These messages can be of arbitrary size. Anyone with a port_id can read from or write to the port. When you create a port, you specify the number of messages that the port can hold. If the port fills, the next call to write_port will block. At first glance this might seem undesirable, as code cannot assume that write_port won't block. We chose this design to prevent an orphaned port (a port that never gets read) from consuming all of system memory.

Those buffered messages have to be stored somewhere -- the system wouldn't be too happy buffering 1,493,467 1 K messages or around 1,500 MB of data. The BMessage class, along with BMessenger and BLooper, make use of ports to pass messages between teams. When using ports, take care to synchronize the writing and reading of messages. If the 'virtual' message spans multiple calls to write_port, you must take steps to correctly deal with other threads that might be writing to the same port.

Similar issues arise if multiple threads read from the same port.

That gives a basic overview of messaging, from the kernel's perspective. The Be OS also has several higher-level messaging systems, used for different purposes, that are implemented on top of the lower-level services. The most visible form of messaging in the Be OS is defined by the BLooper and BMessage classes. A BLooper is an abstraction of the thread running a message loop. BMessages are posted or sent to a looper and then dispatched to a message handler. (Release 1.1d7 sneak preview: The BReceiver class will be renamed BHandler.) A BMessage is a structured collection of data.

This data includes a single 4-byte 'what' identifier. To this you can add arbitrary data entries, each entry having both a string name and a 4-byte type identifier. You can add multiple chunks of data under the same
name and type. The data in a message is kept in what we call the "shared heap," a heap shared by all applications in the system. This heap is created using the kernel area API. When messages move between threads in the same team -- typically using BLooper::PostMessage -- the team is simply given the pointer to the shared data. Things are a little more complicated when using BMessenger::SendMessage to send a message to another team. In this case, the address of the data in the shared heap is written into a port. Once the destination reads the address out of the port, it has complete access to the data of the message. This design provides for fairly efficient messaging, as the data is only copied once -- into the shared heap. From
then on it can be passed among many different threads and teams.

Media Kit Tricks.

The final data-interchange mechanism is defined by the Media Kit. The BeBox is designed to be a dream machine for audio/video developers. One of the big hurdles in developing applications that deal with audio/video data is being able to efficiently move very large amounts of data between interested parties -- even those in different address spaces. The Media Kit is designed with this in mind. At its core is the BStream class, which implements a data-streaming facility using shared memory (there's that kernel area mechanism again). The BStream class synchronizes access to data that lives in dynamic areas of shared memory. Each participant in the stream is given access to the buffers present in the stream. Although BStream is defined in the Media Kit, it isn't constrained to just media data: You can use the class can for all kinds of tasks. If the API looks interesting, give it a try.

TCP/IP
Transmission Control Protocol / Internet Protocol

Remote Program Calls

InterProcess Communication and Device Drivers

Comparison on Messaging Protocols used in Be

Volume II, Issue 48; December 2, 1998
DEVELOPERS' WORKSHOP: Back to Basics
By Stephen Beaulieu hippo@be.com

"In this article I'm going to reinvestigate some of the basic building blocks of the BeOS. We'll look at what I'll call the AppKit model: BMessages, BLoopers, BHandlers, and BMessengers.

The BMessage is fundamentally a data container, commonly used to hold both instructions and data to be acted upon. BHandlers are objects that perform an action when BMessages are delivered to them; they handle the incoming message. BLoopers are threaded BHandlers that run a message loop, waiting for incoming BMessages and dispatching them to the appropriate BHandlers. BMessengers are system wide tokens that represent a given BLooper-BHandler pair, delivering messages to (and replies from) the specified BHandler.

The most visible examples of BLoopers and BHandlers are BApplications, BWindows, and BViews. These lie at the heart of the BeOS APIs, and are common to most BeOS applications. Many developers, however, seem to use the AppKit model only in their interface areas, where it is pretty much required. Since the AppKit model has other valid uses, I'm going to offer some design ideas that may persuade developers to take advantage of its versatility. First though, a list of the AppKit model's advantages and disadvantages to keep in mind while reviewing my designs.

Advantages:
Uses a common, familiar system where a great deal of organizational work is handled by the BeOS. This includes a well-defined communication system, automatic threading of your app, and built-in object management through the BLooper AddHandler and RemoveHandler functions.

The public class interface is easily extendable by extending the messaging protocol used. The functions to handle the new messages are usually private functions, and can be extended as necessary.

The interface can be exposed to interapplication systems by publishing the messaging protocol. This allows other apps doing complementary work to interact with your application easily. The system interface is eligible for scripting, since the AppKit model is the basis of the BeOS scripting mechanism.

Disadvantages:

A BLooper thread's main responsibility of is to run the message loop, not some other tasks. To have threads work on other tasks requires using Kernel Kit threads. The BLooper threading model is therefore not always appropriate to the task at hand. However, combining the two models can work well (i.e., a BLooper with extra, special-purpose threads for other tasks).

BHandlers can belong to only one BLooper, effectively serializing access to each handler. This can be problematic in a system where the BHandler would (ideally) be accessible by multiple threads. Some designs can work around the limitation by creating a new BHandler subclass for each BLooper, but this works only when the BHandlers themselves do not encapsulate data that needs to be instantiated only once.

Adding information to BMessages requires copying that data. This can introduce significant overhead if large amounts of information need to be transmitted, or if the data needs to be looked at many times over the course of an operation. Introducing other methods of data sharing (like putting a
reference to a shared memory area or a pointer to data into the BMessage) can reduce the size and complexity of the messages. Note that this might lead to some undesirable consequences, as the recipient of the BMessage no longer has to go through the messaging mechanism to access the data.
Keeping these advantages and disadvantages in mind, here are two design schemes that use the AppKit model: the Handler as Data Object and Handler as Operation. Handler as Data Object In this scheme, the BHandler contains both the data to be acted upon and the knowledge of how modifications are to be performed. The data is encapsulated in a self-modifying object.

BMessages serve as instructions for what actions to perform. The BLooper serves as the initial interface to the various objects, but would usually pass back BMessengers for the appropriate data objects, so the outside processes can deal with them directly.

Example: Transaction Server

Here, the data object can be independently acted upon by multiple threads/applications while in a guaranteed consistent state. BMessages instruct the server to create, delete, or modify objects. Change notifications can be sent back to all interested processes. Furthermore, the transactions can be recorded so that a previous state could be reinstated by rewinding the transaction stack.

Handler as Operation In this scheme, each BHandler represents an operation that can be performed on some data. The BMessage carries the data to modify and instructions about which operations to carry out. The BLooper serves as a common interface to the operations, investigating the instructions and passing the data to the operations in the correct order, then sending the modified data back to its origin.

Example: Data Filters

Use BHandlers to represent add-on filters to manipulate data. Have each add-on create an entry function that returns a BHandler that performs the appropriate filter. Then simply pass the data to each filter as appropriate. This could be parallelized by calling the entry function for a new BHandler for each thread that needs a copy of the filter, at the expense of more memory.

Example: State Machine

A BLooper represents a state machine, with BHandlers representing each state. The looper passes the appropriate instructions off to each state, which responds to them and asks the BLooper to change state when appropriate. In this example, the BLoopers might contain the data to act upon, rather than a BMessage, but the view from the BHandler is the same.

You can find some simple example code for these two schemes at:
<ftp://ftp.be.com/pub/samples/application_kit/AppKitModel.zip>

Both schemes perform the same work, transforming strings to uppercase, lowercase, or mixed case (with every word capitalized.) They implement the code to modify the strings the same way, using the BString class functions: ToUpper, ToLower, and CapitalizeEachWord. They also act on the same two strings. The only difference between the two examples is the schemes used to organize the code (and correspondingly, the printed output).

This structure is obviously overkill for simple string modification, but the schemes become more useful as the complexity of the data and operations increases. Strings are just an easy way to demonstrate the various designs. The Notes file in each folder explains how the project is put together. Also, note that both applications are useful only when run from the command line, as all feedback is through printf() calls.

I hope these designs can be helpful in your programs, or at least will start you thinking more about the overall design of your application.
"


The Communal Be Documentation Site
1999 - bedriven.miffy.org