The Application Kit: BLooper

The Application Kit Table of Contents | The Application Kit Index

# BLooper

Derived from: public BHandler

Declared in: be/app/Looper.h

Library: libbe.so

###### Summary

A BLooper object creates a "message loop" that receives messages that are sent or posted to the BLooper. The message loop runs in a separate thread that's spawned (and told to run) when the BLooper receives a `Run()` call. If you're creating your own BLooper, you can invoke `Run()` from within the constructor.

You tell the loop to stop by sending the BLooper a `B_QUIT_REQUESTED` message, which invokes the object's `Quit()` function. You can also call `Quit()` directly, but you have to Lock() the object first (BLooper locking is explained later). `Quit()` deletes the BLooper for you.

| The BApplication class, the most important BLooper subclass, bends the above description in a couple of ways:

- A BApplication takes over the main thread, it doesn't spawn a new one.

- You do have to `delete` `be_app`; you can't just`Quit()` it.

### Messages and Handlers

You can deliver messages to a BLooper's thread by...

- Posting them directly by calling BLooper's `PostMessage()` function.

- Sending them through BMessenger's `SendMessage()` or BMessage's `SendReply()` function.

As messages arrive, they're added to the BLooper's BMessageQueue object. The BLooper takes messages from the queue in the order that they arrived, and calls `DispatchMessage()` for each one. `DispatchMessage()` locks the BLooper and then hands the message to a BHandler object by invoking the handler's `MessageReceived()` function. But which BHandler does the BLooper hand the message to? Here's the path:

- If an incoming message targets a specific BHandler, and if that BHandler is one of the BLooper's eligible handlers (as set through the `AddHandler()` function), the BLooper uses that BHandler. (See the BMessage and BMessenger classes for instructions on how to target a BHandler.)

- Otherwise it hands the message to its preferred handler, as set through `SetPreferredHandler()`.

- If no preferred handler is set, the BLooper itself handles the message (its own implementation of `MessageReceived()` is invoked).

After the handler is finished (when its MessageReceived() returns), the BMessage is automatically deleted and the BLooper is unlocked.

### Locking

Access to many BLooper functions (and some BHandler functions) is proteced by a lock. To invoke a lock-protected functions (or groups of functions), you must first call `Lock()`, and then call `Unlock()` when you're done. The lock is scoped to the calling thread: `Lock()`/`Unlock()` calls can be nested within the thread. Keep in mind that each `Lock()` must balanced by an `Unlock()`.

The BLooper constructor automatically locks the object. It's unlocked when `Run()` is invoked. This means that the `Run()` function—and any other lock-protected functions that you call before you call `Run()`—must be called from the thread that constructed the BLooper.

### Allocation

Because they `delete` themselves when told to quit, BLoopers can't be allocated on the stack; you have to construct them with `new`.

## Hook Functions

- `DispatchMessage()

  `
- `QuitRequested()

  `

## Constructor and Destructor

### BLooper()

`BLooper(`const char *name = NULL, int32 priority = B_NORMAL_PRIORITY, int32 portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY`)`
`BLooper(` BMessage *archive`)`

Assigns the BLooper object a name and then locks it (by calling `Lock()`). priority is a value that describes the amount of CPU attention the message loop will receive once it starts running, and portCapacity is the number of messages the BLooper can hold in its "message port" (this is not the message queue, as explained below).

After you construct the BLooper, you have to tell it to `Run()`. Because the object is locked, `Run()` can only be called from the thread that constructed the object. It's legal to invoke `Run()` from within a subclass implementation of the constructor.

#### Priority

A set of priority values are defined in kernel/OS.h; from lowest to highest, they are:

`B_LOW_PRIORITY` | For threads running in the background that shouldn't interrupt other threads.
`B_NORMAL_PRIORITY` | For all ordinary threads, including the main thread.
`B_DISPLAY_PRIORITY` | For threads associated with objects in the user interface, including window threads.
`B_URGENT_DISPLAY_PRIORITY` | For interface threads that deserve more attention than ordinary windows.
`B_REAL_TIME_DISPLAY_PRIORITY` | For threads that animate the on-screen display.
`B_URGENT_PRIORITY` | For threads performing time-critical computations.
`B_REAL_TIME_PRIORITY` | For threads controlling real-time processes that need unfettered access to the CPUs.

#### Port Capacity

Messages that are sent to a BLooper first show up in a port (as the term is defined by the Kernel Kit), and then are moved to the BMessageQueue. The capacity of the BMessageQueue is virtually unlimited; the capacity of the port is not. Although messages are moved from the port to the queue as quickly as possible, the port can fill up. A full port will block subsequent message senders.

The default port capacity (100), should be sufficient for most apps, but you can fiddle with it through the portCapacity argument.

### ~BLooper()

virtual `~BLooper() `

Frees the message queue and all pending messages and deletes the message loop. BHandlers that have been added to the BLooper are not deleted, but BMessageFilter objects added as common filters are

In general, you should never `delete` your BLooper objects: With the exception of the BApplication object, BLoopers are destroyed by the `Quit()` function.

If you create a BLooper-derived class that uses multiple inheritance, make sure the first class your mixin class inherits from is BLooper; otherwise, you'll crash when you try to close the window. This happens because of an interaction between the window thread how C++ deletes objects of a multiply-inherited class. In other words:

          class myClass : public BLooper, public OtherClass {
          ...
          };

is safe, while

          class myClass : public OtherClass, public BLooper {
          ...
          };

is not.

## Static Functions

### LooperForThread()

static BLooper *`LooperForThread(`thread_id thread`) `

Returns the BLooper object that spawned the specified thread, or `NULL` if the thread doesn't belong to a BLooper.

## Member Functions

### AddCommonFilterList() , RemoveCommonFilterList() , SetCommonFilterList() , CommonFilterList()

`virtual` `void` `AddCommonFilter(BMessageFilter *`filter`) `
`virtual` `bool` `RemoveCommonFilter(BMessageFilter *`filter`) `
`virtual` `void` `SetCommonFilterList(BList *`filters`) `
`BList *``CommonFilterList(void``)` `const `

| For all but `CommonFilterList()`, the BLooper must be locked.

These functions manage the BLooper's list of BMessageFilters. Message filters are objects that screen in-coming messages. In the case of BLooper, each message is passed through all filters in the list before it's passed on to `DispatchMessage()`. The order of the filters in the list is determinate. See the BMessageFilter class for details on how message filters work.

`AddCommonFilter()` adds filter to the end of the filter list (creating a BList container if necessary).

`RemoveCommonFilter()` removes filter from the list, but doesn't free the filter. It returns `true` if successful, and `false` if it can't find the specified filter.

`SetCommonFilterList()` deletes the current filter list and its contents, and replaces it with filters. All elements in filters must be BMessageFilter pointers. The BLooper takes ownership of all objects in filters, as well as filters itself. If filters is `NULL`, the current list is deleted without a replacement.

`CommonFilterList()` returns a pointer to the current list. You can examine the list but you shouldn't modify or delete it.

### AddHandler() , RemoveHandler() , HandlerAt() , CountHandlers() , IndexOf()

void `AddHandler(` BHandler *handler`) `
bool `RemoveHandler(` BHandler *handler`) `
BHandler *`HandlerAt(`int32 index`)` const
int32 `CountHandlers(`void`)` const
int32 `IndexOf(` BHandler *handler`)` const

`AddHandler()` adds handler to the BLooper's list of BHandler objects, and `RemoveHandler()` removes it. Only BHandlers that have been added to the list are eligible to respond to the messages the BLooper dispatches.

`AddHandler()` fails if the handler already belongs to a BLooper; a BHandler can belong to no more than one BLooper at a time. It can change its affiliation from time to time, but must be removed from one BLooper before it can be added to another. `RemoveHandler()` returns `true` if it succeeds in removing the BHandler from the BLooper, and `false` if not or if the handler doesn't belong to the BLooper in the first place.

`AddHandler()` also calls the handler's `SetNextHandler()` function to assign it the BLooper as its default next handler. `RemoveHandler()` calls the same function to set the handler's next handler to `NULL`.

`HandlerAt()` returns the BHandler object currently located at index in the BLooper's list of eligible handlers, or `NULL` if the index is out of range. Indices begin at 0 and there are no gaps in the list. `CountHandlers()` returns the number of objects currently in the list; the count should always be at least 1, since the list automatically includes the BLooper itself. `IndexOf()` returns the index of the specified handler, or `B_ERROR` if that object isn't in the list.

For any of these functions to work, the BLooper must be locked.

See also: `BHandler::Looper()`, `BHandler::SetNextHandler()`, `PostMessage()`, the BMessenger class

### Archive() see BArchivable::Archive()

### CommonFilterList() see AddCommonFilterList()

### CountHandlers() see AddHandler()

### CountLockRequests() see LockingThread()

### CountLocks() see LockingThread()

### CurrentMessage() , DetachCurrentMessage()

BMessage *`CurrentMessage(`void`)` const
BMessage *`DetachCurrentMessage(`void`) `

The message that a BLooper passes to its handler(s) is called the "current message." These functions access the current message; they're meaningless (they return `NULL`) when called from outside the message processing loop.

`CurrentMessage()` simply returns a pointer to the current message without affecting the BMessage object itself. This is particularly useful to functions that respond to system messages (such as `MouseDown()` and `ScreenChanged()`), but that aren't sent the full BMessage object that initiated the response.

`DetachCurrentMessage()` removes the current message from the message queue and passes ownership of it to the caller; deleting the message is the caller's responsibility. This is useful if you want to delay the response to the message without tying up the BLooper. But be careful—if the message sender is waiting for a synchronous reply, detaching the message and holding on to it will block the sender.

### DetachCurrentMessage() see CurrentMessage()

### DispatchMessage()

`virtual` `void` `DispatchMessage(BMessage *`message, `BHandler *`target`) `

`DispatchMessage()` is the BLooper's central message-processing function. It's called automatically as messages arrive in the looper's queue, one invocation per message. You never invoke `DispatchMessage()` yourself.

The default implementation passes message to handler by invoking the latter's `MessageReceived()`:

       target->MessageReceived(message);

The only exception is where message.`what` is `B_QUIT_REQUESTED` and handler is the looper itself; in this case, the object invokes its own `QuitRequested()` function.

You can override this function to dispatch the messages that your own application defines or recognizes. All unhandled messages should be passed to the base class version, as demonstrated below:

       void MyLooper::DispatchMessage(BMessage *msg, BHandler *target)
       {
          switch ( msg->what ) {
          case MY_MESSAGE1:
             ...
             break;
          case MY_MESSAGE2:
             ...
             break;
          default:
             baseClass::DispatchMessage(msg, target);
             break;
           }
       }

Also, note that you mustn't delete message; it's deleted for you..

The system locks the BLooper before calling `DispatchMessage()` and keeps it locked for the duration of the function.

### HandlerAt() see AddHandler()

### IndexOf() see AddHandler()

### IsLocked() see LockingThread()

### Lock() , LockWithTimeout() , Unlock()

`bool` `Lock(void``) `
`status_t` `LockWithTimeout(bigtime_t` timeout`) `
`void` `Unlock(void``) `

`Lock()` locks the BLooper. Locks are held within the context of a thread; while a BLooper is locked, no other thread can invoke its most important functions (`AddHandler()`, `DispatchMessage()`, etc.)

If the looper is already locked (by some other thread), `Lock()` blocks until the looper is unlocked. To set a timeout for the block, use `LockWithTimeout()` instead. timeout is measured in microseconds; if it's 0, the function returns immediately (with or without the lock); if it's `B_INFINITE_TIMEOUT`, it blocks without limit.

`Unlock()` unlocks a locked looper. It can only be called by the thread that currently holds the lock.

Calls to `Lock()`/`LockWithTimeout()` and `Unlock()` can be nested, but locking and unlocking must always be balanced. A single `Unlock()` will not undo a series of `Lock()`'s.

RETURN CODES

`Lock()` returns `true` if it was able to lock the looper, or if it's already locked by the calling thread, and `false` otherwise.

`LockWithTimeout()` returns:

- `B_OK`. The looper was successfully locked.

- `B_TIMED_OUT`. The call timed out without locking the looper.

- `B_BAD_VALUE`. This looper was deleted while the function was blocked.

### LockingThread() , IsLocked() , CountLocks() , CountLockRequests() , Sem()

thread_id `LockingThread(`void`)` const
bool `IsLocked(`void`)` const
int32 `CountLocks(`void`)` const
int32 `CountLockRequests(`void`)` const
sem_id `Sem(`void`)` const

These functions may be useful while debugging a BLooper.

`LockingThread()` returns the thread that currently has the BLooper locked, or –1 if the BLooper isn't locked.

`IsLocked()` returns `true` if the calling thread currently has the BLooper locked (if it's the locking thread) and `false` if not (if some other thread is the locking thread or the BLooper isn't locked).

`CountLocks()` returns the number of times the locking thread has locked the BLooper—the number of `Lock()` (or `LockWithTimeout()`) calls that have not yet been balanced by matching `Unlock()` calls.

`CountLockRequests()` returns the number of threads currently trying to lock the BLooper. The count includes the thread that currently has the lock plus all threads currently waiting to acquire it.

`Sem()` returns the `sem_id` for the semaphore that the BLooper uses to implement the locking mechanism.

See also: `Lock()`

### LockWithTimeout() see Lock()

### MessageReceived()

virtual void `MessageReceived(` BMessage *message`) `

Simply calls the inherited function. For the current release, the BLooper implementation of this function does nothing of importance.

See also: `BHandler::MessageReceived()`

### MessageQueue()

BMessageQueue *`MessageQueue(`void`)` const

Returns the queue that holds messages delivered to the BLooper's thread. You rarely need to examine the message queue directly; it's made available so you can cheat fate by looking ahead.

See also: the BMessageQueue class

### PostMessage()

`status_t` `PostMessage(BMessage *`message`) `
`status_t` `PostMessage(uint32` command`) `
`status_t` `PostMessage(BMessage *`message, `BHandler *`handler, `BHandler *`replyHandler = `NULL``) `
`status_t` `PostMessage(uint32` command, `BHandler *`handler, `BHandler *`replyHandler = `NULL``) `

| `PostMessage()` is similar to `BMessenger::SendMessage()`. The BMessenger version is preferred (it's a bit safer than `PostMessage()`).

Places a message at the far end of the BLooper's message queue. The message will be processed by `DispatchMessage()` when it comes to the head of the queue.

The message can be a full BMessage object (message), or just a command constant (command). In the former case, the message is copied and the caller retains ownership of the argument, which can be deleted as soon as `PostMessage()` returns. In the latter case, a BMessage is created (and deleted) for you.

handler is the designated handler for the message, and must be part of this BLooper's handler chain. If handler is (literally) `NULL`, the designated handler is the BLooper's preferred handler at the time `DispatchMessage()` is called. In the versions of `PostMessage()` that don't have a handler argument, the designated handler is the BLooper object itself.

Replies to the message are delivered to replyHandler. If a replyHandler isn't specified, replies are sent to `be_app_messenger`.

| A BLooper should never post a message to itself from within its own message loop thread.

RETURN CODES

- `B_OK`. The message was successfully posted.

- `B_MISMATCHED_VALUES`. handler doesn't belong to this BLooper.

- Other errors. See the return values for `BMessenger::SendMessage()`.

### PreferredHandler() see SetPreferredHandler()

### Quit()

`virtual` `void` `Quit(void``) `

Shuts down the message loop (if it's running), and deletes the BLooper. The object must be locked.

When `Quit()` is called from the BLooper's thread, the message loop is immediately stopped and any messages in the message queue are deleted (without being processed). Note that, in this case, `Quit()` doesn't return since the calling thread is dead.

When called from another thread, `Quit()` waits until all messages currently in the queue have been handled before it kills the message loop. It returns after the BLooper has been deleted.

### QuitRequested()

`virtual` `bool` `QuitRequested(void``) `

Hook function that's invoked when the BLooper receives a `B_QUIT_REQUESTED` message. You never invoke this function directly. Derived classes implement this function to return `true` if it's okay to quit this BLooper, and `false` if not. Note that this function does not actually quit the object—the code that handles the `B_QUIT_REQUESTED` message does that.

BLooper's default implementation of `QuitRequested()` always returns `true`.

### RemoveCommonFilter() see AddCommonFilterList()

### Run()

`virtual` `thread_id` `Run(void``) `

Spawns the message loop thread and starts it running. `Run()` expects the BLooper to be locked (once only!) when it's called; it unlocks the object before it returns. Keep in mind that a BLooper is locked when it's constructed.

| Calling `Run()` on a BLooper that's already running will dump you into the debugger.

RETURN CODES

- Positive values. The thread was successfully spawned and started; this is the thread_id for the thread.

- Thread errors. See `spawn_thread()` and `resume_thread()`.

- Port errors. See `create_port()`.

### AddCommonFilter()

### SetPreferredHandler() , PreferredHandler()

void `SetPreferredHandler(`BHandlerAddCommonFilter() *handler`)` const
BHandler *`PreferredHandler(`void`)`

These functions set and return the BLooper's preferred handler—the BHandler object that should handle messages not specifically targetted to another BHandler.

To designate the current preferred handler—whatever object that may be—as the target of a message, pass `NULL` for the target handler to `PostMessage()` or to the BMessenger constructor.

Posting or sending messages to the preferred handler can be useful. For example, in the Interface Kit, BWindow objects name the current focus view as the preferred handler. This makes it possible for other objects—such as BMenuItems and BButtons—to target messages to the BView that's currently in focus, without knowing what view that might be. For example, by posting its messages to the window's preferred handler, a Cut menu item can make sure that it always acts on whatever view contains the current selection. See the chapter on the Interface Kit for information on windows, views, and the role of the focus view.

By default, BLoopers don't have a preferred handler; until one is set, `PreferredHandler()` returns `NULL`. Note however, that messages targeted to the preferred handler are dispatched to the BLooper whenever the preferred handler is `NULL`. In other words, the BLooper acts as default preferred handler, even though the default is formally `NULL`.

See also: `BControl::SetTarget()` and `BMenuItem::SetTarget()` in the Interface Kit, `PostMessage()`

### Thread() , Team()

thread_id `Thread(`void`)` const
team_id `Team(`void`)` const

These functions identify the thread that runs the message loop and the team to which it belongs. `Thread()` returns `B_ERROR` if `Run()` hasn't yet been called to spawn the thread and begin the loop. `Team()` always returns the application's `team_id`.

### Unlock() see Lock()

## Constants

### B_LOOPER_PORT_DEFAULT_CAPACITY

#define `B_LOOPER_PORT_DEFAULT_CAPACITY` 100

The default capacity of the port that holds incoming messages before they're placed in the BLooper's BMessageQueue. The capacity is set in the BLooper constructor.

The Application Kit Table of Contents | The Application Kit Index

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

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