# The Application Kit: BLooper

Derived from: public BHandler

Declared in: be/app/Looper.h

Library: libbe.so

## Overview

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()
` Passes incoming messages to a BHandler; can be overridden to change the way certain messages or classes of messages are dispatched.

`QuitRequested()
` Can be implemented to decide whether a request to terminate the message loop and destroy the BLooper should be honored or not.

## 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.

## Static Functions

### Instantiate()

          static BLooper *Instantiate(BMessage *archive)

Returns a new BLooper object, allocated by `new` and created with the version of the constructor that takes a BMessage archive. However, if the archive message doesn't contain data for a BLooper object, this function returns `NULL`.

See also: `BArchivable::Instantiate()`, `instantiate_object()`, `Archive()`

### 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

### AddCommonFilter() see SetCommonFilterList()

### 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()

          virtual status_t Archive(BMessage *archive, bool deep = true) const

Archives the BLooper by recording the priority of its thread and the capacity of its port in the BMessage archive passed as an argument.

See also: `BArchivable::Archive()`, `Instantiate()` static function

### CommonFilterList() see SetCommonFilterList()

### CountHandlers() see AddHandler()

### 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)

Dispatches messages as they're received by the BLooper's thread. Precisely how they're dispatched depends on the message and the designated target BHandler. The BWindow and BApplication classes that derive from BLooper implement their own versions of this function to provide for special dispatching of system messages. Each class defines its own set of such messages.

The target may be the BHandler object that was named when the message was posted, the handler that was specified when the BMessenger was constructed, the current preferred handler, the handler that was designated as the target for a reply message, or (for a BWindow) the BView where the message was dropped. It might be the BLooper itself, acting as a specific target or in its capacity as the default preferred handler. For system messages the target may be `NULL`; if so, the dispatcher must figure out a target for the message based on the contents of the BMessage object.

`DispatchMessage()` is the first stop in the message-handling mechanism. The BLooper's thread calls it automatically as it reads messages from the queue--you never call it yourself.

BLooper's version of `DispatchMessage()` dispatches `B_QUIT_REQUESTED` messages by calling its own `QuitRequested()` function, but only if the message is targeted to the BLooper itself. All other messages are forwarded to the target's `MessageReceived()` function. The BApplication and BWindow classes add other kinds of message-specific dispatching.

You can override this function to dispatch the messages that your own application defines or recognizes. Of course, you can also just wait for these messages to fall through to `MessageReceived()`--the choice is yours. If you do override `DispatchMessage()`, you should:

- Call the base class version of the function after you've handled your own messages.
- Exclude all messages that you've handled yourself from the base version call.

For example:

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

Don't delete the messages you handle when you're through with them; they're deleted for you.

The system locks the BLooper before calling `DispatchMessage()` and keeps it locked for the duration of the thread's response to the message (until `DispatchMessage()` returns).

See also: the BMessage class, `BHandler::MessageReceived()`, `QuitRequested()`

### HandlerAt() see AddHandler()

### IndexOf() see AddHandler()

### IsLocked() see LockingThread()

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

          bool Lock(void)

          status_t LockWithTimeout(bigtime_t timeout)

          void Unlock(void)

These functions lock and unlock the BLooper. Only one thread can have the BLooper locked at any given time.

`Lock()` blocks until it can lock the object, then returns `true`. If the calling thread already has the object locked, it returns `true` immediately. If another thread has the BLooper locked, it waits until that thread releases the lock and it can acquire it; it then returns `true`. It returns `false` if the BLooper is destroyed while you're waiting for `Lock()` to return.

`LockWithTimeout()` lets you set a limit (in microseconds) on the amount of time you want the BLooper to block while waiting for the lock. If the time limit expires, the function returns `B_TIMED_OUT`. If the timeout is 0, it doesn't block but returns immediately with or without the lock. If the timeout is `B_INFINITE_TIMEOUT`, it blocks without limit, just as `Lock()` does.

If it locks the BLooper (or if the calling thread already has it locked), `LockWithTimeout()` returns `B_OK`. In addition to `B_TIMED_OUT`, it may also return `B_BAD_VALUE` if the BLooper has been deleted, is invalid, or was improperly allocated. Each of these failures would cause `Lock()` to return `false`.

Note that if `Lock()` returns 0 (`false`), it has failed to lock the BLooper, but if `LockWithTimeout()` returns 0 (`B_OK`), it has succeeded.

`Unlock()` releases the lock previously obtained by `Lock()` or `LockWithTimeout()`. Only the locking thread should call `Unlock()`. This is the natural result if it's called in the same section of code to balance a previous `Lock()` or `LockWithTimeout()` call, as follows:

       if ( myLooper->Lock() ) {
           myLooper->DoSomethingCritical();
           . . .
           myLooper->Unlock();
       }

Calls to `Lock()` (or `LockWithTimeout()`) and `Unlock()` can be nested. For example, the function that's called within the brace of the lock in the example above can itself call `Lock()` and `Unlock()`:

       status_t MyLooperClass::DoSomethingCritical(void)
       {
           if ( Lock() ) {
               . . .
               Unlock();
               return B_OK;
           }
           return B_ERROR;
       }

If the locking functions are called more than once from the same thread, it will take an equal number of `Unlock()` calls from that thread to unlock the BLooper. Only when `Unlock()` has released the lock at the base level will another thread be permitted to lock the BLooper.

It's generally not necessary to lock a BLooper when calling functions defined in the class itself or in a derived class. The BLooper is locked for you when:

- It's constructed; `Run()` unlocks it.

- It dispatches a message; it remains locked until the response to the message is complete.

Moreover, BApplication and BWindow functions are implemented to call `Lock()` and `Unlock()` when necessary. Functions you define in classes derived from BLooper (or from BApplication and BWindow) should also call `Lock()` (or `LockWithTimeout()`) and `Unlock()`.

See also: `LockingThread()`, `BMessenger::LockTarget()`, the BLocker class in the Support Kit

### 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()`

### 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,
             BHandler *handler,
             BHandler *replyHandler = NULL)
          status_t PostMessage(uint32 command,
             BHandler *handler,
             BHandler *replyHandler = NULL)
          status_t PostMessage(BMessage *message)
          status_t PostMessage(uint32 command)

Delivers a message to the BLooper, just as constructing a BMessenger and calling `SendMessage()` would.

If a target handler object is named for the message, it will be passed as the designated handler to `DispatchMessage()`. `DispatchMessage()` will, in turn, call an appropriate function of the handler to respond to the message. However, if the target BHandler isn't associated with the BLooper (if the handler's `Looper()` function returns `NULL` or some other BLooper object), the posting fails. A BHandler must be associated with a BLooper before it can be the target for dispatched messages; it can't get messages from any other BLooper except the one to which it belongs. For example, BViews in the Interface Kit can receive messages only from the BWindows to which they're attached.

If the handler is `NULL`, the designated handler will be the BLooper's preferred handler at the time `DispatchMessage()` is called.

For the versions of `PostMessage()` that take a single argument and don't allow you to designate a handler, the handler will be the BLooper object. These shorthand functions may not be supported in the future. It's better programming practice to name the BLooper explicitly as the target handler.

Replies to the posted message will be delivered to the replyHandler BHandler. Like the target handler, this object must belong to a BLooper (not necessarily this BLooper) or be a BLooper itself. If a replyHandler isn't specified, replies will be delivered to the BApplication object.

The caller retains ownership of the posted message; it's safe to delete it when `PostMessage()` returns.

If a command is passed rather than a message, `PostMessage()` creates a BMessage object, initializes its `what` data member to command, and posts it. This simply saves you the step of constructing a BMessage when it won't contain any data. For example, this code

       myWindow->PostMessage(command, handler);

is equivalent to:

       BMessage message(command);
       myWindow->PostMessage(&message, handler);

`PostMessage()` returns `B_OK` if successful, `B_MISMATCHED_VALUES` if the posting fails because the proposed target BHandler doesn't belong to the BLooper, and `B_ERROR`, `B_BAD_PORT_ID`, or some other error if it fails because the BLooper is invalid or corrupted.

See also: `BHandler::Looper()`, `DispatchMessage()`

### PreferredHandler() see SetPreferredHandler()

### Quit()

          virtual void Quit(void)

Shuts down and deletes the BLooper; the object must be locked for this to succeed.

If `Run()` hasn't been called yet, `Quit()` just deletes the BLooper object. But if `Run()` has been called, it exits the message loop, frees the message queue, kills the thread, and then deletes the BLooper object.

When `Quit()` is called from the BLooper's thread, all this happens immediately. Any pending messages are ignored and destroyed. Because the thread dies, `Quit()` doesn't return.

However, when called from another thread, `Quit()` waits until all previously posted messages (all messages already in the queue) work their way through the message loop and are handled. It then destroys the BLooper and returns only after the loop, queue, thread, and object no longer exist.

`Quit()` therefore terminates the BLooper synchronously; when it returns, you know that everything has been destroyed. To quit the BLooper asynchronously, you can post a `B_QUIT_REQUESTED` message to the thread (that is, a BMessage with `B_QUIT_REQUESTED` as its `what` data member). `PostMessage()` places the message in the queue and returns immediately.

When it gets a `B_QUIT_REQUESTED` message, the BLooper calls the `QuitRequested()` virtual function. If `QuitRequested()` returns `true`, as it does by default, it then calls `Quit()`.

See also: `QuitRequested()`

### QuitRequested()

          virtual bool QuitRequested(void)

Implemented by derived classes to determine whether the BLooper should quit when requested to do so. The BLooper calls this function to respond to `B_QUIT_REQUESTED` messages. If it returns `true`, the BLooper calls `Quit()` to exit the message loop, kill the thread, and delete itself. If it returns `false`, the request is denied and no further action is taken.

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

A request to quit that's delivered to the BApplication object is, in fact, a request to quit the entire application, not just one thread. BApplication therefore overrides `QuitRequested()` to pass the request on to each window thread before shutting down.

For BWindow objects in the Interface Kit, a request to quit might come from the user clicking the window's close button (a quit-requested event for the window), from the user's decision to quit the application (a quit-requested event for the application), from a Close menu item, or from some other occurrence that forces the window to close.

Classes derived from BWindow typically implement `QuitRequested()` to give the user a chance to save documents before the window is destroyed, or to cancel the request.

If a BWindow represents the last window the application has open (or the last one that gives the user access to menus and the ability to continue doing work), closing the window is tantamount to quitting the application. In this case, `QuitRequested()` should make sure the application quits by passing the request along to the BApplication object. For example

       bool MyWindow::QuitRequested()
       {
           . . .
           if ( myDocuments <= 1 )
               be_app->PostMessage(B_QUIT_REQUESTED, be_app);
           return true;
       }

After asking the application to quit, `QuitRequested()` returns `true` to immediately dispose of the window. If it returns `false`, BApplication's version of the function will again request the window to quit.

If you call `QuitRequested()` from your own code, be sure to also provide the code that calls `Quit()`:

       if ( myLooper->QuitRequested() )
           myLooper->Quit();

See also: `BApplication::QuitRequested()`, `Quit()`

### RemoveCommonFilter() see SetCommonFilterList()

### Run()

          virtual thread_id Run(void)

Spawns a thread at the priority level that was specified when the BLooper was constructed and begins running a message loop in that thread. If successful, this function returns the thread identifier. If unsuccessful, it returns `B_NO_MORE_THREADS` or `B_NO_MEMORY` to indicate why.

`Run()` expects the BLooper to be locked when it's called--and to be locked just once. Since a BLooper is locked on construction, you should not lock it again before calling `Run()`. `Run()` will unlock the BLooper, but make sure that it's locked while the thread responds to each dispatched message.

A BLooper can be run only once. If called a second time, `Run()` returns `B_ERROR`, but doesn't disrupt the message loop already running. (Currently, it drops into the debugger so you can correct the error.)

The message loop is terminated when `Quit()` is called, or (potentially) when a `B_QUIT_REQUESTED` message is received. This also kills the thread and deletes the BLooper object.

See also: the BLooper constructor, the BApplication class, `Quit()`

### SetCommonFilterList(), CommonFilterList(), AddCommonFilter(), RemoveCommonFilter()

          virtual void SetCommonFilterList(BList *list)

          BList *CommonFilterList(void) const

          virtual void AddCommonFilter(BMessageFilter *filter)

          virtual void RemoveCommonFilter(BMessageFilter *filter)

These functions manage a list of filters that can apply to any message the BLooper receives, regardless of its target BHandler. They complement a similar set of functions defined in the BHandler class. When a filter is associated with a BHandler, it applies only to messages targeted to that BHandler. When it's associated with a BLooper as a common filter, it applies to all messages that the BLooper dispatches, regardless of the target.

In addition to the list of common filters, a BLooper can maintain a filter list in its role as a BHandler. These filters apply only if the BLooper is the target of the message (see `SetFilterList()` in the BHandler class.)

`SetCommonFilterList()` assigns the BLooper a new list of common filters; the list must contain pointers to instances of the BMessageFilter class or instances of classes that derive from BMessageFilter. The new list replaces any list of common filters previously assigned. All objects in the previous list are deleted, as is the BList itself. If list is `NULL`, the current list is removed without a replacement. `CommonFilterList()` returns the current list of common filters.

`AddCommonFilter()` adds a filter to the end of the list of common filters. It creates the BList object if it doesn't already exist. By default, BLoopers don't keep a BList of common filters until one is assigned or `AddCommonFilter()` is called for the first time. `RemoveCommonFilter()` removes a filter from the list without freeing it. It returns `true` if successful, and `false` if it can't find the specified filter in the list (or the list doesn't exist). It leaves the BList in place even after removing the last filter.

For `SetCommonFilterList()`, `AddCommonFilter()`, and `RemoveCommonFilter()` to work, the BLooper must be locked.

See also: `BHandler::SetFilterList()`, `Lock()`, the BMessageFilter class

### SetPreferredHandler(), PreferredHandler()

          void SetPreferredHandler(BHandler *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()

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

Copyright © 1998 Be, Inc. All rights reserved.

Last modified March 26, 1998.