* Documented the class.
* Added helper methods and rewrote Remove() to take advantage of the fact that the event list is ordered. * Introduced a termination flag to shutdown the thread as early as possible. * Event::Do() get a pointer to the queue as parameter. git-svn-id: file:///srv/svn/repos/haiku/trunk/current@1515 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
@@ -22,7 +22,7 @@
|
|||||||
// File Name: EventQueue.cpp
|
// File Name: EventQueue.cpp
|
||||||
// Author: Ingo Weinhold ([email protected])
|
// Author: Ingo Weinhold ([email protected])
|
||||||
// YellowBites (http://www.yellowbites.com)
|
// YellowBites (http://www.yellowbites.com)
|
||||||
// Description: A class featuring providing a mechanism to do events at
|
// Description: A class providing a mechanism for executing events at
|
||||||
// specified times.
|
// specified times.
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -31,19 +31,78 @@
|
|||||||
#include <String.h>
|
#include <String.h>
|
||||||
|
|
||||||
#include "Event.h"
|
#include "Event.h"
|
||||||
|
|
||||||
#include "EventQueue.h"
|
#include "EventQueue.h"
|
||||||
|
|
||||||
static const char *kDefaultEventQueueName = "event looper";
|
static const char *kDefaultEventQueueName = "event looper";
|
||||||
|
|
||||||
|
/*!
|
||||||
|
\class EventQueue
|
||||||
|
\brief A class providing a mechanism for executing events at specified
|
||||||
|
times.
|
||||||
|
|
||||||
|
The class' interface is quite small. It basically features methods to
|
||||||
|
add or remove an event or to modify an event's time. It is derived from
|
||||||
|
BLocker to inherit a locking mechanism needed to serialize the access to
|
||||||
|
its member variables (especially the event list).
|
||||||
|
|
||||||
|
The queue runs an own thread (in _EventLooper()), which executes the
|
||||||
|
events at the right times. The execution of an event consists of invoking
|
||||||
|
its Event::Do() method. If the event's Event::IsAutoDelete() or its Do()
|
||||||
|
method return \c true, the event object is deleted after execution. In
|
||||||
|
any case the event is removed from the list before it is executed. The
|
||||||
|
queue is not locked while an event is executed.
|
||||||
|
|
||||||
|
The event list (\a fEvents) is ordered ascendingly by time. The thread
|
||||||
|
uses a semaphore (\a fLooperControl) to wait (time out) for the next
|
||||||
|
event. This semaphore is released to indicate changes to the event list.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \var BList EventQueue::fEvents
|
||||||
|
\brief List of events (Event*) in ascending order (by time).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \var thread_id EventQueue::fEventLooper
|
||||||
|
\brief Thread ID of the queue's thread.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \var sem_id EventQueue::fLooperControl
|
||||||
|
\brief Queue thread control semaphore.
|
||||||
|
|
||||||
|
The semaphore is initialized with count \c 0 and used by the queue's
|
||||||
|
thread for timing out at the time when the next event has to be executed.
|
||||||
|
If the event list is changed by another thread and that changed the time
|
||||||
|
of the next event the semaphore is released (_Reschedule()).
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \var volatile bigtime_t EventQueue::fNextEventTime
|
||||||
|
\brief Time at which the queue's thread will wake up next time.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \var status_t EventQueue::fStatus
|
||||||
|
\brief Initialization status.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*! \var volatile bool EventQueue::fTerminating
|
||||||
|
\brief Set to \c true by Die() to signal the queue's thread not to
|
||||||
|
execute any more events.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
// constructor
|
// constructor
|
||||||
|
/*! \brief Creates a new event queue.
|
||||||
|
|
||||||
|
The status of the initialization can and should be check with InitCheck().
|
||||||
|
|
||||||
|
\param name The name used for the queue's thread and semaphore. If \c NULL,
|
||||||
|
a default name is used.
|
||||||
|
*/
|
||||||
EventQueue::EventQueue(const char *name)
|
EventQueue::EventQueue(const char *name)
|
||||||
: fEvents(100),
|
: fEvents(100),
|
||||||
fEventLooper(-1),
|
fEventLooper(-1),
|
||||||
fLooperControl(-1),
|
fLooperControl(-1),
|
||||||
fNextEventTime(0),
|
fNextEventTime(0),
|
||||||
fStatus(B_ERROR)
|
fStatus(B_ERROR),
|
||||||
|
fTerminating(false)
|
||||||
{
|
{
|
||||||
if (!name)
|
if (!name)
|
||||||
name = kDefaultEventQueueName;
|
name = kDefaultEventQueueName;
|
||||||
@@ -64,6 +123,11 @@ EventQueue::EventQueue(const char *name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// destructor
|
// destructor
|
||||||
|
/*! \brief Frees all resources associated by this object.
|
||||||
|
|
||||||
|
Die() is called to terminate the queue's thread and all events whose
|
||||||
|
IsAutoDelete() returns \c true are deleted.
|
||||||
|
*/
|
||||||
EventQueue::~EventQueue()
|
EventQueue::~EventQueue()
|
||||||
{
|
{
|
||||||
Die();
|
Die();
|
||||||
@@ -74,6 +138,9 @@ EventQueue::~EventQueue()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// InitCheck
|
// InitCheck
|
||||||
|
/*! \brief Returns the initialization status of the event queue.
|
||||||
|
\return \c B_OK, if everything went fine, an error code otherwise.
|
||||||
|
*/
|
||||||
status_t
|
status_t
|
||||||
EventQueue::InitCheck()
|
EventQueue::InitCheck()
|
||||||
{
|
{
|
||||||
@@ -81,9 +148,17 @@ EventQueue::InitCheck()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Die
|
// Die
|
||||||
|
/*! \brief Terminates the queue's thread.
|
||||||
|
|
||||||
|
If an event is currently executed, it is allowed to finish its task
|
||||||
|
normally, but no more events will be executed.
|
||||||
|
|
||||||
|
Afterwards events can still be added to and removed from the queue.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
EventQueue::Die()
|
EventQueue::Die()
|
||||||
{
|
{
|
||||||
|
fTerminating = true;
|
||||||
if (delete_sem(fLooperControl) == B_OK) {
|
if (delete_sem(fLooperControl) == B_OK) {
|
||||||
int32 dummy;
|
int32 dummy;
|
||||||
wait_for_thread(fEventLooper, &dummy);
|
wait_for_thread(fEventLooper, &dummy);
|
||||||
@@ -91,11 +166,22 @@ EventQueue::Die()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// AddEvent
|
// AddEvent
|
||||||
|
/*! \brief Adds a new event to the queue.
|
||||||
|
|
||||||
|
The event's time must be set, before adding it. Afterwards ModifyEvent()
|
||||||
|
must be used to change an event's time.
|
||||||
|
|
||||||
|
If the event's time is already passed, it is executed as soon as possible.
|
||||||
|
|
||||||
|
\param event The event to be added.
|
||||||
|
\return \c true, if the event has been added successfully, \c false, if
|
||||||
|
an error occured.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
EventQueue::AddEvent(Event *event)
|
EventQueue::AddEvent(Event *event)
|
||||||
{
|
{
|
||||||
Lock();
|
Lock();
|
||||||
bool result = _AddEvent(event);
|
bool result = (event && _AddEvent(event));
|
||||||
if (result)
|
if (result)
|
||||||
_Reschedule();
|
_Reschedule();
|
||||||
Unlock();
|
Unlock();
|
||||||
@@ -103,18 +189,33 @@ EventQueue::AddEvent(Event *event)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RemoveEvent
|
// RemoveEvent
|
||||||
|
/*! \brief Removes an event from the queue.
|
||||||
|
\param event The event to be removed.
|
||||||
|
\return \c true, if the event has been removed successfully, \c false, if
|
||||||
|
the supplied event wasn't in the queue before.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
EventQueue::RemoveEvent(Event *event)
|
EventQueue::RemoveEvent(Event *event)
|
||||||
{
|
{
|
||||||
bool result = false;
|
bool result = false;
|
||||||
Lock();
|
Lock();
|
||||||
if ((result = fEvents.RemoveItem(event)))
|
if (event && ((result = _RemoveEvent(event))))
|
||||||
_Reschedule();
|
_Reschedule();
|
||||||
Unlock();
|
Unlock();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ModifyEvent
|
// ModifyEvent
|
||||||
|
/*! \brief Modifies an event's time.
|
||||||
|
|
||||||
|
The event must be in the queue.
|
||||||
|
|
||||||
|
If the event's new time is already passed, it is executed as soon as
|
||||||
|
possible.
|
||||||
|
|
||||||
|
\param event The event in question.
|
||||||
|
\param newTime The new event time.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
EventQueue::ModifyEvent(Event *event, bigtime_t newTime)
|
EventQueue::ModifyEvent(Event *event, bigtime_t newTime)
|
||||||
{
|
{
|
||||||
@@ -128,33 +229,112 @@ EventQueue::ModifyEvent(Event *event, bigtime_t newTime)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// _AddEvent
|
// _AddEvent
|
||||||
//
|
/*! \brief Adds an event to the event list.
|
||||||
// PRE: The object must be locked.
|
|
||||||
|
\note The object must be locked when this method is invoked.
|
||||||
|
|
||||||
|
\param event The event to be added.
|
||||||
|
\return \c true, if the event has been added successfully, \c false, if
|
||||||
|
an error occured.
|
||||||
|
*/
|
||||||
bool
|
bool
|
||||||
EventQueue::_AddEvent(Event *event)
|
EventQueue::_AddEvent(Event *event)
|
||||||
{
|
{
|
||||||
// find the insertion index
|
int32 index = _FindInsertionIndex(event->Time());
|
||||||
int32 lower = 0;
|
return fEvents.AddItem(event, index);
|
||||||
int32 upper = fEvents.CountItems();
|
}
|
||||||
while (lower < upper) {
|
|
||||||
int32 mid = (lower + upper) / 2;
|
// _RemoveEvent
|
||||||
Event* midEvent = _EventAt(mid);
|
/*! \brief Removes an event from the event list.
|
||||||
if (event->Time() < midEvent->Time())
|
|
||||||
upper = mid;
|
\note The object must be locked when this method is invoked.
|
||||||
else
|
|
||||||
lower = mid + 1;
|
\param event The event to be removed.
|
||||||
}
|
\return \c true, if the event has been removed successfully, \c false, if
|
||||||
return fEvents.AddItem(event, lower);
|
the supplied event wasn't in the queue before.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
EventQueue::_RemoveEvent(Event *event)
|
||||||
|
{
|
||||||
|
int32 index = _IndexOfEvent(event);
|
||||||
|
return (index >= 0 && fEvents.RemoveItem(index));
|
||||||
}
|
}
|
||||||
|
|
||||||
// _EventAt
|
// _EventAt
|
||||||
|
/*! \brief Returns an event from the event list.
|
||||||
|
|
||||||
|
\note The object must be locked when this method is invoked.
|
||||||
|
|
||||||
|
\param index The list index of the event to be returned.
|
||||||
|
\return The event, or \c NULL, if the index is out of range.
|
||||||
|
*/
|
||||||
Event*
|
Event*
|
||||||
EventQueue::_EventAt(int32 index) const
|
EventQueue::_EventAt(int32 index) const
|
||||||
{
|
{
|
||||||
return (Event*)fEvents.ItemAt(index);
|
return (Event*)fEvents.ItemAt(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// _IndexOfEvent
|
||||||
|
/*! \brief Returns the event list index of the supplied event.
|
||||||
|
|
||||||
|
\note The object must be locked when this method is invoked.
|
||||||
|
|
||||||
|
\param event The event to be found.
|
||||||
|
\return The event list index of the supplied event or \c -1, if the event
|
||||||
|
is not in the event list.
|
||||||
|
*/
|
||||||
|
int32
|
||||||
|
EventQueue::_IndexOfEvent(Event *event) const
|
||||||
|
{
|
||||||
|
bigtime_t time = event->Time();
|
||||||
|
int32 index = _FindInsertionIndex(time);
|
||||||
|
// The found index is the index succeeding the one of the last event with
|
||||||
|
// the same time.
|
||||||
|
// search backwards for the event
|
||||||
|
Event *listEvent = NULL;
|
||||||
|
while (((listEvent = _EventAt(--index))) && listEvent->Time() == time) {
|
||||||
|
if (listEvent == event)
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// _FindInsertionIndex
|
||||||
|
/*! \brief Finds the event list index at which an event with the supplied
|
||||||
|
has to be added.
|
||||||
|
|
||||||
|
The returned index is the greatest possible index, that is if there are
|
||||||
|
events with the same time in the list, the index is the successor of the
|
||||||
|
index of the last of these events.
|
||||||
|
|
||||||
|
\note The object must be locked when this method is invoked.
|
||||||
|
|
||||||
|
\param time The event time.
|
||||||
|
\return The index at which an event with the supplied time should be added.
|
||||||
|
*/
|
||||||
|
int32
|
||||||
|
EventQueue::_FindInsertionIndex(bigtime_t time) const
|
||||||
|
{
|
||||||
|
// binary search
|
||||||
|
int32 lower = 0;
|
||||||
|
int32 upper = fEvents.CountItems();
|
||||||
|
// invariant: lower-1:time <= time < upper:time (ignoring invalid indices)
|
||||||
|
while (lower < upper) {
|
||||||
|
int32 mid = (lower + upper) / 2;
|
||||||
|
Event* midEvent = _EventAt(mid);
|
||||||
|
if (time < midEvent->Time())
|
||||||
|
upper = mid; // time < mid:time => time < upper:time
|
||||||
|
else
|
||||||
|
lower = mid + 1; // mid:time <= time => lower-1:time <= time
|
||||||
|
}
|
||||||
|
return lower;
|
||||||
|
}
|
||||||
|
|
||||||
// _EventLooperEntry
|
// _EventLooperEntry
|
||||||
|
/*! \brief Entry point from the queue's thread.
|
||||||
|
\param data The queue's \c this pointer.
|
||||||
|
\return The thread's result. Of no relevance in this case.
|
||||||
|
*/
|
||||||
int32
|
int32
|
||||||
EventQueue::_EventLooperEntry(void *data)
|
EventQueue::_EventLooperEntry(void *data)
|
||||||
{
|
{
|
||||||
@@ -162,6 +342,9 @@ EventQueue::_EventLooperEntry(void *data)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// _EventLooper
|
// _EventLooper
|
||||||
|
/*! \brief Method with the main loop of the queue's thread.
|
||||||
|
\return The thread's result. Of no relevance in this case.
|
||||||
|
*/
|
||||||
int32
|
int32
|
||||||
EventQueue::_EventLooper()
|
EventQueue::_EventLooper()
|
||||||
{
|
{
|
||||||
@@ -179,12 +362,12 @@ EventQueue::_EventLooper()
|
|||||||
switch (err) {
|
switch (err) {
|
||||||
case B_TIMED_OUT:
|
case B_TIMED_OUT:
|
||||||
// do events, that are supposed to go off
|
// do events, that are supposed to go off
|
||||||
while (Lock() && !fEvents.IsEmpty()
|
while (!fTerminating && Lock() && !fEvents.IsEmpty()
|
||||||
&& system_time() >= _EventAt(0)->Time()) {
|
&& system_time() >= _EventAt(0)->Time()) {
|
||||||
Event *event = (Event*)fEvents.RemoveItem(0L);
|
Event *event = (Event*)fEvents.RemoveItem(0L);
|
||||||
Unlock();
|
Unlock();
|
||||||
bool autoDeleteEvent = event->IsAutoDelete();
|
bool autoDeleteEvent = event->IsAutoDelete();
|
||||||
bool deleteEvent = event->Do() || autoDeleteEvent;
|
bool deleteEvent = event->Do(this) || autoDeleteEvent;
|
||||||
if (deleteEvent)
|
if (deleteEvent)
|
||||||
delete event;
|
delete event;
|
||||||
}
|
}
|
||||||
@@ -203,8 +386,14 @@ EventQueue::_EventLooper()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// _Reschedule
|
// _Reschedule
|
||||||
//
|
/*! \brief To be called, when an event has been added or removed.
|
||||||
// PRE: The object must be locked.
|
|
||||||
|
Checks whether the queue's thread has to recalculate the time when it
|
||||||
|
needs to wake up to execute the next event, and signals the thread to
|
||||||
|
do so, if necessary.
|
||||||
|
|
||||||
|
\note The object must be locked when this method is invoked.
|
||||||
|
*/
|
||||||
void
|
void
|
||||||
EventQueue::_Reschedule()
|
EventQueue::_Reschedule()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
// File Name: EventQueue.h
|
// File Name: EventQueue.h
|
||||||
// Author: Ingo Weinhold ([email protected])
|
// Author: Ingo Weinhold ([email protected])
|
||||||
// YellowBites (http://www.yellowbites.com)
|
// YellowBites (http://www.yellowbites.com)
|
||||||
// Description: A class featuring providing a mechanism to do events at
|
// Description: A class providing a mechanism for executing events at
|
||||||
// specified times.
|
// specified times.
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
@@ -50,7 +50,10 @@ public:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool _AddEvent(Event *event);
|
bool _AddEvent(Event *event);
|
||||||
|
bool _RemoveEvent(Event *event);
|
||||||
Event *_EventAt(int32 index) const;
|
Event *_EventAt(int32 index) const;
|
||||||
|
int32 _IndexOfEvent(Event *event) const;
|
||||||
|
int32 _FindInsertionIndex(bigtime_t time) const;
|
||||||
|
|
||||||
static int32 _EventLooperEntry(void *data);
|
static int32 _EventLooperEntry(void *data);
|
||||||
int32 _EventLooper();
|
int32 _EventLooper();
|
||||||
@@ -61,6 +64,7 @@ public:
|
|||||||
sem_id fLooperControl;
|
sem_id fLooperControl;
|
||||||
volatile bigtime_t fNextEventTime;
|
volatile bigtime_t fNextEventTime;
|
||||||
status_t fStatus;
|
status_t fStatus;
|
||||||
|
volatile bool fTerminating;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // EVENT_QUEUE_H
|
#endif // EVENT_QUEUE_H
|
||||||
|
|||||||
Reference in New Issue
Block a user