/** Copyright 2008-2019 Haiku, Inc. All rights reserved.* Distributed under the terms of the MIT License.** Authors:* Niels Sascha Reedijk, niels.reedijk@gmail.com* John Scipione, jscipione@gmail.com* Adrien Destugues, pulkomandy@pulkomandy.tk** Corresponds to:* headers/os/app/Looper.h hrev52501* src/kits/app/Looper.cpp hrev52501*//*!\file Looper.h\ingroup app\ingroup libbe\brief Provides the BLooper class.*//*!\def B_LOOPER_PORT_DEFAULT_CAPACITY\brief The default size of the port of a BLooper.\since BeOS R3*//*!\class BLooper\ingroup app\ingroup libbe\brief Receive and process messages in a separate thread.When an object of this class is created, the message loop can be startedwith Run(). This spawns the thread that receives messages and processesmessages. Messages are actually passed on to \link BHandler handlers \endlinkthat are associated with this looper. By default there is always onehandler available: the looper itself. To 'quit' a looper, you should passa \c B_QUIT_REQUESTED message using one of the message post functions. Whena looper receives such a request, it will \b delete itself. As such, loopershould <em>always be created on the heap</em> (with \c new), and never onthe stack.Posting messages can be done using the various PostMessage() methods.Whenever a message is posted, it will be added through to the messagequeue. It is possible to apply filters (see AddCommonFilter()) to filterout any messages that correspond with certain criteria. The method willcopy the contents of the message and this copy is processed, so make sureyou delete the original messages in case you create them on the heap.The handler for the message is chosen using the following criteria:-# If PostMessage() or the BMessenger is set to a specific handler, andthis handler is associated with this looper, than the message isprocessed by that handler.-# Else, the preferred handler is used. You can set this usingSetPreferredHandler().-# If there is no preferred handler, then the looper itself will processthe message.Because a looper usually is used in multiple threads, you should make sureyou Lock() and Unlock() it during most operations. Locking calls can berecursive (so multiple locks can come from a single thread), but make sureyou pair every Lock() with an Unlock() call. Failing to do so willinevitably cause a deadlock.Because a looper provides a separate thread, and the inherited handler isusually a default handler, you will most often use this class bysubclassing it. For example, you are likely to subclass BWindow (which isderived from BLooper) to customize your window and handle the messagessent to that window. You can override Run() in case you want to performadditional tasks before (or right after) starting the message loop. You canoverride QuitRequested() if you want to decline quitting in certaincircumstances. You can override Quit() in case you want to performadditional procedures during closing time. You can also overrideDispatchMessage() if you want to do something with all incoming messagesbefore they are dispatched to a handler.BLooper is one of the major base classes of the Haiku applicationprogrammers interface. Closely related classes are BMessage, BHandler andBMessenger. It is used in the interface kit, for example by the BWindowclass, which makes sure every window runs it its own thread.BLooper is a part of the chain in the eloquent messaging structure. For aproper understanding of all its facets, have a look at the \ref app_messaging"messaging overview".\since BeOS R3*//*!\fn BLooper::BLooper(const char* name, int32 priority, int32 portCapacity)\brief Construct a new BLooper with a \a priority and an \a capacity.The new looper is, by default, not running yet. If you have set upeverything properly, you may call Run().\attention Remember that loopers should be created on the heap, becausethey will \c delete themselves in the Quit() method.\param name The name of the looper.\param priority The priority of the message thread of this looper. Thedefault priority should be good enough for most tasks. Also, somederived versions of BLooper will use a specialized priority. So itis advised to leave this setting at the default, unless you knowwhy you would like another setting.\param portCapacity Loopers use ports to send and receive messages (seethe kernel kit). Ports have a maximum capacity; if there are so manymessages queued that the port is full, all other incoming messagesare dropped. There are situations where the size of the port shouldbe different from the default. This might be when your looperreceives a lot of messages, or if the message handling thread runsat a lower priority than normal, which would decrease the processingspeed. Finding a suitable value for these custom scenarios would bedone by testing.\see Run()\since BeOS R3*//*!\fn BLooper::~BLooper()\brief Destruct the looper.You will never delete a looper yourself. You should pass a\c B_QUIT_REQUESTED message, or if you are destroying the looper frominside its own message handling thread, you should call Quit().\see Quit()\since BeOS R3*////// Archiving //////*!\name Archiving*///! @{/*!\fn BLooper::BLooper(BMessage* data)\brief Construct a looper from an archived message.The \a data message has to be constructed by a BLooper::Archive() call.Note that the data that is restored, is merely the port capacity and thename of the looper/handler. Other data, such as filters, is not archived bythe default archiver.\warning This constructor does no type check whatsoever. Since you can passany BMessage, you should - if you are not sure about the exacttype - use the Instantiate() method, which does check the type.\see Instantiate()\see Archive()\since BeOS R3*//*!\fn BArchivable* BLooper::Instantiate(BMessage* data)\brief Static method to instantiate a looper from an archived message.\return A pointer to the instantiated looper, or \c NULL if the \a datais not a valid archived BLooper object.\see BLooper(BMessage* data)\since BeOS R3*//*!\fn status_t BLooper::Archive(BMessage* data, bool deep) const\brief Archive a looper to a messageCurrently, only the name and the port capacity are archived. Any otherdata, such as the filters, is not stored.\param data The message to archive the object in.\param deep This parameter is ignored, as BLooper does not have children.\retval B_OK Archiving succeeded.\retval B_BAD_VALUE The \a data parameter is not a valid message.\see BLooper::Instantiate(BMessage* data)\since BeOS R3*///! @}/*!\name Message Mechanics*///! @{/*!\fn status_t BLooper::PostMessage(uint32 command)\brief Post a message with the \a command as \c what identifier to thislooper.Posting a message puts it in the message queue. The message passes throughthe default handler chain.\param command The \c what identifier of the message to be sent.\return A status code.\retval B_OK The operation succeeded, and the message is sent to the port.\retval B_ERROR There was a general operation error.\retval B_BAD_VALUE This looper is not yet running and therefore cannotreceive messages.\see PostMessage(BMessage *) if you want to send a message with datamembers.\see PostMessage(uint32, BHandler *, BHandler *) if you want to send amessage to a specific handler, and request a reply.\see PostMessage(BMessage *, BHandler *, BHandler *) for the same thing,but with a complete message.\since BeOS R5*//*!\fn status_t BLooper::PostMessage(BMessage* message)\brief Post a \a message to this looper.Posting a message puts it in the message queue. The message passes throughthe default handler chain.The \a message is copied, and as such, you should make sure you will notleak it. The best way to send messages is like this:\codeBMessage message;message.what = B_DO_SOMETHING;message.AddString("some_data", "This is data")aLooper->PostMessage(&message);\endcode\param message The message you would like to pass to this method.\return A status code.\retval B_OK The operation succeeded, and the message is sent to the port.\retval B_ERROR There was a general operation error.\retval B_BAD_VALUE This looper is not yet running and therefore cannotreceive messages.\see PostMessage(uint32) if you want to send a message without datamembers.\see PostMessage(uint32, BHandler *, BHandler *) if you want to send amessage to a specific handler, and request a reply.\see PostMessage(BMessage *, BHandler *, BHandler *) for the same thing,but with a complete message.\since BeOS R5*//*!\fn status_t BLooper::PostMessage(uint32 command, BHandler* handler,BHandler* replyTo)\brief Sends a message with \a command \c what identifier to the\a handler associated with this looper. A response may be sent to the\a replyTo handler asynchronously.The target \a handler should be associated with this looper. This methodbypasses the default message queue.\param command The value you want as the message's \c what identifier.\param handler The handler you would like to pass this message to.\param replyTo If you would like to request a reply, pass the handler towhich this reply should be directed to. If you pass \c NULL, youwill not receive a reply.\return A status code.\retval B_OK The operation succeeded, and the message is sent to the port.\retval B_ERROR There was a general operation error.\retval B_BAD_VALUE This looper is not yet running and therefore cannotreceive messages.\retval B_MISMATCHED_VALUES The \a handler is not associated with thislooper.\see PostMessage(uint32) if you want to send a message without datamembers.\see PostMessage(BMessage *) if you want to send a message with datamembers.\see PostMessage(BMessage *, BHandler *, BHandler *) if you want to send amessage to a specific handler, and request a reply.\since BeOS R5*//*!\fn status_t BLooper::PostMessage(BMessage* message, BHandler* handler,BHandler* replyTo)\brief Send a \a message to the \a handler associated with this looper.A response may be sent to the \a replyTo handler asynchronously.The target \a handler should be associated with this looper. This methodbypasses the default message queue.The \a message is copied, and as such, you should make sure you will notleak it. The best way to send messages is like this:\codeBMessage message;message.what = B_DO_SOMETHING;message.AddString("some_data", "This is data")aLooper->PostMessage(&message, aHandler);\endcode\param message The message you want to pass.\param handler The handler you would like to pass this message to.\param replyTo If you would like to request a reply, pass the handler towhich this reply should be directed to. If you pass \c NULL, youwill not receive a reply.\return A status code.\retval B_OK The operation succeeded, and the message is sent to the port.\retval B_ERROR There was a general operation error.\retval B_BAD_VALUE This looper is not yet running and therefore cannotreceive messages.\retval B_MISMATCHED_VALUES The \a handler is not associated with thislooper.\see PostMessage(uint32) if you want to send a message without datamembers.\see PostMessage(BMessage *) if you want to send a message with datamembers.\see PostMessage(uint32, BHandler *, BHandler *) if you want to send amessage without data to a specific handler, and request a reply.\since BeOS R5*///! @}/*!\name Message Processing*///! @{/*!\fn void BLooper::DispatchMessage(BMessage *message, BHandler *handler)\brief Dispatch a message to a handler. Override if there are messages thatyou want to catch before they are sent to the handlers.This method is called by the message looping thread to dispatch a messageto \a handler. If you implement the BLooper class and your looper receivesmessages that absolutely have to be processed by the looper instead of anyof the handlers, override this method. For example, the defaultimplementation catches B_QUIT_REQUESTED messages before they are sent tothe handlers, so that the looper will quit at those messages.You are discouraged from using this method to filter out any messages youdo not want to process. For this, there is a more generic method usingthe BMessageFilter class. If you want to skip messages with certainpatterns, have a look at the AddCommonFilter() and SetCommonFilterList()methods.If you do override this method, please remember to call theDispatchMessage() method of the parent class.\since BeOS R3*//*!\fn void BLooper::MessageReceived(BMessage* message)\brief Process a message received by the internal handler of this looper.Reimplemented from BHandler::MessageReceived();\since BeOS R5*//*!\fn BMessage* BLooper::CurrentMessage() const\brief Retrieve the current message.\attention Only call this method from within the thread that processes themessages. It contains a pointer to the message that is currentlybeing handled. Due to the multithreaded nature of the operatingsystem, this method will not safely let you read the messagethat is being processed by this handler from outside the contextof the processing. If you do want to use a message outside ofthe processing thread, have a look at DetachCurrentMessage() tosafely retrieve a message.\return A pointer to the message that is currently being processed. Notethat calling it from outside the thread that processes the message,could give you a \c NULL pointer or an invalid pointer.\since BeOS R5*//*!\fn BMessage* BLooper::DetachCurrentMessage()\brief Get ownership of the message currently being processed.Retrieve the current message and gain ownership of it. This means that themessage will not be deleted as soon as the looper is done processing it.You can then use it for different purposes.\attention Only call this method from within the thread that processes themessages. Due to the multithreaded nature of the operatingsystem, calling it from another thread is very likely to giveyou an invalid or a \c NULL pointer.\since BeOS R5*//*!\fn void BLooper::DispatchExternalMessage(BMessage* message,BHandler* handler, bool& _detached)\brief Internal method to support single-threaded GUI toolkits\since Haiku R1*//*!\fn BMessageQueue* BLooper::MessageQueue() const\brief Get a pointer to the internal message queue of this looper.You can use this pointer to manipulate the message queue. Note that themessage that is being processed is already detached from this queue.\return A pointer to the internal message queue.\since BeOS R5*//*!\fn bool BLooper::IsMessageWaiting() const\brief Check if there is a message waiting.\return \c true if there are still messages to be processed,\c false if there is no message waiting.*///! @}/*!\name Handler Management*///! @{/*!\fn void BLooper::AddHandler(BHandler* handler)\brief Associate a \a handler to this looper.The \a handler will be associated to this looper. By default, the handlerin this looper will be chained to the supplied \a handler.\param handler The handler to associate with this looper. If the handleris already associated to another looper, the operation will failsilently. Check beforehand if you cannot be sure that the\a handler is unassociated.\see RemoveHandler()\since BeOS R3*//*!\fn bool BLooper::RemoveHandler(BHandler* handler)\brief Disassociate a \a handler from this looper.If the handler is disassociated, it can be reassociated to another looper.\return \c true if the \a handler has been removed from this looper,\c false The \a handler was invalid or the handler was notassociated to this looper.\see AddHandler()\since BeOS R3*//*!\fn int32 BLooper::CountHandlers() const\brief Get the number of handlers associated with this looper.\see HandlerAt()\see IndexOf()\since BeOS R3*//*!\fn BHandler* BLooper::HandlerAt(int32 index) const\brief Get the handler at an \a index of the list of associated handlers.\return A pointer to the handler at that \a index, or \c NULL if the\a index is out of range.\see CountHandlers()\see IndexOf()\since BeOS R3*//*!\fn int32 BLooper::IndexOf(BHandler* handler) const\brief Get the index of the \a handler that is in the associated handlerlist.\return The index of the handler in the list if the \a handler is in thelist, else this method will return -1.\since BeOS R3*//*!\fn BHandler* BLooper::PreferredHandler() const\brief Get the preferred handler.\return A pointer to the preferred handler, or \c NULL if none is set.\see SetPreferredHandler()\since BeOS R3*//*!\fn void BLooper::SetPreferredHandler(BHandler* handler)\brief Set a preferred handler.If messages are posted to this looper using one of the PostMessage()methods without a specific BHandler argument, the messages will be handledby the looper itself (since a looper is a subclass of BHandler, this isperfectly possible). If you want to override that behavior, you should seta preferred handler. This handler will be called if incoming messages donot ask to be directly passed on to a specific handler.\param handler The preferred handler you want undesignated messages to behandled by. If you want to unset the preferred handler, pass\c NULL. If the supplied \a handler is not associated with thislooper, this call will fail silently and the current preferredhandler will be unset.\see PreferredHandler()\since BeOS R3*///! @}/*!\name Loop Control*///! @{/*!\fn thread_id BLooper::Run()\brief Start the event loop.After the looper has been constructed, it needs to be started using thismethod. A thread will be spawned, which will receive messages.Make sure the looper is not yet running before you call this method.\return A (positive) thread id if spawning the thread succeeded, or anerror code.\since BeOS R3*//*!\fn thread_id BLooper::Loop()\brief Run the event loop in the current thread.This method runs the event loop in an already existing thread. It blocksuntil the looper stops looping. This canbe used to turn an existing thread into a BLooper.Make sure the looper is not yet running before you call this method.\since Haiku R1*//*!\fn void BLooper::Quit()\brief Hook method that is called after a \c B_QUIT_REQUESTED message.If you want to quit and delete the looper, you should post a\c B_QUIT_REQUESTED message. This will first call the hook methodQuitRequested(), which can be overridden in child classes in case thereare conditions that would prevent the looper to be quit. If you reallyknow what you are doing, and you definitely want to quit this looper,you may call this method, but only after performing a Lock() operation.Override this method if your subclass needs to perform specific clean-uptasks. Remember to call the base class implementation when you're done.\attention You will not have to delete the looper object, if a looper quitsit will delete itself.\since BeOS R3*//*!\fn bool BLooper::QuitRequested()\brief Hook method that is called during a \c B_QUIT_REQUESTED message.This hook function is called by the looper thread when a\c B_QUIT_REQUESTED is received. The default implementation always acceptsthe message, but if your subclass needs a special condition to be metbefore actually accepting a quit message, you can test for that conditionin this hook method. A good example is a window (which is a derivative ofBLooper), which contains a modified document. The condition may be that amodal dialog requesting a path of action is closed.\return \c true if the looper can be quit and destroyed,\c false if this method does not accept the quit messageand continue processing messages.\since BeOS R3*//*!\fn bool BLooper::Lock()\brief Lock the looper.For most operations involving the internal data of the looper, you need tohold the lock. Each looper implements a global lock, which you can use toperform operations on internal data in a thread-safe manner.Do not forget to pair each Lock() request with an Unlock() request. Lock()requests can be stacked, which means that recursively locking a looper froma thread that actually holds the lock, will not cause a deadlock. SeeBLocker for more information on locking internals.\return \c true if the locking request succeeded,\c false if the locking request could not be completed. There are avariety of reasons for this to happen, for example when thelooper is destroyed.\see Unlock()\see LockWithTimeout()\see IsLocked()\since BeOS R5*//*!\fn void BLooper::Unlock()\brief Unlock a locked looper.Use this method paired with Lock() calls, to release a lock. Make sure thatthis method is only called on a locked looper.\see Lock()\see LockWithTimeout()\see IsLocked()\since BeOS R5*//*!\fn bool BLooper::IsLocked() const\brief Check if a looper is locked.\return \c true if the looper is locked,\c false if the looper is not locked, or the looper has beendeleted.\see Lock()\see Unlock()\see LockWithTimeout()\since BeOS R5*//*!\fn status_t BLooper::LockWithTimeout(bigtime_t timeout)\brief Lock a looper with a \a timeout.This method locks the looper like Lock(), but if the locking request doesnot succeed within the provided \a timeout, the method will return.\param timeout The maximum time to wait for the lock request to succeed.\return A status code.\retval B_OK The lock is acquired.\retval B_BAD_VALUE The looper has been destroyed.\retval "other errors" There was an error acquiring the lock.\see Lock()\see Unlock()\see IsLocked()\since BeOS R5*//*!\fn thread_id BLooper::Thread() const\brief Return the thread id of the internal message looper thread.If the looper is not yet running, this method will return 0.\see Run()\since BeOS R3*//*!\fn team_id BLooper::Team() const\brief Return the team id in which this looper exists.\since BeOS R3*//*!\fn BLooper* BLooper::LooperForThread(thread_id thread)\brief Static method to retrieve a BLooper for a specified \a thread.\since BeOS R3*///! @}/*!\name Loop DebuggingThese methods may aid you in debugging problems when they occur, but do notuse these in actual production code. These methods are unreliable becausethey are not thread-safe, and as such are only useful in specific debuggingsituations. Handle with care.*///! @{/*!\fn thread_id BLooper::LockingThread() const\brief Return the thread id of the thread that currently holds the lock.\since BeOS R3*//*!\fn int32 BLooper::CountLocks() const\brief Return the number of recursive locks that are currently being heldon this looper.\since BeOS R3*//*!\fn int32 BLooper::CountLockRequests() const\brief Return the number of pending locks.\since BeOS R3*//*!\fn sem_id BLooper::Sem() const\brief Return the id of the semaphore that is used to lock this looper.\since BeOS R3*///! @}/*!\name Scripting*///! @{/*!\fn BHandler* BLooper::ResolveSpecifier(BMessage* message, int32 index,BMessage* specifier, int32 what, const char* property)\brief Determine the proper handler for a scripting message.\copydetails BHandler::ResolveSpecifier()*//*!\fn status_t BLooper::GetSupportedSuites(BMessage* data)\brief Reports the suites of messages and specifiers that derived classesunderstand.\copydetails BHandler::GetSupportedSuites()*///! @}/*!\name Looper Message FiltersNote that filters added with these methods will be applied to allassociated handlers. Have a look at the filtering methods of the BHandlerclass to see how filters can be applied to the inherited handler of thislooper specifically.*///! @{/*!\fn void BLooper::AddCommonFilter(BMessageFilter* filter)\brief Add a common filter to the list of filters that are applied to allincoming messages.Filters can only be applied once, so they cannot be shared between loopers,a handler and a looper or between two handlers.The \a filter is not copied; rather a pointer is stored. Keep the \a filteralive as long as it is used by a looper.\see RemoveCommonFilter()\see SetCommonFilterList()\see CommonFilterList()\since BeOS R3*//*!\fn bool BLooper::RemoveCommonFilter(BMessageFilter* filter)\brief Remove a \a filter from the common message filter list.Note that this will not free the memory used by the \a filter, so youshould dispose of it yourself.\see AddCommonFilter()\see SetCommonFilterList()\see CommonFilterList()\since BeOS R3*//*!\fn void BLooper::SetCommonFilterList(BList* filters)\brief Set a new list of \a filters that need to be applied to allincoming messages.You are responsible for validating that all the items in the list of\a filters are actual filters. The old list is discarded; all the filtersare \b destroyed.Note that filters can only be applied to one looper or handler. If anyof the filters is already associated with another one, this call will fail.\see AddCommonFilter()\see RemoveCommonFilter()\see CommonFilterList()\since BeOS R3*//*!\fn BList* BLooper::CommonFilterList() const\brief Return a list of filters applied to all incoming messages.\return A pointer to the internal filter list, or \c NULL if such a listhas not yet been created. Please note that you should use theinternal list management functions to manipulate the internalfilter list, in order to maintain internal consistency.\see AddCommonFilter()\see RemoveCommonFilter()\see SetCommonFilterList()\since BeOS R3*///! @}/*!\fn status_t BLooper::Perform(perform_code d, void* arg)\brief Internal method.\since Haiku R1*//*!\fn BMessage* BLooper::MessageFromPort(bigtime_t timeout)\brief Hook method to retrieve a message from the looper's port.The default implementation is called by the internal message looping threadand retrieves the next message from the port that belongs to this looper.If you use a looper in a context where it might receive messages from othersources, you can override this method in order to insert these methods intothe message processing. Note that any messages that are returned by thismethod will be deleted by this looper, so make sure you have ownership ofthe message. If you override this method, remember to call the baseimplementation every now and then, in order to retrieve the messagesarriving at the default port.\since Haiku R1*/