#include <Looper.h>
#include <new>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <Autolock.h>
#include <Message.h>
#include <MessageFilter.h>
#include <MessageQueue.h>
#include <Messenger.h>
#include <PropertyInfo.h>
#include <AppMisc.h>
#include <AutoLocker.h>
#include <DirectMessageTarget.h>
#include <LooperList.h>
#include <MessagePrivate.h>
#include <TokenSpace.h>
#define DBG(x) ;
#define PRINT(x) DBG({ printf("[%6" B_PRId32 "] ", find_thread(NULL)); printf x; })
#define FILTER_LIST_BLOCK_SIZE 5
#define DATA_BLOCK_SIZE 5
using BPrivate::gDefaultTokens;
using BPrivate::gLooperList;
using BPrivate::BLooperList;
port_id _get_looper_port_(const BLooper* looper);
enum {
BLOOPER_PROCESS_INTERNALLY = 0,
BLOOPER_HANDLER_BY_INDEX
};
static property_info sLooperPropInfo[] = {
{
"Handler",
{},
{B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER},
NULL, BLOOPER_HANDLER_BY_INDEX,
{},
{},
{}
},
{
"Handlers",
{B_GET_PROPERTY},
{B_DIRECT_SPECIFIER},
NULL, BLOOPER_PROCESS_INTERNALLY,
{B_MESSENGER_TYPE},
{},
{}
},
{
"Handler",
{B_COUNT_PROPERTIES},
{B_DIRECT_SPECIFIER},
NULL, BLOOPER_PROCESS_INTERNALLY,
{B_INT32_TYPE},
{},
{}
},
{ 0 }
};
struct _loop_data_ {
BLooper* looper;
thread_id thread;
};
BLooper::BLooper(const char* name, int32 priority, int32 portCapacity)
:
BHandler(name)
{
_InitData(name, priority, -1, portCapacity);
}
BLooper::~BLooper()
{
if (fRunCalled && !fTerminating) {
debugger("You can't call delete on a BLooper object "
"once it is running.");
}
Lock();
if (fLastMessage) {
delete fLastMessage;
fLastMessage = NULL;
}
if (fMsgPort >= 0 && fOwnsPort)
close_port(fMsgPort);
fDirectTarget->Close();
BMessage* message;
while ((message = fDirectTarget->Queue()->NextMessage()) != NULL) {
delete message;
}
if (fOwnsPort) {
do {
delete ReadMessageFromPort(0);
} while (IsMessageWaiting());
delete_port(fMsgPort);
}
fDirectTarget->Release();
SetCommonFilterList(NULL);
AutoLocker<BLooperList> ListLock(gLooperList);
RemoveHandler(this);
int32 count = fHandlers.CountItems();
for (int32 i = 0; i < count; i++) {
BHandler* handler = (BHandler*)fHandlers.ItemAtFast(i);
handler->SetNextHandler(NULL);
handler->SetLooper(NULL);
}
fHandlers.MakeEmpty();
Unlock();
gLooperList.RemoveLooper(this);
delete_sem(fLockSem);
}
BLooper::BLooper(BMessage* data)
: BHandler(data)
{
int32 portCapacity;
if (data->FindInt32("_port_cap", &portCapacity) != B_OK || portCapacity < 0)
portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
int32 priority;
if (data->FindInt32("_prio", &priority) != B_OK)
priority = B_NORMAL_PRIORITY;
_InitData(Name(), priority, -1, portCapacity);
}
BArchivable*
BLooper::Instantiate(BMessage* data)
{
if (validate_instantiation(data, "BLooper"))
return new BLooper(data);
return NULL;
}
status_t
BLooper::Archive(BMessage* data, bool deep) const
{
status_t status = BHandler::Archive(data, deep);
if (status < B_OK)
return status;
port_info info;
status = get_port_info(fMsgPort, &info);
if (status == B_OK)
status = data->AddInt32("_port_cap", info.capacity);
thread_info threadInfo;
if (get_thread_info(Thread(), &threadInfo) == B_OK)
status = data->AddInt32("_prio", threadInfo.priority);
return status;
}
status_t
BLooper::PostMessage(uint32 command)
{
BMessage message(command);
return _PostMessage(&message, this, NULL);
}
status_t
BLooper::PostMessage(BMessage* message)
{
return _PostMessage(message, this, NULL);
}
status_t
BLooper::PostMessage(uint32 command, BHandler* handler, BHandler* replyTo)
{
BMessage message(command);
return _PostMessage(&message, handler, replyTo);
}
status_t
BLooper::PostMessage(BMessage* message, BHandler* handler, BHandler* replyTo)
{
return _PostMessage(message, handler, replyTo);
}
void
BLooper::DispatchMessage(BMessage* message, BHandler* handler)
{
PRINT(("BLooper::DispatchMessage(%.4s)\n", (char*)&message->what));
switch (message->what) {
case _QUIT_:
fTerminating = true;
break;
case B_QUIT_REQUESTED:
if (handler == this) {
_QuitRequested(message);
break;
}
default:
handler->MessageReceived(message);
break;
}
PRINT(("BLooper::DispatchMessage() done\n"));
}
void
BLooper::MessageReceived(BMessage* message)
{
if (!message->HasSpecifiers()) {
BHandler::MessageReceived(message);
return;
}
BMessage replyMsg(B_REPLY);
status_t err = B_BAD_SCRIPT_SYNTAX;
int32 index;
BMessage specifier;
int32 what;
const char* property;
if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
!= B_OK) {
return BHandler::MessageReceived(message);
}
BPropertyInfo propertyInfo(sLooperPropInfo);
switch (propertyInfo.FindMatch(message, index, &specifier, what,
property)) {
case 1:
if (message->what == B_GET_PROPERTY) {
int32 count = CountHandlers();
err = B_OK;
for (int32 i = 0; err == B_OK && i < count; i++) {
BMessenger messenger(HandlerAt(i));
err = replyMsg.AddMessenger("result", messenger);
}
}
break;
case 2:
if (message->what == B_COUNT_PROPERTIES)
err = replyMsg.AddInt32("result", CountHandlers());
break;
default:
return BHandler::MessageReceived(message);
}
if (err != B_OK) {
replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
if (err == B_BAD_SCRIPT_SYNTAX)
replyMsg.AddString("message", "Didn't understand the specifier(s)");
else
replyMsg.AddString("message", strerror(err));
}
replyMsg.AddInt32("error", err);
message->SendReply(&replyMsg);
}
BMessage*
BLooper::CurrentMessage() const
{
return fLastMessage;
}
BMessage*
BLooper::DetachCurrentMessage()
{
BMessage* message = fLastMessage;
fLastMessage = NULL;
return message;
}
void
BLooper::DispatchExternalMessage(BMessage* message, BHandler* handler,
bool& _detached)
{
AssertLocked();
BMessage* previousMessage = fLastMessage;
fLastMessage = message;
DispatchMessage(message, handler);
_detached = fLastMessage == NULL;
fLastMessage = previousMessage;
}
BMessageQueue*
BLooper::MessageQueue() const
{
return fDirectTarget->Queue();
}
bool
BLooper::IsMessageWaiting() const
{
AssertLocked();
if (!fDirectTarget->Queue()->IsEmpty())
return true;
int32 count;
do {
count = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, 0);
} while (count == B_INTERRUPTED);
return count > 0;
}
void
BLooper::AddHandler(BHandler* handler)
{
if (handler == NULL)
return;
AssertLocked();
if (handler->Looper() == NULL) {
fHandlers.AddItem(handler);
handler->SetLooper(this);
if (handler != this)
handler->SetNextHandler(this);
}
}
bool
BLooper::RemoveHandler(BHandler* handler)
{
if (handler == NULL)
return false;
AssertLocked();
if (handler->Looper() == this && fHandlers.RemoveItem(handler)) {
if (handler == fPreferred)
fPreferred = NULL;
handler->SetNextHandler(NULL);
handler->SetLooper(NULL);
return true;
}
return false;
}
int32
BLooper::CountHandlers() const
{
AssertLocked();
return fHandlers.CountItems();
}
BHandler*
BLooper::HandlerAt(int32 index) const
{
AssertLocked();
return (BHandler*)fHandlers.ItemAt(index);
}
int32
BLooper::IndexOf(BHandler* handler) const
{
AssertLocked();
return fHandlers.IndexOf(handler);
}
BHandler*
BLooper::PreferredHandler() const
{
return fPreferred;
}
void
BLooper::SetPreferredHandler(BHandler* handler)
{
if (handler && handler->Looper() == this && IndexOf(handler) >= 0) {
fPreferred = handler;
} else {
fPreferred = NULL;
}
}
thread_id
BLooper::Run()
{
AssertLocked();
if (fRunCalled) {
debugger("can't call BLooper::Run twice!");
return fThread;
}
fThread = spawn_thread(_task0_, Name(), fInitPriority, this);
if (fThread < B_OK)
return fThread;
if (fMsgPort < B_OK)
return fMsgPort;
fRunCalled = true;
Unlock();
status_t err = resume_thread(fThread);
if (err < B_OK)
return err;
return fThread;
}
void
BLooper::Loop()
{
AssertLocked();
if (fRunCalled) {
debugger("can't call BLooper::Loop twice!");
return;
}
fThread = find_thread(NULL);
fRunCalled = true;
task_looper();
}
void
BLooper::Quit()
{
PRINT(("BLooper::Quit()\n"));
if (!IsLocked()) {
printf("ERROR - you must Lock a looper before calling Quit(), "
"team=%" B_PRId32 ", looper=%s\n", Team(),
Name() ? Name() : "unnamed");
}
if (!Lock()) {
return;
}
PRINT((" is locked\n"));
if (!fRunCalled) {
PRINT((" Run() has not been called yet\n"));
fTerminating = true;
delete this;
} else if (find_thread(NULL) == fThread) {
PRINT((" We are the looper thread\n"));
fTerminating = true;
delete this;
exit_thread(0);
} else {
PRINT((" Run() has already been called and we are not the looper thread\n"));
thread_id thread = Thread();
UnlockFully();
PostMessage(_QUIT_);
status_t status;
while (wait_for_thread(thread, &status) == B_INTERRUPTED)
;
}
PRINT(("BLooper::Quit() done\n"));
}
bool
BLooper::QuitRequested()
{
return true;
}
bool
BLooper::Lock()
{
return _Lock(this, -1, B_INFINITE_TIMEOUT) == B_OK;
}
void
BLooper::Unlock()
{
PRINT(("BLooper::Unlock()\n"));
AssertLocked();
--fOwnerCount;
PRINT((" fOwnerCount now: %ld\n", fOwnerCount));
if (fOwnerCount == 0) {
fOwner = -1;
fCachedStack = 0;
#if DEBUG < 1
int32 atomicCount = atomic_add(&fAtomicCount, -1);
PRINT((" fAtomicCount now: %ld\n", fAtomicCount));
if (atomicCount > 1)
#endif
release_sem(fLockSem);
}
PRINT(("BLooper::Unlock() done\n"));
}
bool
BLooper::IsLocked() const
{
if (!gLooperList.IsLooperValid(this)) {
return false;
}
uint32 stack;
return ((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
|| find_thread(NULL) == fOwner;
}
status_t
BLooper::LockWithTimeout(bigtime_t timeout)
{
return _Lock(this, -1, timeout);
}
thread_id
BLooper::Thread() const
{
return fThread;
}
team_id
BLooper::Team() const
{
return BPrivate::current_team();
}
BLooper*
BLooper::LooperForThread(thread_id thread)
{
return gLooperList.LooperForThread(thread);
}
thread_id
BLooper::LockingThread() const
{
return fOwner;
}
int32
BLooper::CountLocks() const
{
return fOwnerCount;
}
int32
BLooper::CountLockRequests() const
{
return fAtomicCount;
}
sem_id
BLooper::Sem() const
{
return fLockSem;
}
BHandler*
BLooper::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
int32 what, const char* property)
{
BPropertyInfo propertyInfo(sLooperPropInfo);
uint32 data;
status_t err = B_OK;
const char* errMsg = "";
if (propertyInfo.FindMatch(message, index, specifier, what, property, &data)
>= 0) {
switch (data) {
case BLOOPER_PROCESS_INTERNALLY:
return this;
case BLOOPER_HANDLER_BY_INDEX:
{
int32 index = specifier->FindInt32("index");
if (what == B_REVERSE_INDEX_SPECIFIER) {
index = CountHandlers() - index;
}
BHandler* target = HandlerAt(index);
if (target) {
message->PopSpecifier();
return target;
} else {
err = B_BAD_INDEX;
errMsg = "handler index out of range";
}
break;
}
default:
err = B_BAD_SCRIPT_SYNTAX;
errMsg = "Didn't understand the specifier(s)";
}
} else {
return BHandler::ResolveSpecifier(message, index, specifier, what,
property);
}
BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
reply.AddInt32("error", err);
reply.AddString("message", errMsg);
message->SendReply(&reply);
return NULL;
}
status_t
BLooper::GetSupportedSuites(BMessage* data)
{
if (data == NULL)
return B_BAD_VALUE;
status_t status = data->AddString("suites", "suite/vnd.Be-looper");
if (status == B_OK) {
BPropertyInfo PropertyInfo(sLooperPropInfo);
status = data->AddFlat("messages", &PropertyInfo);
if (status == B_OK)
status = BHandler::GetSupportedSuites(data);
}
return status;
}
void
BLooper::AddCommonFilter(BMessageFilter* filter)
{
if (filter == NULL)
return;
AssertLocked();
if (filter->Looper()) {
debugger("A MessageFilter can only be used once.");
return;
}
if (fCommonFilters == NULL)
fCommonFilters = new BList(FILTER_LIST_BLOCK_SIZE);
filter->SetLooper(this);
fCommonFilters->AddItem(filter);
}
bool
BLooper::RemoveCommonFilter(BMessageFilter* filter)
{
AssertLocked();
if (fCommonFilters == NULL)
return false;
bool result = fCommonFilters->RemoveItem(filter);
if (result)
filter->SetLooper(NULL);
return result;
}
void
BLooper::SetCommonFilterList(BList* filters)
{
AssertLocked();
BMessageFilter* filter;
if (filters) {
for (int32 i = 0; i < filters->CountItems(); ++i) {
filter = (BMessageFilter*)filters->ItemAt(i);
if (filter->Looper()) {
debugger("A MessageFilter can only be used once.");
return;
}
}
}
if (fCommonFilters) {
for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
delete (BMessageFilter*)fCommonFilters->ItemAt(i);
}
delete fCommonFilters;
fCommonFilters = NULL;
}
fCommonFilters = filters;
if (fCommonFilters) {
for (int32 i = 0; i < fCommonFilters->CountItems(); ++i) {
filter = (BMessageFilter*)fCommonFilters->ItemAt(i);
filter->SetLooper(this);
}
}
}
BList*
BLooper::CommonFilterList() const
{
return fCommonFilters;
}
status_t
BLooper::Perform(perform_code d, void* arg)
{
return BHandler::Perform(d, arg);
}
BMessage*
BLooper::MessageFromPort(bigtime_t timeout)
{
return ReadMessageFromPort(timeout);
}
void BLooper::_ReservedLooper1() {}
void BLooper::_ReservedLooper2() {}
void BLooper::_ReservedLooper3() {}
void BLooper::_ReservedLooper4() {}
void BLooper::_ReservedLooper5() {}
void BLooper::_ReservedLooper6() {}
#ifdef __HAIKU_BEOS_COMPATIBLE
BLooper::BLooper(const BLooper& other)
{
}
BLooper&
BLooper::operator=(const BLooper& other)
{
return *this;
}
#endif
BLooper::BLooper(int32 priority, port_id port, const char* name)
{
_InitData(name, priority, port, B_LOOPER_PORT_DEFAULT_CAPACITY);
}
status_t
BLooper::_PostMessage(BMessage* msg, BHandler* handler, BHandler* replyTo)
{
status_t status;
BMessenger messenger(handler, this, &status);
if (status == B_OK)
return messenger.SendMessage(msg, replyTo, 0);
return status;
}
status_t
BLooper::_Lock(BLooper* looper, port_id port, bigtime_t timeout)
{
PRINT(("BLooper::_Lock(%p, %lx)\n", looper, port));
if (looper == NULL && port < 0) {
PRINT(("BLooper::_Lock() done 1\n"));
return B_BAD_VALUE;
}
thread_id currentThread = find_thread(NULL);
int32 oldCount;
sem_id sem;
{
AutoLocker<BLooperList> ListLock(gLooperList);
if (!ListLock.IsLocked())
return B_BAD_VALUE;
if (looper == NULL) {
looper = gLooperList.LooperForPort(port);
if (looper == NULL) {
PRINT(("BLooper::_Lock() done 3\n"));
return B_BAD_VALUE;
}
} else if (!gLooperList.IsLooperValid(looper)) {
PRINT(("BLooper::_Lock() done 4\n"));
return B_BAD_VALUE;
}
if (currentThread == looper->fOwner) {
++looper->fOwnerCount;
PRINT(("BLooper::_Lock() done 5: fOwnerCount: %ld\n", looper->fOwnerCount));
return B_OK;
}
sem = looper->fLockSem;
if (sem < 0) {
PRINT(("BLooper::_Lock() done 6\n"));
return B_BAD_VALUE;
}
oldCount = atomic_add(&looper->fAtomicCount, 1);
}
return _LockComplete(looper, oldCount, currentThread, sem, timeout);
}
status_t
BLooper::_LockComplete(BLooper* looper, int32 oldCount, thread_id thread,
sem_id sem, bigtime_t timeout)
{
status_t err = B_OK;
#if DEBUG < 1
if (oldCount > 0) {
#endif
do {
err = acquire_sem_etc(sem, 1, B_RELATIVE_TIMEOUT, timeout);
} while (err == B_INTERRUPTED);
#if DEBUG < 1
}
#endif
if (err == B_OK) {
looper->fOwner = thread;
looper->fCachedStack = (addr_t)&err & ~(B_PAGE_SIZE - 1);
looper->fOwnerCount = 1;
}
PRINT(("BLooper::_LockComplete() done: %lx\n", err));
return err;
}
void
BLooper::_InitData(const char* name, int32 priority, port_id port,
int32 portCapacity)
{
fOwner = B_ERROR;
fCachedStack = 0;
fRunCalled = false;
fDirectTarget = new (std::nothrow) BPrivate::BDirectMessageTarget();
fCommonFilters = NULL;
fLastMessage = NULL;
fPreferred = NULL;
fThread = B_ERROR;
fTerminating = false;
fOwnsPort = true;
fMsgPort = -1;
fAtomicCount = 0;
if (name == NULL)
name = "anonymous looper";
#if DEBUG
fLockSem = create_sem(1, name);
#else
fLockSem = create_sem(0, name);
#endif
if (portCapacity <= 0)
portCapacity = B_LOOPER_PORT_DEFAULT_CAPACITY;
if (port >= 0)
fMsgPort = port;
else
fMsgPort = create_port(portCapacity, name);
fInitPriority = priority;
gLooperList.AddLooper(this);
AddHandler(this);
}
void
BLooper::AddMessage(BMessage* message)
{
_AddMessagePriv(message);
if (find_thread(NULL) != Thread()
&& fDirectTarget->Queue()->IsNextMessage(message)
&& port_count(fMsgPort) <= 0) {
write_port_etc(fMsgPort, 0, NULL, 0, B_RELATIVE_TIMEOUT, 0);
}
}
void
BLooper::_AddMessagePriv(BMessage* message)
{
fDirectTarget->Queue()->AddMessage(message);
}
status_t
BLooper::_task0_(void* arg)
{
BLooper* looper = (BLooper*)arg;
PRINT(("LOOPER: _task0_()\n"));
if (looper->Lock()) {
PRINT(("LOOPER: looper locked\n"));
looper->task_looper();
delete looper;
}
PRINT(("LOOPER: _task0_() done: thread %ld\n", find_thread(NULL)));
return B_OK;
}
void*
BLooper::ReadRawFromPort(int32* msgCode, bigtime_t timeout)
{
PRINT(("BLooper::ReadRawFromPort()\n"));
uint8* buffer = NULL;
ssize_t bufferSize;
do {
bufferSize = port_buffer_size_etc(fMsgPort, B_RELATIVE_TIMEOUT, timeout);
} while (bufferSize == B_INTERRUPTED);
if (bufferSize < B_OK) {
PRINT(("BLooper::ReadRawFromPort(): failed: %ld\n", bufferSize));
return NULL;
}
if (bufferSize > 0)
buffer = (uint8*)malloc(bufferSize);
PRINT(("read_port()...\n"));
bufferSize = read_port_etc(fMsgPort, msgCode, buffer, bufferSize,
B_RELATIVE_TIMEOUT, 0);
if (bufferSize < B_OK) {
free(buffer);
return NULL;
}
PRINT(("BLooper::ReadRawFromPort() read: %.4s, %p (%d bytes)\n",
(char*)msgCode, buffer, bufferSize));
return buffer;
}
BMessage*
BLooper::ReadMessageFromPort(bigtime_t timeout)
{
PRINT(("BLooper::ReadMessageFromPort()\n"));
int32 msgCode;
BMessage* message = NULL;
void* buffer = ReadRawFromPort(&msgCode, timeout);
if (buffer == NULL)
return NULL;
message = ConvertToMessage(buffer, msgCode);
free(buffer);
PRINT(("BLooper::ReadMessageFromPort() done: %p\n", message));
return message;
}
BMessage*
BLooper::ConvertToMessage(void* buffer, int32 code)
{
PRINT(("BLooper::ConvertToMessage()\n"));
if (buffer == NULL)
return NULL;
BMessage* message = new BMessage();
if (message->Unflatten((const char*)buffer) != B_OK) {
PRINT(("BLooper::ConvertToMessage(): unflattening message failed\n"));
delete message;
message = NULL;
}
PRINT(("BLooper::ConvertToMessage(): %p\n", message));
return message;
}
void
BLooper::task_looper()
{
PRINT(("BLooper::task_looper()\n"));
AssertLocked();
Unlock();
if (IsLocked())
debugger("looper must not be locked!");
while (!fTerminating) {
PRINT(("LOOPER: outer loop\n"));
PRINT(("LOOPER: MessageFromPort()...\n"));
BMessage* msg = MessageFromPort();
PRINT(("LOOPER: ...done\n"));
if (msg)
_AddMessagePriv(msg);
int32 msgCount = port_count(fMsgPort);
for (int32 i = 0; i < msgCount; ++i) {
msg = MessageFromPort(0);
if (msg)
_AddMessagePriv(msg);
}
bool dispatchNextMessage = true;
while (!fTerminating && dispatchNextMessage) {
PRINT(("LOOPER: inner loop\n"));
BMessage* message = fDirectTarget->Queue()->NextMessage();
Lock();
fLastMessage = message;
if (fLastMessage == NULL) {
dispatchNextMessage = false;
} else {
PRINT(("LOOPER: fLastMessage: 0x%lx: %.4s\n", fLastMessage->what,
(char*)&fLastMessage->what));
DBG(fLastMessage->PrintToStream());
BHandler* handler = NULL;
BMessage::Private messagePrivate(fLastMessage);
bool usePreferred = messagePrivate.UsePreferredTarget();
if (usePreferred) {
PRINT(("LOOPER: use preferred target\n"));
handler = fPreferred;
if (handler == NULL)
handler = this;
} else {
gDefaultTokens.GetToken(messagePrivate.GetTarget(),
B_HANDLER_TOKEN, (void**)&handler);
if (handler != NULL && handler->Looper() != this)
handler = NULL;
PRINT(("LOOPER: use %ld, handler: %p, this: %p\n",
messagePrivate.GetTarget(), handler, this));
}
if (handler != NULL && fLastMessage->HasSpecifiers()) {
int32 index = 0;
if (fLastMessage->GetCurrentSpecifier(&index) == B_OK)
handler = resolve_specifier(handler, fLastMessage);
}
if (handler) {
handler = _TopLevelFilter(fLastMessage, handler);
PRINT(("LOOPER: _TopLevelFilter(): %p\n", handler));
if (handler && handler->Looper() == this)
DispatchMessage(fLastMessage, handler);
}
}
if (fTerminating) {
return;
}
message = fLastMessage;
fLastMessage = NULL;
Unlock();
if (message != NULL)
delete message;
if (port_count(fMsgPort) > 0) {
dispatchNextMessage = false;
}
}
}
PRINT(("BLooper::task_looper() done\n"));
}
void
BLooper::_QuitRequested(BMessage* message)
{
bool isQuitting = QuitRequested();
int32 thread = fThread;
if (isQuitting)
Quit();
bool shutdown;
if (message->IsSourceWaiting()
|| (message->FindBool("_shutdown_", &shutdown) == B_OK && shutdown)) {
BMessage replyMsg(B_REPLY);
replyMsg.AddBool("result", isQuitting);
replyMsg.AddInt32("thread", thread);
message->SendReply(&replyMsg);
}
}
bool
BLooper::AssertLocked() const
{
if (!IsLocked()) {
debugger("looper must be locked before proceeding\n");
return false;
}
return true;
}
BHandler*
BLooper::_TopLevelFilter(BMessage* message, BHandler* target)
{
if (message == NULL)
return target;
target = _ApplyFilters(CommonFilterList(), message, target);
if (target) {
if (target->Looper() != this) {
debugger("Targeted handler does not belong to the looper.");
target = NULL;
} else {
target = _HandlerFilter(message, target);
}
}
return target;
}
BHandler*
BLooper::_HandlerFilter(BMessage* message, BHandler* target)
{
BHandler* previousTarget = NULL;
while (target != NULL && target != previousTarget) {
previousTarget = target;
target = _ApplyFilters(target->FilterList(), message, target);
if (target != NULL && target->Looper() != this) {
debugger("Targeted handler does not belong to the looper.");
target = NULL;
}
}
return target;
}
BHandler*
BLooper::_ApplyFilters(BList* list, BMessage* message, BHandler* target)
{
if (list == NULL || message == NULL)
return target;
BMessageFilter* filter = NULL;
for (int32 i = 0; i < list->CountItems(); ++i) {
filter = (BMessageFilter*)list->ItemAt(i);
if (filter->FiltersAnyCommand() || filter->Command() == message->what) {
message_delivery delivery = filter->MessageDelivery();
bool dropped = message->WasDropped();
if (delivery == B_ANY_DELIVERY
|| (delivery == B_DROPPED_DELIVERY && dropped)
|| (delivery == B_PROGRAMMED_DELIVERY && !dropped)) {
message_source source = filter->MessageSource();
bool remote = message->IsSourceRemote();
if (source == B_ANY_SOURCE
|| (source == B_REMOTE_SOURCE && remote)
|| (source == B_LOCAL_SOURCE && !remote)) {
filter_result result;
filter_hook filterFunction = filter->FilterFunction();
if (filterFunction != NULL)
result = filterFunction(message, &target, filter);
else
result = filter->Filter(message, &target);
if (result == B_SKIP_MESSAGE) {
return NULL;
}
}
}
}
}
return target;
}
void
BLooper::check_lock()
{
uint32 stack;
if (((addr_t)&stack & ~(B_PAGE_SIZE - 1)) == fCachedStack
|| fOwner == find_thread(NULL)) {
return;
}
debugger("Looper must be locked.");
}
BHandler*
BLooper::resolve_specifier(BHandler* target, BMessage* message)
{
if (!target || !message)
return NULL;
int32 index;
BMessage specifier;
int32 form;
const char* property;
status_t err = B_OK;
BHandler* newTarget = target;
do {
err = message->GetCurrentSpecifier(&index, &specifier, &form,
&property);
if (err != B_OK) {
BMessage reply(B_REPLY);
reply.AddInt32("error", err);
message->SendReply(&reply);
return NULL;
}
target = newTarget;
newTarget = target->ResolveSpecifier(message, index, &specifier, form,
property);
if (newTarget == NULL || IndexOf(newTarget) < 0)
return NULL;
err = message->GetCurrentSpecifier(&index);
} while (newTarget && newTarget != target && err == B_OK && index >= 0);
return newTarget;
}
void
BLooper::UnlockFully()
{
AssertLocked();
fOwnerCount = 0;
fOwner = -1;
fCachedStack = 0;
#if DEBUG < 1
int32 atomicCount = atomic_add(&fAtomicCount, -1);
if (atomicCount > 1)
#endif
release_sem(fLockSem);
}
port_id
_get_looper_port_(const BLooper* looper)
{
return looper->fMsgPort;
}