diff --git a/src/servers/registrar/Event.cpp b/src/servers/registrar/Event.cpp new file mode 100644 index 0000000000..1698138173 --- /dev/null +++ b/src/servers/registrar/Event.cpp @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: Event.cpp +// Author: Ingo Weinhold (bonefish@users.sf.net) +// YellowBites (http://www.yellowbites.com) +// Description: Base class for events as handled by EventQueue. +//------------------------------------------------------------------------------ + +#include "Event.h" + +// constructor +Event::Event(bool autoDelete) + : fTime(0), + fAutoDelete(autoDelete) +{ +} + +// constructor +Event::Event(bigtime_t time, bool autoDelete) + : fTime(time), + fAutoDelete(autoDelete) +{ +} + +// destructor +Event::~Event() +{ +} + +// SetTime +void +Event::SetTime(bigtime_t time) +{ + fTime = time; +} + +// Time +bigtime_t +Event::Time() const +{ + return fTime; +} + +// SetAutoDelete +void +Event::SetAutoDelete(bool autoDelete) +{ + fAutoDelete = autoDelete; +} + +// IsAutoDelete +bool +Event::IsAutoDelete() const +{ + return fAutoDelete; +} + +// Do +bool +Event::Do() +{ + return fAutoDelete; +} + diff --git a/src/servers/registrar/Event.h b/src/servers/registrar/Event.h new file mode 100644 index 0000000000..4780442ce7 --- /dev/null +++ b/src/servers/registrar/Event.h @@ -0,0 +1,52 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: Event.h +// Author: Ingo Weinhold (bonefish@users.sf.net) +// YellowBites (http://www.yellowbites.com) +// Description: Base class for events as handled by EventQueue. +//------------------------------------------------------------------------------ + +#ifndef EVENT_H +#define EVENT_H + +#include + +class Event { +public: + Event(bool autoDelete = true); + Event(bigtime_t time, bool autoDelete = true); + virtual ~Event(); + + void SetTime(bigtime_t time); + bigtime_t Time() const; + + void SetAutoDelete(bool autoDelete); + bool IsAutoDelete() const; + + virtual bool Do(); + + private: + bigtime_t fTime; + bool fAutoDelete; +}; + +#endif // EVENT_H diff --git a/src/servers/registrar/EventQueue.cpp b/src/servers/registrar/EventQueue.cpp new file mode 100644 index 0000000000..681f00a8ef --- /dev/null +++ b/src/servers/registrar/EventQueue.cpp @@ -0,0 +1,216 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: EventQueue.cpp +// Author: Ingo Weinhold (bonefish@users.sf.net) +// YellowBites (http://www.yellowbites.com) +// Description: A class featuring providing a mechanism to do events at +// specified times. +//------------------------------------------------------------------------------ + +#include + +#include + +#include "Event.h" + +#include "EventQueue.h" + +static const char *kDefaultEventQueueName = "event looper"; + +// constructor +EventQueue::EventQueue(const char *name) + : fEvents(100), + fEventLooper(-1), + fLooperControl(-1), + fNextEventTime(0), + fStatus(B_ERROR) + +{ + if (!name) + name = kDefaultEventQueueName; + fLooperControl = create_sem(0, (BString(name) += " control").String()); + if (fLooperControl >= B_OK) + fStatus = B_OK; + else + fStatus = fLooperControl; + if (fStatus == B_OK) { + fEventLooper = spawn_thread(_EventLooperEntry, name, B_NORMAL_PRIORITY, + this); + if (fEventLooper >= B_OK) { + fStatus = B_OK; + resume_thread(fEventLooper); + } else + fStatus = fEventLooper; + } +} + +// destructor +EventQueue::~EventQueue() +{ + Die(); + while (Event *event = (Event*)fEvents.RemoveItem(0L)) { + if (event->IsAutoDelete()) + delete event; + } +} + +// InitCheck +status_t +EventQueue::InitCheck() +{ + return fStatus; +} + +// Die +void +EventQueue::Die() +{ + if (delete_sem(fLooperControl) == B_OK) { + int32 dummy; + wait_for_thread(fEventLooper, &dummy); + } +} + +// AddEvent +bool +EventQueue::AddEvent(Event *event) +{ + Lock(); + bool result = _AddEvent(event); + if (result) + _Reschedule(); + Unlock(); + return result; +} + +// RemoveEvent +bool +EventQueue::RemoveEvent(Event *event) +{ + bool result = false; + Lock(); + if ((result = fEvents.RemoveItem(event))) + _Reschedule(); + Unlock(); + return result; +} + +// ModifyEvent +void +EventQueue::ModifyEvent(Event *event, bigtime_t newTime) +{ + Lock(); + if (fEvents.RemoveItem(event)) { + event->SetTime(newTime); + _AddEvent(event); + _Reschedule(); + } + Unlock(); +} + +// _AddEvent +// +// PRE: The object must be locked. +bool +EventQueue::_AddEvent(Event *event) +{ + // find the insertion index + int32 lower = 0; + int32 upper = fEvents.CountItems(); + while (lower < upper) { + int32 mid = (lower + upper) / 2; + Event* midEvent = _EventAt(mid); + if (event->Time() < midEvent->Time()) + upper = mid; + else + lower = mid + 1; + } + return fEvents.AddItem(event, lower); +} + +// _EventAt +Event* +EventQueue::_EventAt(int32 index) const +{ + return (Event*)fEvents.ItemAt(index); +} + +// _EventLooperEntry +int32 +EventQueue::_EventLooperEntry(void *data) +{ + return ((EventQueue*)data)->_EventLooper(); +} + +// _EventLooper +int32 +EventQueue::_EventLooper() +{ + bool running = true; + while (running) { + bigtime_t waitUntil = B_INFINITE_TIMEOUT; + if (Lock()) { + if (!fEvents.IsEmpty()) + waitUntil = _EventAt(0)->Time(); + fNextEventTime = waitUntil; + Unlock(); + } + status_t err = acquire_sem_etc(fLooperControl, 1, B_ABSOLUTE_TIMEOUT, + waitUntil); + switch (err) { + case B_TIMED_OUT: + // do events, that are supposed to go off + while (Lock() && !fEvents.IsEmpty() + && system_time() >= _EventAt(0)->Time()) { + Event *event = (Event*)fEvents.RemoveItem(0L); + Unlock(); + bool autoDeleteEvent = event->IsAutoDelete(); + bool deleteEvent = event->Do() || autoDeleteEvent; + if (deleteEvent) + delete event; + } + if (IsLocked()) + Unlock(); + break; + case B_BAD_SEM_ID: + running = false; + break; + case B_OK: + default: + break; + } + } + return 0; +} + +// _Reschedule +// +// PRE: The object must be locked. +void +EventQueue::_Reschedule() +{ + if (fStatus == B_OK) { + if (!fEvents.IsEmpty() && _EventAt(0)->Time() < fNextEventTime) + release_sem(fLooperControl); + } +} + diff --git a/src/servers/registrar/EventQueue.h b/src/servers/registrar/EventQueue.h new file mode 100644 index 0000000000..6b60fcfe85 --- /dev/null +++ b/src/servers/registrar/EventQueue.h @@ -0,0 +1,66 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: EventQueue.h +// Author: Ingo Weinhold (bonefish@users.sf.net) +// YellowBites (http://www.yellowbites.com) +// Description: A class featuring providing a mechanism to do events at +// specified times. +//------------------------------------------------------------------------------ + +#ifndef EVENT_QUEUE_H +#define EVENT_QUEUE_H + +#include +#include +#include + +class Event; + +class EventQueue : public BLocker { +public: + EventQueue(const char *name = NULL); + virtual ~EventQueue(); + + status_t InitCheck(); + + void Die(); + + bool AddEvent(Event *event); + bool RemoveEvent(Event *event); + void ModifyEvent(Event *event, bigtime_t newTime); + + private: + bool _AddEvent(Event *event); + Event *_EventAt(int32 index) const; + + static int32 _EventLooperEntry(void *data); + int32 _EventLooper(); + void _Reschedule(); + + BList fEvents; + thread_id fEventLooper; + sem_id fLooperControl; + volatile bigtime_t fNextEventTime; + status_t fStatus; +}; + +#endif // EVENT_QUEUE_H diff --git a/src/servers/registrar/Jamfile b/src/servers/registrar/Jamfile index 638fe4371e..e968a9f5cd 100644 --- a/src/servers/registrar/Jamfile +++ b/src/servers/registrar/Jamfile @@ -7,6 +7,9 @@ UsePrivateHeaders storage ; Server obos_registrar : AppInfoList.cpp ClipboardHandler.cpp + Event.cpp + EventQueue.cpp + MessageRunnerManager.cpp MIMEManager.cpp Registrar.cpp RosterAppInfo.cpp diff --git a/src/servers/registrar/MessageRunnerManager.cpp b/src/servers/registrar/MessageRunnerManager.cpp new file mode 100644 index 0000000000..281d1ad45a --- /dev/null +++ b/src/servers/registrar/MessageRunnerManager.cpp @@ -0,0 +1,519 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: MessageRunnerManager.cpp +// Author: Ingo Weinhold (bonefish@users.sf.net) +// Description: Manages the registrar side "shadows" of BMessageRunners. +//------------------------------------------------------------------------------ + +#include +#include + +#include +#include +#include +#include +#include + +#include "Debug.h" +#include "Event.h" +#include "EventQueue.h" +#include "MessageRunnerManager.h" + +// the minimal time interval for message runners +static const bigtime_t kMininalTimeInterval = 50000LL; + +// RunnerEvent +class MessageRunnerManager::RunnerEvent : public Event { +public: + RunnerEvent(MessageRunnerManager *manager, RunnerInfo *info) + : Event(false), + fManager(manager), + fInfo(info) + { + } + + virtual bool Do() + { + return fManager->_DoEvent(fInfo); + } + +private: + MessageRunnerManager *fManager; + RunnerInfo *fInfo; +}; + + +// RunnerInfo +struct MessageRunnerManager::RunnerInfo { + RunnerInfo(team_id team, int32 token, BMessenger target, BMessage *message, + bigtime_t interval, int32 count, BMessenger replyTarget) + : team(team), + token(token), + target(target), + message(message), + interval(interval), + count(count), + replyTarget(replyTarget), + time(0), + event(NULL), + rescheduled(false) + { + } + + ~RunnerInfo() + { + delete message; + delete event; + } + + status_t DeliverMessage() + { + if (count > 0) + count--; + status_t error = target.SendMessage(message, replyTarget, 0); + // B_WOULD_BLOCK is as good as B_OK. We return an error only, if + // there are serious problems with the target, i.e. if it doesn't + // exist anymore for instance. A full message port is harmless. + if (error == B_WOULD_BLOCK) + error = B_OK; + return error; + } + + team_id team; + int32 token; + BMessenger target; + BMessage *message; + bigtime_t interval; + int32 count; + BMessenger replyTarget; + bigtime_t time; + RunnerEvent *event; + bool rescheduled; +}; + + +// constructor +MessageRunnerManager::MessageRunnerManager(EventQueue *eventQueue) + : fRunnerInfos(), + fLock(), + fEventQueue(eventQueue), + fNextToken(0) +{ +} + +// destructor +MessageRunnerManager::~MessageRunnerManager() +{ + // The event queue should already be stopped, but must still exist. + // If it is still running and an event gets executed after we've locked + // ourselves, then it will access an already deleted manager. + BAutolock _lock(fLock); + for (int32 i = 0; RunnerInfo *info = _InfoAt(i); i++) { + if (!fEventQueue->RemoveEvent(info->event)) + info->event = NULL; + delete info; + } + fRunnerInfos.MakeEmpty(); +} + +// HandleRegisterRunner +void +MessageRunnerManager::HandleRegisterRunner(BMessage *request) +{ + FUNCTION_START(); + + BAutolock _lock(fLock); + status_t error = B_OK; + // get the parameters + team_id team; + BMessenger target; + // TODO: This should be a "new (nothrow)", but R5's BMessage doesn't + // define that version. + BMessage *message = new BMessage; + bigtime_t interval; + int32 count; + BMessenger replyTarget; + if (error == B_OK && message == NULL) + error = B_NO_MEMORY; + if (error == B_OK && request->FindInt32("team", &team) != B_OK) + error = B_BAD_VALUE; + if (error == B_OK && request->FindMessenger("target", &target) != B_OK) + error = B_BAD_VALUE; + if (error == B_OK && request->FindMessage("message", message) != B_OK) + error = B_BAD_VALUE; + if (error == B_OK && request->FindInt64("interval", &interval) != B_OK) + error = B_BAD_VALUE; + if (error == B_OK && request->FindInt32("count", &count) != B_OK) + error = B_BAD_VALUE; + if (error == B_OK + && request->FindMessenger("reply_target", &replyTarget) != B_OK) { + error = B_BAD_VALUE; + } + // check the parameters + if (error == B_OK && count == 0) + error = B_BAD_VALUE; + // add a new runner info + RunnerInfo *info = NULL; + if (error == B_OK) { + interval = max(interval, kMininalTimeInterval); + info = new(nothrow) RunnerInfo(team, _NextToken(), target, message, + interval, count, replyTarget); + if (info) { + info->time = system_time(); + if (!_AddInfo(info)) + error = B_NO_MEMORY; + } else + error = B_NO_MEMORY; + } + // create a new event + RunnerEvent *event = NULL; + if (error == B_OK) { + event = new(nothrow) RunnerEvent(this, info); + if (event) { + info->event = event; + if (!_ScheduleEvent(info)) + error = B_NO_MEMORY; // TODO: The only possible reason? + } else + error = B_NO_MEMORY; + } + // cleanup on error + if (error != B_OK) { + if (info) { + _RemoveInfo(info); + delete info; + } + delete message; + } + // reply to the request + if (error == B_OK) { + BMessage reply(B_REG_SUCCESS); + reply.AddInt32("token", info->token); + request->SendReply(&reply); + } else { + BMessage reply(B_REG_ERROR); + reply.AddInt32("error", error); + request->SendReply(&reply); + } + + FUNCTION_END(); +} + +// HandleUnregisterRunner +void +MessageRunnerManager::HandleUnregisterRunner(BMessage *request) +{ + FUNCTION_START(); + + BAutolock _lock(fLock); + status_t error = B_OK; + // get the parameters + int32 token; + if (error == B_OK && request->FindInt32("token", &token) != B_OK) + error = B_BAD_VALUE; + // find and delete the runner info + if (error == B_OK) { + if (RunnerInfo *info = _InfoForToken(token)) + _DeleteInfo(info, false); + else + error = B_BAD_VALUE; + } + // reply to the request + if (error == B_OK) { + BMessage reply(B_REG_SUCCESS); + request->SendReply(&reply); + } else { + BMessage reply(B_REG_ERROR); + reply.AddInt32("error", error); + request->SendReply(&reply); + } + + FUNCTION_END(); +} + +// HandleSetRunnerParams +void +MessageRunnerManager::HandleSetRunnerParams(BMessage *request) +{ + FUNCTION_START(); + + BAutolock _lock(fLock); + status_t error = B_OK; + // get the parameters + int32 token; + bigtime_t interval; + int32 count; + bool setInterval = false; + bool setCount = false; + if (error == B_OK && request->FindInt32("token", &token) != B_OK) + error = B_BAD_VALUE; + if (error == B_OK && request->FindInt64("interval", &interval) == B_OK) + setInterval = true; + if (error == B_OK && request->FindInt32("count", &count) == B_OK) + setCount = true; + // find the runner info + RunnerInfo *info = NULL; + if (error == B_OK) { + info = _InfoForToken(token); + if (!info) + error = B_BAD_VALUE; + } + // set the new values + if (error == B_OK) { + bool eventRemoved = false; + bool deleteInfo = false; + // count + if (setCount) { + if (count == 0) + deleteInfo = true; + else + info->count = count; + } + // interval + if (setInterval) { + eventRemoved = fEventQueue->RemoveEvent(info->event); + if (!eventRemoved) + info->rescheduled = true; + interval = max(interval, kMininalTimeInterval); + info->interval = interval; + info->time = system_time(); + if (!_ScheduleEvent(info)) + error = B_NO_MEMORY; // TODO: The only possible reason? + } + // remove and delete the info on error + if (error != B_OK || deleteInfo) + _DeleteInfo(info, eventRemoved); + } + // reply to the request + if (error == B_OK) { + BMessage reply(B_REG_SUCCESS); + request->SendReply(&reply); + } else { + BMessage reply(B_REG_ERROR); + reply.AddInt32("error", error); + request->SendReply(&reply); + } + + FUNCTION_END(); +} + +// HandleGetRunnerInfo +void +MessageRunnerManager::HandleGetRunnerInfo(BMessage *request) +{ + FUNCTION_START(); + + BAutolock _lock(fLock); + status_t error = B_OK; + // get the parameters + int32 token; + if (error == B_OK && request->FindInt32("token", &token) != B_OK) + error = B_BAD_VALUE; + // find the runner info + RunnerInfo *info = NULL; + if (error == B_OK) { + info = _InfoForToken(token); + if (!info) + error = B_BAD_VALUE; + } + // reply to the request + if (error == B_OK) { + BMessage reply(B_REG_SUCCESS); + reply.AddInt64("interval", info->interval); + reply.AddInt32("count", info->count); + request->SendReply(&reply); + } else { + BMessage reply(B_REG_ERROR); + reply.AddInt32("error", error); + request->SendReply(&reply); + } + + FUNCTION_END(); +} + +// Lock +bool +MessageRunnerManager::Lock() +{ + return fLock.Lock(); +} + +// Unlock +void +MessageRunnerManager::Unlock() +{ + fLock.Unlock(); +} + +// _AddInfo +bool +MessageRunnerManager::_AddInfo(RunnerInfo *info) +{ + return fRunnerInfos.AddItem(info); +} + +// _RemoveInfo +bool +MessageRunnerManager::_RemoveInfo(RunnerInfo *info) +{ + return fRunnerInfos.RemoveItem(info); +} + +// _RemoveInfo +MessageRunnerManager::RunnerInfo* +MessageRunnerManager::_RemoveInfo(int32 index) +{ + return (RunnerInfo*)fRunnerInfos.RemoveItem(index); +} + +// _RemoveInfoWithToken +MessageRunnerManager::RunnerInfo* +MessageRunnerManager::_RemoveInfoWithToken(int32 token) +{ + RunnerInfo *info = NULL; + int32 index = _IndexOfToken(token); + if (index >= 0) + info = _RemoveInfo(index); + return info; +} + +// _DeleteInfo +bool +MessageRunnerManager::_DeleteInfo(RunnerInfo *info, bool eventRemoved) +{ + bool result = _RemoveInfo(info); + if (result) { + // If the event is not in the event queue and has not been removed + // just before, then it is in progress. It will delete itself. + if (!eventRemoved && !fEventQueue->RemoveEvent(info->event)) + info->event = NULL; + delete info; + } + return result; +} + +// _CountInfos +int32 +MessageRunnerManager::_CountInfos() const +{ + return fRunnerInfos.CountItems(); +} + +// _InfoAt +MessageRunnerManager::RunnerInfo* +MessageRunnerManager::_InfoAt(int32 index) const +{ + return (RunnerInfo*)fRunnerInfos.ItemAt(index); +} + +// _InfoForToken +MessageRunnerManager::RunnerInfo* +MessageRunnerManager::_InfoForToken(int32 token) const +{ + return _InfoAt(_IndexOfToken(token)); +} + +// _IndexOf +int32 +MessageRunnerManager::_IndexOf(RunnerInfo *info) const +{ + return fRunnerInfos.IndexOf(info); +} + +// _IndexOfToken +int32 +MessageRunnerManager::_IndexOfToken(int32 token) const +{ + for (int32 i = 0; RunnerInfo *info = _InfoAt(i); i++) { + if (info->token == token) + return i; + } + return -1; +} + +// _DoEvent +bool +MessageRunnerManager::_DoEvent(RunnerInfo *info) +{ + FUNCTION_START(); + + BAutolock _lock(fLock); + bool deleteEvent = false; + // first check whether the info does still exist + if (_lock.IsLocked() && _IndexOf(info) >= 0) { + // If the event has been rescheduled after being removed from the + // queue for execution, it needs to be ignored. This may happen, when + // the interval is modified. + if (info->rescheduled) + info->rescheduled = false; + else { + // send the message + bool success = (info->DeliverMessage() == B_OK); + // reschedule the event + if (success) + success = _ScheduleEvent(info); + // clean up, if the message delivery of the rescheduling failed + if (!success) { + deleteEvent = true; + info->event = NULL; + _RemoveInfo(info); + delete info; + } + } + } else { + // The info is no more. That means it had been removed after the + // event was removed from the event queue, but before we could acquire + // the lock. Simply delete the event. + deleteEvent = true; + } + + FUNCTION_END(); + + return deleteEvent; +} + +// _ScheduleEvent +bool +MessageRunnerManager::_ScheduleEvent(RunnerInfo *info) +{ + bool scheduled = false; + // calculate next event time + if (info->count != 0) { + // avoid a bigtime_t overflow + if (LONGLONG_MAX - info->interval < info->time) + info->time = LONGLONG_MAX; + else + info->time += info->interval; + info->event->SetTime(info->time); + scheduled = fEventQueue->AddEvent(info->event); +PRINT(("runner %ld (%lld, %ld) rescheduled: %d, time: %lld, now: %lld\n", +info->token, info->interval, info->count, scheduled, info->time, system_time())); + } + return scheduled; +} + +// _NextToken +int32 +MessageRunnerManager::_NextToken() +{ + return fNextToken++; +} + diff --git a/src/servers/registrar/MessageRunnerManager.h b/src/servers/registrar/MessageRunnerManager.h new file mode 100644 index 0000000000..c40500e819 --- /dev/null +++ b/src/servers/registrar/MessageRunnerManager.h @@ -0,0 +1,83 @@ +//------------------------------------------------------------------------------ +// Copyright (c) 2001-2002, OpenBeOS +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the "Software"), +// to deal in the Software without restriction, including without limitation +// the rights to use, copy, modify, merge, publish, distribute, sublicense, +// and/or sell copies of the Software, and to permit persons to whom the +// Software is furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +// +// File Name: MessageRunnerManager.h +// Author: Ingo Weinhold (bonefish@users.sf.net) +// Description: Manages the registrar side "shadows" of BMessageRunners. +//------------------------------------------------------------------------------ + +#ifndef MESSAGE_RUNNER_MANAGER_H +#define MESSAGE_RUNNER_MANAGER_H + +#include +#include + +class BMessage; +class EventQueue; + +class MessageRunnerManager { +public: + MessageRunnerManager(EventQueue *eventQueue); + virtual ~MessageRunnerManager(); + + void HandleRegisterRunner(BMessage *request); + void HandleUnregisterRunner(BMessage *request); + void HandleSetRunnerParams(BMessage *request); + void HandleGetRunnerInfo(BMessage *request); + + bool Lock(); + void Unlock(); + +private: + class RunnerEvent; + struct RunnerInfo; + friend class RunnerEvent; + +private: + bool _AddInfo(RunnerInfo *info); + bool _RemoveInfo(RunnerInfo *info); + RunnerInfo *_RemoveInfo(int32 index); + RunnerInfo *_RemoveInfoWithToken(int32 token); + bool _DeleteInfo(RunnerInfo *info, bool eventRemoved); + + int32 _CountInfos() const; + + RunnerInfo *_InfoAt(int32 index) const; + RunnerInfo *_InfoForToken(int32 token) const; + + bool _HasInfo(RunnerInfo *info) const; + + int32 _IndexOf(RunnerInfo *info) const; + int32 _IndexOfToken(int32 token) const; + + bool _DoEvent(RunnerInfo *info); + bool _ScheduleEvent(RunnerInfo *info); + + int32 _NextToken(); + +private: + BList fRunnerInfos; + BLocker fLock; + EventQueue *fEventQueue; + int32 fNextToken; +}; + +#endif // MESSAGE_RUNNER_MANAGER_H diff --git a/src/servers/registrar/Registrar.cpp b/src/servers/registrar/Registrar.cpp index 946722cba8..5e28c7863b 100644 --- a/src/servers/registrar/Registrar.cpp +++ b/src/servers/registrar/Registrar.cpp @@ -8,10 +8,14 @@ #include #include "ClipboardHandler.h" +#include "EventQueue.h" +#include "MessageRunnerManager.h" #include "MIMEManager.h" #include "Registrar.h" #include "TRoster.h" +static const char *kEventQueueName = "timer_thread"; + /*! \class Registrar \brief The application class of the registrar. @@ -26,7 +30,9 @@ Registrar::Registrar() : BApplication(kRegistrarSignature), fRoster(NULL), fClipboardHandler(NULL), - fMIMEManager(NULL) + fMIMEManager(NULL), + fEventQueue(NULL), + fMessageRunnerManager(NULL) { FUNCTION_START(); } @@ -41,6 +47,9 @@ Registrar::~Registrar() { FUNCTION_START(); Lock(); + fEventQueue->Die(); + delete fMessageRunnerManager; + delete fEventQueue; fMIMEManager->Lock(); fMIMEManager->Quit(); RemoveHandler(fClipboardHandler); @@ -59,6 +68,7 @@ Registrar::MessageReceived(BMessage *message) { FUNCTION_START(); switch (message->what) { + // general requests case B_REG_GET_MIME_MESSENGER: { PRINT(("B_REG_GET_MIME_MESSENGER\n")); @@ -77,6 +87,7 @@ Registrar::MessageReceived(BMessage *message) message->SendReply(&reply); break; } + // roster requests case B_REG_ADD_APP: fRoster->HandleAddApplication(message); break; @@ -107,6 +118,19 @@ Registrar::MessageReceived(BMessage *message) case B_REG_ACTIVATE_APP: fRoster->HandleActivateApp(message); break; + // message runner requests + case B_REG_REGISTER_MESSAGE_RUNNER: + fMessageRunnerManager->HandleRegisterRunner(message); + break; + case B_REG_UNREGISTER_MESSAGE_RUNNER: + fMessageRunnerManager->HandleUnregisterRunner(message); + break; + case B_REG_SET_MESSAGE_RUNNER_PARAMS: + fMessageRunnerManager->HandleSetRunnerParams(message); + break; + case B_REG_GET_MESSAGE_RUNNER_INFO: + fMessageRunnerManager->HandleGetRunnerInfo(message); + break; default: BApplication::MessageReceived(message); break; @@ -131,6 +155,9 @@ Registrar::ReadyToRun() // create MIME manager fMIMEManager = new MIMEManager; fMIMEManager->Run(); + // create message runner manager + fEventQueue = new EventQueue(kEventQueueName); + fMessageRunnerManager = new MessageRunnerManager(fEventQueue); // init the global be_roster BPrivate::init_registrar_roster(be_app_messenger, BMessenger(NULL, fMIMEManager)); diff --git a/src/servers/registrar/Registrar.h b/src/servers/registrar/Registrar.h index 6a6aff3183..6ee7a74753 100644 --- a/src/servers/registrar/Registrar.h +++ b/src/servers/registrar/Registrar.h @@ -29,6 +29,8 @@ #include class ClipboardHandler; +class EventQueue; +class MessageRunnerManager; class MIMEManager; namespace BPrivate { @@ -45,9 +47,11 @@ public: virtual bool QuitRequested(); private: - BPrivate::TRoster *fRoster; - ClipboardHandler *fClipboardHandler; - MIMEManager *fMIMEManager; + BPrivate::TRoster *fRoster; + ClipboardHandler *fClipboardHandler; + MIMEManager *fMIMEManager; + EventQueue *fEventQueue; + MessageRunnerManager *fMessageRunnerManager; }; #endif // REGISTRAR_H