BTimedEventQueue: Rewrite from scratch, avoiding malloc().
The previous implementation allocated and freed event objects on every insertion and removal using malloc()/free(). It was also licensed under a "distributions in binary form must reproduce ... in the binary" license, which is more restrictive than the MIT license that we prefer. So, this is a rewrite from scratch. It uses the standard DoublyLinkedList<> rather than rolling its own, and manages a free list of event queue objects rather than hitting malloc() all the time. It only frees chunks on destruction, though, but that hopefully won't be an issue anyway. All tests from the TimedEventQueueTest still pass, and media playback still works as before. Change-Id: Ia940b6176f8051ae4823b75acd305ded8783d1e0 Reviewed-on: https://review.haiku-os.org/c/haiku/+/8594 Tested-by: Commit checker robot <[email protected]> Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
committed by
waddlesplash
parent
2d54103521
commit
4055af5143
@@ -1,24 +1,20 @@
|
||||
/*
|
||||
* Copyright 2009, Haiku, Inc. All rights reserved.
|
||||
* Copyright 2009-2024, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
#ifndef _TIMED_EVENT_QUEUE_H
|
||||
#define _TIMED_EVENT_QUEUE_H
|
||||
|
||||
#include <MediaDefs.h>
|
||||
|
||||
struct _event_queue_imp;
|
||||
#include <MediaDefs.h>
|
||||
|
||||
|
||||
struct media_timed_event {
|
||||
media_timed_event();
|
||||
media_timed_event(bigtime_t inTime,
|
||||
int32 inType);
|
||||
media_timed_event(bigtime_t inTime,
|
||||
int32 inType, void*inPointer,
|
||||
uint32 inCleanup);
|
||||
media_timed_event(
|
||||
bigtime_t inTime, int32 inType,
|
||||
media_timed_event(bigtime_t inTime, int32 inType);
|
||||
media_timed_event(bigtime_t inTime, int32 inType,
|
||||
void* inPointer, uint32 inCleanup);
|
||||
media_timed_event(bigtime_t inTime, int32 inType,
|
||||
void* inPointer, uint32 inCleanup,
|
||||
int32 inData, int64 inBigdata,
|
||||
const char* inUserData, size_t dataSize = 0);
|
||||
@@ -28,9 +24,9 @@ struct media_timed_event {
|
||||
|
||||
~media_timed_event();
|
||||
|
||||
// TODO: Should this not return "media_timed_event&" ?!
|
||||
void operator=(const media_timed_event& other);
|
||||
|
||||
public:
|
||||
bigtime_t event_time;
|
||||
int32 type;
|
||||
void* pointer;
|
||||
@@ -39,6 +35,7 @@ struct media_timed_event {
|
||||
int64 bigdata;
|
||||
char user_data[64];
|
||||
|
||||
private:
|
||||
uint32 _reserved_media_timed_event_[8];
|
||||
};
|
||||
|
||||
@@ -49,15 +46,16 @@ bool operator<(const media_timed_event& a, const media_timed_event& b);
|
||||
bool operator>(const media_timed_event& a, const media_timed_event&b);
|
||||
|
||||
|
||||
/*! A priority queue for holding media_timed_events. Sorts by increasing time,
|
||||
so events near the front of the queue are to occur earlier.
|
||||
*/
|
||||
namespace BPrivate {
|
||||
class TimedEventQueueData;
|
||||
};
|
||||
|
||||
class BTimedEventQueue {
|
||||
public:
|
||||
|
||||
enum event_type {
|
||||
B_NO_EVENT = -1, // Pushing this type will always fail.
|
||||
B_ANY_EVENT = 0, // Pushing this type will always fail.
|
||||
B_NO_EVENT = -1,
|
||||
B_ANY_EVENT = 0,
|
||||
|
||||
B_START,
|
||||
B_STOP,
|
||||
B_SEEK,
|
||||
@@ -68,16 +66,13 @@ public:
|
||||
B_HARDWARE,
|
||||
B_PARAMETER,
|
||||
|
||||
// User defined events are greater than this value.
|
||||
B_USER_EVENT = 0x4000
|
||||
};
|
||||
|
||||
enum cleanup_flag {
|
||||
B_NO_CLEANUP = 0,
|
||||
B_RECYCLE_BUFFER, // Specifies to recycle buffers handled by
|
||||
// BTimedEventQueue.
|
||||
B_EXPIRE_TIMER, // Specifies to call TimerExpired() on the
|
||||
// event->data.
|
||||
B_RECYCLE_BUFFER,
|
||||
B_EXPIRE_TIMER,
|
||||
B_USER_CLEANUP = 0x4000
|
||||
};
|
||||
|
||||
@@ -88,13 +83,18 @@ public:
|
||||
B_AFTER_TIME
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
void* operator new(size_t size);
|
||||
void operator delete(void* ptr, size_t size);
|
||||
|
||||
BTimedEventQueue();
|
||||
virtual ~BTimedEventQueue();
|
||||
|
||||
typedef void (*cleanup_hook)(const media_timed_event* event,
|
||||
void* context);
|
||||
void SetCleanupHook(cleanup_hook hook,
|
||||
void* context);
|
||||
|
||||
status_t AddEvent(const media_timed_event& event);
|
||||
status_t RemoveEvent(const media_timed_event* event);
|
||||
status_t RemoveFirstEvent(
|
||||
@@ -103,7 +103,6 @@ public:
|
||||
bool HasEvents() const;
|
||||
int32 EventCount() const;
|
||||
|
||||
|
||||
const media_timed_event* FirstEvent() const;
|
||||
bigtime_t FirstEventTime() const;
|
||||
const media_timed_event* LastEvent() const;
|
||||
@@ -114,12 +113,6 @@ public:
|
||||
bool inclusive = true,
|
||||
int32 eventType = B_ANY_EVENT);
|
||||
|
||||
|
||||
// Queue manipulation
|
||||
// Call DoForEach to perform a function on each event in the queue.
|
||||
// Return an appropriate status defining an action to take for that event.
|
||||
// DoForEach is an atomic operation ensuring the consistency of the queue
|
||||
// during the call.
|
||||
enum queue_action {
|
||||
B_DONE = -1,
|
||||
B_NO_ACTION = 0,
|
||||
@@ -135,14 +128,6 @@ public:
|
||||
bool inclusive = true,
|
||||
int32 eventType = B_ANY_EVENT);
|
||||
|
||||
|
||||
// Flushing events
|
||||
// The cleanup hook is called when events are flushed from the queue and
|
||||
// the cleanup is not B_NO_CLEANUP or B_RECYCLE_BUFFER.
|
||||
typedef void (*cleanup_hook)(const media_timed_event* event,
|
||||
void* context);
|
||||
void SetCleanupHook(cleanup_hook hook,
|
||||
void* context);
|
||||
status_t FlushEvents(bigtime_t eventTime,
|
||||
time_direction direction,
|
||||
bool inclusive = true,
|
||||
@@ -150,39 +135,45 @@ public:
|
||||
|
||||
private:
|
||||
// FBC padding and forbidden methods
|
||||
BTimedEventQueue(
|
||||
const BTimedEventQueue& other);
|
||||
BTimedEventQueue& operator=(const BTimedEventQueue& other);
|
||||
virtual void _ReservedTimedEventQueue0();
|
||||
virtual void _ReservedTimedEventQueue1();
|
||||
virtual void _ReservedTimedEventQueue2();
|
||||
virtual void _ReservedTimedEventQueue3();
|
||||
virtual void _ReservedTimedEventQueue4();
|
||||
virtual void _ReservedTimedEventQueue5();
|
||||
virtual void _ReservedTimedEventQueue6();
|
||||
virtual void _ReservedTimedEventQueue7();
|
||||
virtual void _ReservedTimedEventQueue8();
|
||||
virtual void _ReservedTimedEventQueue9();
|
||||
virtual void _ReservedTimedEventQueue10();
|
||||
virtual void _ReservedTimedEventQueue11();
|
||||
virtual void _ReservedTimedEventQueue12();
|
||||
virtual void _ReservedTimedEventQueue13();
|
||||
virtual void _ReservedTimedEventQueue14();
|
||||
virtual void _ReservedTimedEventQueue15();
|
||||
virtual void _ReservedTimedEventQueue16();
|
||||
virtual void _ReservedTimedEventQueue17();
|
||||
virtual void _ReservedTimedEventQueue18();
|
||||
virtual void _ReservedTimedEventQueue19();
|
||||
virtual void _ReservedTimedEventQueue20();
|
||||
virtual void _ReservedTimedEventQueue21();
|
||||
virtual void _ReservedTimedEventQueue22();
|
||||
virtual void _ReservedTimedEventQueue23();
|
||||
|
||||
virtual status_t _Reserved_BTimedEventQueue_0(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_1(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_2(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_3(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_4(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_5(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_6(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_7(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_8(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_9(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_10(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_11(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_12(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_13(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_14(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_15(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_16(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_17(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_18(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_19(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_20(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_21(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_22(void*, ...);
|
||||
virtual status_t _Reserved_BTimedEventQueue_23(void*, ...);
|
||||
BTimedEventQueue(const BTimedEventQueue&);
|
||||
BTimedEventQueue& operator=(const BTimedEventQueue&);
|
||||
|
||||
private:
|
||||
_event_queue_imp* fImp;
|
||||
static int _Match(const media_timed_event& event,
|
||||
bigtime_t eventTime,
|
||||
time_direction direction,
|
||||
bool inclusive, int32 eventType);
|
||||
|
||||
uint32 _reserved_timed_event_queue_[6];
|
||||
private:
|
||||
BPrivate::TimedEventQueueData* fData;
|
||||
|
||||
uint32 _reserved[6];
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@ SubDir HAIKU_TOP src kits media ;
|
||||
|
||||
AddResources libmedia.so : libmedia.rdef ;
|
||||
|
||||
UsePrivateHeaders app media shared ;
|
||||
UsePrivateHeaders app media kernel shared ;
|
||||
UsePrivateHeaders [ FDirName media experimental ] ;
|
||||
UsePrivateHeaders [ FDirName interface ] ;
|
||||
|
||||
@@ -70,7 +70,6 @@ for architectureObject in [ MultiArchSubDirSetup ] {
|
||||
request_data.cpp
|
||||
SharedBufferList.cpp
|
||||
TrackReader.cpp
|
||||
TimedEventQueuePrivate.cpp
|
||||
TimeSourceObject.cpp
|
||||
TimeSourceObjectManager.cpp
|
||||
SoundPlayNode.cpp
|
||||
|
||||
+422
-118
@@ -1,17 +1,19 @@
|
||||
/***********************************************************************
|
||||
* AUTHOR: Marcus Overhagen
|
||||
* FILE: TimedEventQueue.cpp
|
||||
* DESCR: used by BMediaEventLooper
|
||||
***********************************************************************/
|
||||
/*
|
||||
* Copyright 2024, Haiku, Inc. All rights reserved.
|
||||
* Distributed under the terms of the MIT License.
|
||||
*/
|
||||
|
||||
#include <TimedEventQueue.h>
|
||||
#include <string.h>
|
||||
#include "TimedEventQueuePrivate.h"
|
||||
#include "MediaDebug.h"
|
||||
|
||||
/*************************************************************
|
||||
* struct media_timed_event
|
||||
*************************************************************/
|
||||
#include <Autolock.h>
|
||||
#include <Buffer.h>
|
||||
#include <MediaDebug.h>
|
||||
#include <InterfaceDefs.h>
|
||||
#include <util/DoublyLinkedList.h>
|
||||
|
||||
|
||||
// #pragma mark - media_timed_event
|
||||
|
||||
|
||||
media_timed_event::media_timed_event()
|
||||
{
|
||||
@@ -20,23 +22,22 @@ media_timed_event::media_timed_event()
|
||||
}
|
||||
|
||||
|
||||
media_timed_event::media_timed_event(bigtime_t inTime,
|
||||
int32 inType)
|
||||
media_timed_event::media_timed_event(bigtime_t inTime, int32 inType)
|
||||
{
|
||||
CALLED();
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
event_time = inTime;
|
||||
type = inType;
|
||||
}
|
||||
|
||||
|
||||
media_timed_event::media_timed_event(bigtime_t inTime,
|
||||
int32 inType,
|
||||
void *inPointer,
|
||||
uint32 inCleanup)
|
||||
media_timed_event::media_timed_event(bigtime_t inTime, int32 inType,
|
||||
void* inPointer, uint32 inCleanup)
|
||||
{
|
||||
CALLED();
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
event_time = inTime;
|
||||
type = inType;
|
||||
pointer = inPointer;
|
||||
@@ -44,24 +45,22 @@ media_timed_event::media_timed_event(bigtime_t inTime,
|
||||
}
|
||||
|
||||
|
||||
media_timed_event::media_timed_event(bigtime_t inTime,
|
||||
int32 inType,
|
||||
void *inPointer,
|
||||
uint32 inCleanup,
|
||||
int32 inData,
|
||||
int64 inBigdata,
|
||||
const char *inUserData,
|
||||
size_t dataSize)
|
||||
media_timed_event::media_timed_event(bigtime_t inTime, int32 inType,
|
||||
void* inPointer, uint32 inCleanup,
|
||||
int32 inData, int64 inBigdata,
|
||||
const char* inUserData, size_t dataSize)
|
||||
{
|
||||
CALLED();
|
||||
memset(this, 0, sizeof(*this));
|
||||
|
||||
event_time = inTime;
|
||||
type = inType;
|
||||
pointer = inPointer;
|
||||
cleanup = inCleanup;
|
||||
data = inData;
|
||||
bigdata = inBigdata;
|
||||
memcpy(user_data,inUserData,min_c(sizeof(media_timed_event::user_data),dataSize));
|
||||
memcpy(user_data, inUserData,
|
||||
min_c(sizeof(media_timed_event::user_data), dataSize));
|
||||
}
|
||||
|
||||
|
||||
@@ -85,58 +84,120 @@ media_timed_event::~media_timed_event()
|
||||
CALLED();
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* global operators
|
||||
*************************************************************/
|
||||
|
||||
bool operator==(const media_timed_event & a, const media_timed_event & b)
|
||||
// #pragma mark - media_timed_event global operators
|
||||
|
||||
|
||||
bool
|
||||
operator==(const media_timed_event& a, const media_timed_event& b)
|
||||
{
|
||||
CALLED();
|
||||
return (0 == memcmp(&a,&b,sizeof(media_timed_event)));
|
||||
return (memcmp(&a, &b, sizeof(media_timed_event)) == 0);
|
||||
}
|
||||
|
||||
bool operator!=(const media_timed_event & a, const media_timed_event & b)
|
||||
|
||||
bool
|
||||
operator!=(const media_timed_event& a, const media_timed_event& b)
|
||||
{
|
||||
CALLED();
|
||||
return (0 != memcmp(&a,&b,sizeof(media_timed_event)));
|
||||
return (memcmp(&a, &b, sizeof(media_timed_event)) != 0);
|
||||
}
|
||||
|
||||
bool operator<(const media_timed_event & a, const media_timed_event & b)
|
||||
|
||||
bool
|
||||
operator<(const media_timed_event& a, const media_timed_event& b)
|
||||
{
|
||||
CALLED();
|
||||
return a.event_time < b.event_time;
|
||||
}
|
||||
|
||||
bool operator>(const media_timed_event & a, const media_timed_event &b)
|
||||
|
||||
bool
|
||||
operator>(const media_timed_event& a, const media_timed_event& b)
|
||||
{
|
||||
CALLED();
|
||||
return a.event_time > b.event_time;
|
||||
}
|
||||
|
||||
|
||||
/*************************************************************
|
||||
* public BTimedEventQueue
|
||||
*************************************************************/
|
||||
// #pragma mark - BTimedEventQueue
|
||||
|
||||
|
||||
void *
|
||||
BTimedEventQueue::operator new(size_t s)
|
||||
struct queue_entry : public DoublyLinkedListLinkImpl<queue_entry> {
|
||||
media_timed_event event;
|
||||
};
|
||||
typedef DoublyLinkedList<queue_entry> QueueEntryList;
|
||||
|
||||
|
||||
class BPrivate::TimedEventQueueData {
|
||||
public:
|
||||
TimedEventQueueData()
|
||||
:
|
||||
fLock("BTimedEventQueue"),
|
||||
fEntryAllocationLock("BTimedEventQueue entry allocation")
|
||||
{
|
||||
CALLED();
|
||||
return ::operator new(s);
|
||||
fEventCount = 0;
|
||||
fCleanupHook = NULL;
|
||||
fCleanupHookContext = NULL;
|
||||
|
||||
for (size_t i = 0; i < B_COUNT_OF(fInlineEntries); i++)
|
||||
fFreeEntries.Add(&fInlineEntries[i]);
|
||||
}
|
||||
~TimedEventQueueData()
|
||||
{
|
||||
while (queue_entry* chunk = fChunkHeads.RemoveHead())
|
||||
free(chunk);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTimedEventQueue::operator delete(void *p, size_t s)
|
||||
queue_entry* AllocateEntry()
|
||||
{
|
||||
CALLED();
|
||||
return ::operator delete(p);
|
||||
BAutolock locker(fEntryAllocationLock);
|
||||
if (fFreeEntries.Head() != NULL)
|
||||
return fFreeEntries.RemoveHead();
|
||||
|
||||
// We need a new chunk.
|
||||
const size_t chunkSize = B_PAGE_SIZE;
|
||||
queue_entry* newEntries = (queue_entry*)malloc(chunkSize);
|
||||
fChunkHeads.Add(&newEntries[0]);
|
||||
for (size_t i = 1; i < (chunkSize / sizeof(queue_entry)); i++)
|
||||
fFreeEntries.Add(&newEntries[i]);
|
||||
|
||||
return fFreeEntries.RemoveHead();
|
||||
}
|
||||
|
||||
void FreeEntry(queue_entry* entry)
|
||||
{
|
||||
BAutolock locker(fEntryAllocationLock);
|
||||
fFreeEntries.Add(entry);
|
||||
|
||||
BTimedEventQueue::BTimedEventQueue() :
|
||||
fImp(new _event_queue_imp)
|
||||
// TODO: Chunks are currently only freed in the destructor.
|
||||
// (Is that a problem? They're probably rarely used, anyway.)
|
||||
}
|
||||
|
||||
status_t AddEntry(queue_entry* newEntry);
|
||||
void RemoveEntry(queue_entry* newEntry);
|
||||
void CleanupAndFree(queue_entry* entry);
|
||||
|
||||
public:
|
||||
BLocker fLock;
|
||||
QueueEntryList fEvents;
|
||||
int32 fEventCount;
|
||||
|
||||
BTimedEventQueue::cleanup_hook fCleanupHook;
|
||||
void* fCleanupHookContext;
|
||||
|
||||
private:
|
||||
BLocker fEntryAllocationLock;
|
||||
QueueEntryList fFreeEntries;
|
||||
QueueEntryList fChunkHeads;
|
||||
queue_entry fInlineEntries[8];
|
||||
};
|
||||
|
||||
using BPrivate::TimedEventQueueData;
|
||||
|
||||
|
||||
BTimedEventQueue::BTimedEventQueue()
|
||||
: fData(new TimedEventQueueData)
|
||||
{
|
||||
CALLED();
|
||||
}
|
||||
@@ -145,7 +206,7 @@ BTimedEventQueue::BTimedEventQueue() :
|
||||
BTimedEventQueue::~BTimedEventQueue()
|
||||
{
|
||||
CALLED();
|
||||
delete fImp;
|
||||
delete fData;
|
||||
}
|
||||
|
||||
|
||||
@@ -153,7 +214,18 @@ status_t
|
||||
BTimedEventQueue::AddEvent(const media_timed_event& event)
|
||||
{
|
||||
CALLED();
|
||||
return fImp->AddEvent(event);
|
||||
|
||||
if (event.type < B_START)
|
||||
return B_BAD_VALUE;
|
||||
|
||||
queue_entry* newEntry = fData->AllocateEntry();
|
||||
if (newEntry == NULL)
|
||||
return B_NO_MEMORY;
|
||||
|
||||
newEntry->event = event;
|
||||
|
||||
BAutolock locker(fData->fLock);
|
||||
return fData->AddEntry(newEntry);
|
||||
}
|
||||
|
||||
|
||||
@@ -161,15 +233,130 @@ status_t
|
||||
BTimedEventQueue::RemoveEvent(const media_timed_event* event)
|
||||
{
|
||||
CALLED();
|
||||
return fImp->RemoveEvent(event);
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
QueueEntryList::Iterator it = fData->fEvents.GetIterator();
|
||||
while (queue_entry* entry = it.Next()) {
|
||||
if (entry->event != *event)
|
||||
continue;
|
||||
|
||||
fData->RemoveEntry(entry);
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
// No cleanup.
|
||||
fData->FreeEntry(entry);
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BTimedEventQueue::RemoveFirstEvent(media_timed_event *outEvent)
|
||||
BTimedEventQueue::RemoveFirstEvent(media_timed_event* _event)
|
||||
{
|
||||
CALLED();
|
||||
return fImp->RemoveFirstEvent(outEvent);
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
if (fData->fEventCount == 0)
|
||||
return B_ERROR;
|
||||
|
||||
queue_entry* entry = fData->fEvents.Head();
|
||||
fData->RemoveEntry(entry);
|
||||
|
||||
locker.Unlock();
|
||||
|
||||
if (_event != NULL) {
|
||||
// No cleanup.
|
||||
*_event = entry->event;
|
||||
fData->FreeEntry(entry);
|
||||
} else {
|
||||
fData->CleanupAndFree(entry);
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
TimedEventQueueData::AddEntry(queue_entry* newEntry)
|
||||
{
|
||||
ASSERT(fLock.IsLocked());
|
||||
|
||||
if (fEvents.IsEmpty()) {
|
||||
fEvents.Add(newEntry);
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
if (fEvents.First()->event.event_time > newEntry->event.event_time) {
|
||||
fEvents.Add(newEntry, false);
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
QueueEntryList::ReverseIterator it = fEvents.GetReverseIterator();
|
||||
while (queue_entry* entry = it.Next()) {
|
||||
if (newEntry->event.event_time < entry->event.event_time)
|
||||
continue;
|
||||
|
||||
// Insert the new event after this entry.
|
||||
fEvents.InsertAfter(entry, newEntry);
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
debugger("BTimedEventQueue::AddEvent: Invalid queue!");
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TimedEventQueueData::RemoveEntry(queue_entry* entry)
|
||||
{
|
||||
ASSERT(fLock.IsLocked());
|
||||
|
||||
fEvents.Remove(entry);
|
||||
fEventCount--;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
TimedEventQueueData::CleanupAndFree(queue_entry* entry)
|
||||
{
|
||||
uint32 cleanup = entry->event.cleanup;
|
||||
if (cleanup == B_DELETE) {
|
||||
// B_DELETE is a keyboard code, but the Be Book indicates it's a valid
|
||||
// cleanup value. (Early sample code may have used it too.)
|
||||
cleanup = BTimedEventQueue::B_USER_CLEANUP;
|
||||
}
|
||||
|
||||
if (cleanup == BTimedEventQueue::B_NO_CLEANUP) {
|
||||
// Nothing to do.
|
||||
} else if (entry->event.type == BTimedEventQueue::B_HANDLE_BUFFER
|
||||
&& cleanup == BTimedEventQueue::B_RECYCLE_BUFFER) {
|
||||
(reinterpret_cast<BBuffer*>(entry->event.pointer))->Recycle();
|
||||
} else if (cleanup == BTimedEventQueue::B_EXPIRE_TIMER) {
|
||||
// TimerExpired() is invoked in BMediaEventLooper::DispatchEvent; nothing to do.
|
||||
} else if (cleanup >= BTimedEventQueue::B_USER_CLEANUP) {
|
||||
if (fCleanupHook != NULL)
|
||||
(*fCleanupHook)(&entry->event, fCleanupHookContext);
|
||||
} else {
|
||||
ERROR("BTimedEventQueue: Unhandled cleanup! (type %" B_PRId32 ", "
|
||||
"cleanup %" B_PRId32 ")\n", entry->event.type, entry->event.cleanup);
|
||||
}
|
||||
|
||||
FreeEntry(entry);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTimedEventQueue::SetCleanupHook(cleanup_hook hook, void* context)
|
||||
{
|
||||
CALLED();
|
||||
|
||||
BAutolock lock(fData->fLock);
|
||||
fData->fCleanupHook = hook;
|
||||
fData->fCleanupHookContext = context;
|
||||
}
|
||||
|
||||
|
||||
@@ -177,7 +364,9 @@ bool
|
||||
BTimedEventQueue::HasEvents() const
|
||||
{
|
||||
CALLED();
|
||||
return fImp->HasEvents();
|
||||
|
||||
BAutolock locker(fData->fLock);
|
||||
return fData->fEventCount != 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -185,7 +374,9 @@ int32
|
||||
BTimedEventQueue::EventCount() const
|
||||
{
|
||||
CALLED();
|
||||
return fImp->EventCount();
|
||||
|
||||
BAutolock locker(fData->fLock);
|
||||
return fData->fEventCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -193,7 +384,12 @@ const media_timed_event *
|
||||
BTimedEventQueue::FirstEvent() const
|
||||
{
|
||||
CALLED();
|
||||
return fImp->FirstEvent();
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
queue_entry* entry = fData->fEvents.First();
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
return &entry->event;
|
||||
}
|
||||
|
||||
|
||||
@@ -201,7 +397,12 @@ bigtime_t
|
||||
BTimedEventQueue::FirstEventTime() const
|
||||
{
|
||||
CALLED();
|
||||
return fImp->FirstEventTime();
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
queue_entry* entry = fData->fEvents.First();
|
||||
if (entry == NULL)
|
||||
return B_INFINITE_TIMEOUT;
|
||||
return entry->event.event_time;
|
||||
}
|
||||
|
||||
|
||||
@@ -209,7 +410,12 @@ const media_timed_event *
|
||||
BTimedEventQueue::LastEvent() const
|
||||
{
|
||||
CALLED();
|
||||
return fImp->LastEvent();
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
queue_entry* entry = fData->fEvents.Last();
|
||||
if (entry == NULL)
|
||||
return NULL;
|
||||
return &entry->event;
|
||||
}
|
||||
|
||||
|
||||
@@ -217,84 +423,182 @@ bigtime_t
|
||||
BTimedEventQueue::LastEventTime() const
|
||||
{
|
||||
CALLED();
|
||||
return fImp->LastEventTime();
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
queue_entry* entry = fData->fEvents.Last();
|
||||
if (entry == NULL)
|
||||
return B_INFINITE_TIMEOUT;
|
||||
return entry->event.event_time;
|
||||
}
|
||||
|
||||
|
||||
const media_timed_event*
|
||||
BTimedEventQueue::FindFirstMatch(bigtime_t eventTime,
|
||||
time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType)
|
||||
time_direction direction, bool inclusive, int32 eventType)
|
||||
{
|
||||
CALLED();
|
||||
return fImp->FindFirstMatch(eventTime, direction, inclusive, eventType);
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
QueueEntryList::Iterator it = fData->fEvents.GetIterator();
|
||||
while (queue_entry* entry = it.Next()) {
|
||||
int match = _Match(entry->event, eventTime, direction, inclusive, eventType);
|
||||
if (match == B_DONE)
|
||||
break;
|
||||
if (match == B_NO_ACTION)
|
||||
continue;
|
||||
|
||||
return &entry->event;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BTimedEventQueue::DoForEach(for_each_hook hook,
|
||||
void *context,
|
||||
bigtime_t eventTime,
|
||||
time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType)
|
||||
BTimedEventQueue::DoForEach(for_each_hook hook, void* context,
|
||||
bigtime_t eventTime, time_direction direction,
|
||||
bool inclusive, int32 eventType)
|
||||
{
|
||||
CALLED();
|
||||
return fImp->DoForEach(hook, context, eventTime, direction, inclusive, eventType);
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
bool resort = false;
|
||||
|
||||
QueueEntryList::Iterator it = fData->fEvents.GetIterator();
|
||||
while (queue_entry* entry = it.Next()) {
|
||||
int match = _Match(entry->event, eventTime, direction, inclusive, eventType);
|
||||
if (match == B_DONE)
|
||||
break;
|
||||
if (match == B_NO_ACTION)
|
||||
continue;
|
||||
|
||||
queue_action action = hook(&entry->event, context);
|
||||
if (action == B_DONE)
|
||||
break;
|
||||
|
||||
switch (action) {
|
||||
case B_REMOVE_EVENT:
|
||||
fData->RemoveEntry(entry);
|
||||
fData->CleanupAndFree(entry);
|
||||
break;
|
||||
|
||||
case B_RESORT_QUEUE:
|
||||
resort = true;
|
||||
break;
|
||||
|
||||
case B_NO_ACTION:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (resort) {
|
||||
QueueEntryList entries;
|
||||
entries.MoveFrom(&fData->fEvents);
|
||||
fData->fEventCount = 0;
|
||||
|
||||
while (queue_entry* entry = entries.RemoveHead())
|
||||
fData->AddEntry(entry);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BTimedEventQueue::FlushEvents(bigtime_t eventTime, time_direction direction,
|
||||
bool inclusive, int32 eventType)
|
||||
{
|
||||
CALLED();
|
||||
BAutolock locker(fData->fLock);
|
||||
|
||||
QueueEntryList::Iterator it = fData->fEvents.GetIterator();
|
||||
while (queue_entry* entry = it.Next()) {
|
||||
int match = _Match(entry->event, eventTime, direction, inclusive, eventType);
|
||||
if (match == B_DONE)
|
||||
break;
|
||||
if (match == B_NO_ACTION)
|
||||
continue;
|
||||
|
||||
fData->RemoveEntry(entry);
|
||||
fData->CleanupAndFree(entry);
|
||||
}
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
BTimedEventQueue::_Match(const media_timed_event& event,
|
||||
bigtime_t eventTime, time_direction direction,
|
||||
bool inclusive, int32 eventType)
|
||||
{
|
||||
if (direction == B_ALWAYS) {
|
||||
// Nothing to check.
|
||||
} else if (direction == B_BEFORE_TIME) {
|
||||
if (event.event_time > eventTime)
|
||||
return B_DONE;
|
||||
if (event.event_time == eventTime && !inclusive)
|
||||
return B_DONE;
|
||||
} else if (direction == B_AT_TIME) {
|
||||
if (event.event_time > eventTime)
|
||||
return B_DONE;
|
||||
if (event.event_time != eventTime)
|
||||
return B_NO_ACTION;
|
||||
} else if (direction == B_AFTER_TIME) {
|
||||
if (event.event_time < eventTime)
|
||||
return B_NO_ACTION;
|
||||
if (event.event_time == eventTime && !inclusive)
|
||||
return B_NO_ACTION;
|
||||
}
|
||||
|
||||
if (eventType != B_ANY_EVENT && eventType != event.type)
|
||||
return B_NO_ACTION;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
// #pragma mark - C++ binary compatibility
|
||||
|
||||
|
||||
void*
|
||||
BTimedEventQueue::operator new(size_t size)
|
||||
{
|
||||
CALLED();
|
||||
return ::operator new(size);
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
BTimedEventQueue::SetCleanupHook(cleanup_hook hook,
|
||||
void *context)
|
||||
BTimedEventQueue::operator delete(void* ptr, size_t s)
|
||||
{
|
||||
CALLED();
|
||||
fImp->SetCleanupHook(hook, context);
|
||||
return ::operator delete(ptr);
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
BTimedEventQueue::FlushEvents(bigtime_t eventTime,
|
||||
time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType)
|
||||
{
|
||||
CALLED();
|
||||
return fImp->FlushEvents(eventTime, direction, inclusive, eventType);
|
||||
}
|
||||
|
||||
/*************************************************************
|
||||
* private BTimedEventQueue
|
||||
*************************************************************/
|
||||
|
||||
/*
|
||||
// unimplemented
|
||||
BTimedEventQueue::BTimedEventQueue(const BTimedEventQueue &other)
|
||||
BTimedEventQueue &BTimedEventQueue::operator=(const BTimedEventQueue &other)
|
||||
*/
|
||||
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_0(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_1(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_2(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_3(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_4(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_5(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_6(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_7(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_8(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_9(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_10(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_11(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_12(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_13(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_14(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_15(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_16(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_17(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_18(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_19(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_20(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_21(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_22(void *, ...) { return B_ERROR; }
|
||||
status_t BTimedEventQueue::_Reserved_BTimedEventQueue_23(void *, ...) { return B_ERROR; }
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue0() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue1() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue2() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue3() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue4() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue5() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue6() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue7() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue8() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue9() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue10() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue11() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue12() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue13() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue14() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue15() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue16() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue17() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue18() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue19() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue20() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue21() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue22() {}
|
||||
void BTimedEventQueue::_ReservedTimedEventQueue23() {}
|
||||
|
||||
@@ -1,631 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2002, 2003 Marcus Overhagen <[email protected]>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining
|
||||
* a copy of this software and associated documentation files or portions
|
||||
* thereof (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:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice
|
||||
* in the binary, as well as this list of conditions and the following
|
||||
* disclaimer in the documentation and/or other materials provided with
|
||||
* the distribution.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
*/
|
||||
|
||||
/* Implements _event_queue_imp used by BTimedEventQueue, not thread save!
|
||||
*/
|
||||
#include <string.h>
|
||||
|
||||
#include <Autolock.h>
|
||||
#include <Buffer.h>
|
||||
#include <InterfaceDefs.h> //defines B_DELETE
|
||||
#include <TimedEventQueue.h>
|
||||
#include <TimeSource.h>
|
||||
|
||||
#include "TimedEventQueuePrivate.h"
|
||||
|
||||
#include "Debug.h"
|
||||
#include "MediaDebug.h"
|
||||
|
||||
_event_queue_imp::_event_queue_imp() :
|
||||
fLock(new BLocker("BTimedEventQueue locker")),
|
||||
fEventCount(0),
|
||||
fFirstEntry(NULL),
|
||||
fLastEntry(NULL),
|
||||
fCleanupHookContext(NULL),
|
||||
fCleanupHook(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
_event_queue_imp::~_event_queue_imp()
|
||||
{
|
||||
event_queue_entry *entry;
|
||||
entry = fFirstEntry;
|
||||
while (entry) {
|
||||
event_queue_entry *deleteme;
|
||||
deleteme = entry;
|
||||
entry = entry->next;
|
||||
delete deleteme;
|
||||
}
|
||||
delete fLock;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_event_queue_imp::AddEvent(const media_timed_event &event)
|
||||
{
|
||||
BAutolock lock(fLock);
|
||||
|
||||
// printf(" adding %12Ld at %12Ld\n", event.event_time, system_time());
|
||||
|
||||
if (event.type <= 0) {
|
||||
return B_BAD_VALUE;
|
||||
}
|
||||
|
||||
//create a new queue
|
||||
if (fFirstEntry == NULL) {
|
||||
ASSERT(fEventCount == 0);
|
||||
ASSERT(fLastEntry == NULL);
|
||||
fFirstEntry = fLastEntry = new event_queue_entry;
|
||||
fFirstEntry->event = event;
|
||||
fFirstEntry->prev = NULL;
|
||||
fFirstEntry->next = NULL;
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
//insert at queue begin
|
||||
if (fFirstEntry->event.event_time >= event.event_time) {
|
||||
event_queue_entry *newentry = new event_queue_entry;
|
||||
newentry->event = event;
|
||||
newentry->prev = NULL;
|
||||
newentry->next = fFirstEntry;
|
||||
fFirstEntry->prev = newentry;
|
||||
fFirstEntry = newentry;
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
//insert at queue end
|
||||
if (fLastEntry->event.event_time <= event.event_time) {
|
||||
event_queue_entry *newentry = new event_queue_entry;
|
||||
newentry->event = event;
|
||||
newentry->prev = fLastEntry;
|
||||
newentry->next = NULL;
|
||||
fLastEntry->next = newentry;
|
||||
fLastEntry = newentry;
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
//insert into the queue
|
||||
for (event_queue_entry *entry = fLastEntry; entry; entry = entry->prev) {
|
||||
if (entry->event.event_time <= event.event_time) {
|
||||
//insert after entry
|
||||
event_queue_entry *newentry = new event_queue_entry;
|
||||
newentry->event = event;
|
||||
newentry->prev = entry;
|
||||
newentry->next = entry->next;
|
||||
(entry->next)->prev = newentry;
|
||||
entry->next = newentry;
|
||||
fEventCount++;
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
debugger("_event_queue_imp::AddEvent failed, should not be here\n");
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_event_queue_imp::RemoveEvent(const media_timed_event *event)
|
||||
{
|
||||
BAutolock lock(fLock);
|
||||
|
||||
for (event_queue_entry *entry = fFirstEntry; entry; entry = entry->next) {
|
||||
if (entry->event == *event) {
|
||||
// No cleanup here
|
||||
RemoveEntry(entry);
|
||||
return B_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_event_queue_imp::RemoveFirstEvent(media_timed_event * outEvent)
|
||||
{
|
||||
BAutolock lock(fLock);
|
||||
|
||||
if (fFirstEntry == 0)
|
||||
return B_ERROR;
|
||||
|
||||
if (outEvent != 0) {
|
||||
// No cleanup here
|
||||
*outEvent = fFirstEntry->event;
|
||||
} else {
|
||||
CleanupEvent(&fFirstEntry->event);
|
||||
}
|
||||
|
||||
RemoveEntry(fFirstEntry);
|
||||
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
_event_queue_imp::HasEvents() const
|
||||
{
|
||||
return fEventCount != 0;
|
||||
}
|
||||
|
||||
|
||||
int32
|
||||
_event_queue_imp::EventCount() const
|
||||
{
|
||||
#if DEBUG > 1
|
||||
Dump();
|
||||
#endif
|
||||
return fEventCount;
|
||||
}
|
||||
|
||||
|
||||
const media_timed_event *
|
||||
_event_queue_imp::FirstEvent() const
|
||||
{
|
||||
return fFirstEntry ? &fFirstEntry->event : NULL;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
_event_queue_imp::FirstEventTime() const
|
||||
{
|
||||
return fFirstEntry ? fFirstEntry->event.event_time : B_INFINITE_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
const media_timed_event *
|
||||
_event_queue_imp::LastEvent() const
|
||||
{
|
||||
return fLastEntry ? &fLastEntry->event : NULL;
|
||||
}
|
||||
|
||||
|
||||
bigtime_t
|
||||
_event_queue_imp::LastEventTime() const
|
||||
{
|
||||
return fLastEntry ? fLastEntry->event.event_time : B_INFINITE_TIMEOUT;
|
||||
}
|
||||
|
||||
|
||||
const media_timed_event *
|
||||
_event_queue_imp::FindFirstMatch(bigtime_t eventTime,
|
||||
BTimedEventQueue::time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType)
|
||||
{
|
||||
event_queue_entry *end;
|
||||
event_queue_entry *entry;
|
||||
|
||||
BAutolock lock(fLock);
|
||||
|
||||
switch (direction) {
|
||||
case BTimedEventQueue::B_ALWAYS:
|
||||
for (entry = fFirstEntry; entry; entry = entry->next) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type)
|
||||
return &entry->event;
|
||||
}
|
||||
return NULL;
|
||||
|
||||
case BTimedEventQueue::B_BEFORE_TIME:
|
||||
end = GetEnd_BeforeTime(eventTime, inclusive);
|
||||
if (end == NULL)
|
||||
return NULL;
|
||||
end = end->next;
|
||||
for (entry = fFirstEntry; entry != end; entry = entry->next) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
return &entry->event;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
||||
case BTimedEventQueue::B_AT_TIME:
|
||||
{
|
||||
bool found_time = false;
|
||||
for (entry = fFirstEntry; entry; entry = entry->next) {
|
||||
if (eventTime == entry->event.event_time) {
|
||||
found_time = true;
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type)
|
||||
return &entry->event;
|
||||
} else if (found_time)
|
||||
return NULL;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
case BTimedEventQueue::B_AFTER_TIME:
|
||||
for (entry = GetStart_AfterTime(eventTime, inclusive); entry; entry = entry->next) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
return &entry->event;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_event_queue_imp::DoForEach(BTimedEventQueue::for_each_hook hook,
|
||||
void *context,
|
||||
bigtime_t eventTime,
|
||||
BTimedEventQueue::time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType)
|
||||
{
|
||||
event_queue_entry *end;
|
||||
event_queue_entry *entry;
|
||||
event_queue_entry *remove;
|
||||
BTimedEventQueue::queue_action action;
|
||||
|
||||
BAutolock lock(fLock);
|
||||
|
||||
switch (direction) {
|
||||
case BTimedEventQueue::B_ALWAYS:
|
||||
for (entry = fFirstEntry; entry; ) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
action = (*hook)(&entry->event, context);
|
||||
switch (action) {
|
||||
case BTimedEventQueue::B_DONE:
|
||||
return B_OK;
|
||||
case BTimedEventQueue::B_REMOVE_EVENT:
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
break;
|
||||
case BTimedEventQueue::B_NO_ACTION:
|
||||
case BTimedEventQueue::B_RESORT_QUEUE:
|
||||
default:
|
||||
entry = entry->next;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
|
||||
case BTimedEventQueue::B_BEFORE_TIME:
|
||||
end = GetEnd_BeforeTime(eventTime, inclusive);
|
||||
if (end == NULL)
|
||||
return B_OK;
|
||||
end = end->next;
|
||||
for (entry = fFirstEntry; entry != end; ) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
action = (*hook)(&entry->event, context);
|
||||
switch (action) {
|
||||
case BTimedEventQueue::B_DONE:
|
||||
return B_OK;
|
||||
case BTimedEventQueue::B_REMOVE_EVENT:
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
break;
|
||||
case BTimedEventQueue::B_NO_ACTION:
|
||||
case BTimedEventQueue::B_RESORT_QUEUE:
|
||||
default:
|
||||
entry = entry->next;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
|
||||
case BTimedEventQueue::B_AT_TIME:
|
||||
{
|
||||
bool found_time = false;
|
||||
for (entry = fFirstEntry; entry; ) {
|
||||
if (eventTime == entry->event.event_time) {
|
||||
found_time = true;
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
action = (*hook)(&entry->event, context);
|
||||
switch (action) {
|
||||
case BTimedEventQueue::B_DONE:
|
||||
return B_OK;
|
||||
case BTimedEventQueue::B_REMOVE_EVENT:
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
break;
|
||||
case BTimedEventQueue::B_NO_ACTION:
|
||||
case BTimedEventQueue::B_RESORT_QUEUE:
|
||||
default:
|
||||
entry = entry->next;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
} else if (found_time) {
|
||||
break;
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case BTimedEventQueue::B_AFTER_TIME:
|
||||
for (entry = GetStart_AfterTime(eventTime, inclusive); entry; ) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
action = (*hook)(&entry->event, context);
|
||||
switch (action) {
|
||||
case BTimedEventQueue::B_DONE:
|
||||
return B_OK;
|
||||
case BTimedEventQueue::B_REMOVE_EVENT:
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
break;
|
||||
case BTimedEventQueue::B_NO_ACTION:
|
||||
case BTimedEventQueue::B_RESORT_QUEUE:
|
||||
default:
|
||||
entry = entry->next;
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
status_t
|
||||
_event_queue_imp::FlushEvents(bigtime_t eventTime,
|
||||
BTimedEventQueue::time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType)
|
||||
{
|
||||
event_queue_entry *end;
|
||||
event_queue_entry *entry;
|
||||
event_queue_entry *remove;
|
||||
|
||||
BAutolock lock(fLock);
|
||||
|
||||
switch (direction) {
|
||||
case BTimedEventQueue::B_ALWAYS:
|
||||
for (entry = fFirstEntry; entry; ) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
|
||||
case BTimedEventQueue::B_BEFORE_TIME:
|
||||
end = GetEnd_BeforeTime(eventTime, inclusive);
|
||||
if (end == NULL)
|
||||
return B_OK;
|
||||
end = end->next;
|
||||
for (entry = fFirstEntry; entry != end; ) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
|
||||
case BTimedEventQueue::B_AT_TIME:
|
||||
{
|
||||
bool found_time = false;
|
||||
for (entry = fFirstEntry; entry; ) {
|
||||
if (eventTime == entry->event.event_time) {
|
||||
found_time = true;
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
} else if (found_time) {
|
||||
break;
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
case BTimedEventQueue::B_AFTER_TIME:
|
||||
for (entry = GetStart_AfterTime(eventTime, inclusive); entry; ) {
|
||||
if (eventType == BTimedEventQueue::B_ANY_EVENT || eventType == entry->event.type) {
|
||||
CleanupEvent(&entry->event);
|
||||
remove = entry;
|
||||
entry = entry->next;
|
||||
RemoveEntry(remove);
|
||||
} else {
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
return B_OK;
|
||||
}
|
||||
|
||||
return B_ERROR;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_event_queue_imp::SetCleanupHook(BTimedEventQueue::cleanup_hook hook, void *context)
|
||||
{
|
||||
BAutolock lock(fLock);
|
||||
fCleanupHookContext = context;
|
||||
fCleanupHook = hook;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_event_queue_imp::RemoveEntry(event_queue_entry *entry)
|
||||
{
|
||||
//remove the entry from double-linked list
|
||||
//and delete it
|
||||
if (entry->prev != NULL)
|
||||
(entry->prev)->next = entry->next;
|
||||
if (entry->next != NULL)
|
||||
(entry->next)->prev = entry->prev;
|
||||
if (entry == fFirstEntry) {
|
||||
fFirstEntry = entry->next;
|
||||
if (fFirstEntry != NULL)
|
||||
fFirstEntry->prev = NULL;
|
||||
}
|
||||
if (entry == fLastEntry) {
|
||||
fLastEntry = entry->prev;
|
||||
if (fLastEntry != NULL)
|
||||
fLastEntry->next = NULL;
|
||||
}
|
||||
delete entry;
|
||||
fEventCount--;
|
||||
ASSERT(fEventCount >= 0);
|
||||
ASSERT(fEventCount != 0 || ((fFirstEntry == NULL) && (fLastEntry == NULL)));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
_event_queue_imp::CleanupEvent(media_timed_event *event)
|
||||
{
|
||||
//perform the cleanup action required
|
||||
//when deleting an event from the queue
|
||||
|
||||
//BeBook says:
|
||||
// Each event has a cleanup flag associated with it that indicates
|
||||
// what sort of special action needs to be performed when the event is
|
||||
// removed from the queue. If this value is B_NO_CLEANUP, nothing is done.
|
||||
// If it's B_RECYCLE, and the event is a B_HANDLE_BUFFER event, BTimedEventQueue
|
||||
// will automatically recycle the buffer associated with the event.
|
||||
// If the cleanup flag is B_DELETE or is B_USER_CLEANUP or greater,
|
||||
// the cleanup hook function will be called.
|
||||
//and:
|
||||
// cleanup hook function specified by hook to be called for events
|
||||
// as they're removed from the queue. The hook will be called only
|
||||
// for events with cleanup_flag values of B_DELETE or B_USER_CLEANUP or greater.
|
||||
//and:
|
||||
// These values define how BTimedEventQueue should handle removing
|
||||
// events from the queue. If the flag is B_USER_CLEANUP or greater,
|
||||
// the cleanup hook function is called when the event is removed.
|
||||
//Problems:
|
||||
// B_DELETE is a keyboard code! (seems to have existed in early
|
||||
// sample code as a cleanup flag)
|
||||
//
|
||||
// exiting cleanup flags are:
|
||||
// B_NO_CLEANUP = 0,
|
||||
// B_RECYCLE_BUFFER, // recycle buffers handled by BTimedEventQueue
|
||||
// B_EXPIRE_TIMER, // call TimerExpired() on the event->data
|
||||
// B_USER_CLEANUP = 0x4000 // others go to the cleanup func
|
||||
//
|
||||
|
||||
if (event->cleanup == BTimedEventQueue::B_NO_CLEANUP) {
|
||||
// do nothing
|
||||
} else if (event->type == BTimedEventQueue::B_HANDLE_BUFFER
|
||||
&& event->cleanup == BTimedEventQueue::B_RECYCLE_BUFFER) {
|
||||
((BBuffer *)event->pointer)->Recycle();
|
||||
DEBUG_ONLY(*const_cast<void **>(&event->pointer) = NULL);
|
||||
} else if (event->cleanup == BTimedEventQueue::B_EXPIRE_TIMER) {
|
||||
// do nothing, called in BMediaEventLooper::DispatchEvent
|
||||
} else if (event->cleanup == B_DELETE
|
||||
|| event->cleanup >= BTimedEventQueue::B_USER_CLEANUP) {
|
||||
if (fCleanupHook)
|
||||
(*fCleanupHook)(event,fCleanupHookContext);
|
||||
} else {
|
||||
ERROR("BTimedEventQueue cleanup unhandled! type = %" B_PRId32
|
||||
", cleanup = %" B_PRId32 "\n", event->type, event->cleanup);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
_event_queue_imp::event_queue_entry *
|
||||
_event_queue_imp::GetEnd_BeforeTime(bigtime_t eventTime, bool inclusive)
|
||||
{
|
||||
event_queue_entry *entry;
|
||||
|
||||
entry = fLastEntry;
|
||||
while (entry) {
|
||||
if ((entry->event.event_time > eventTime) || (!inclusive && entry->event.event_time == eventTime))
|
||||
entry = entry->prev;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
_event_queue_imp::event_queue_entry *
|
||||
_event_queue_imp::GetStart_AfterTime(bigtime_t eventTime, bool inclusive)
|
||||
{
|
||||
event_queue_entry *entry;
|
||||
|
||||
entry = fFirstEntry;
|
||||
while (entry) {
|
||||
if ((entry->event.event_time < eventTime) || (!inclusive && entry->event.event_time == eventTime))
|
||||
entry = entry->next;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
|
||||
#if DEBUG > 1
|
||||
void
|
||||
_event_queue_imp::Dump() const
|
||||
{
|
||||
TRACE("fEventCount = %" B_PRId32 "\n", fEventCount);
|
||||
TRACE("fFirstEntry = 0x%p\n", (void*)fFirstEntry);
|
||||
TRACE("fLastEntry = 0x%p\n", (void*)fLastEntry);
|
||||
for (event_queue_entry *entry = fFirstEntry; entry; entry = entry->next) {
|
||||
TRACE("entry = 0x%p\n", (void*)entry);
|
||||
TRACE(" entry.prev = 0x%p\n", (void*)entry->prev);
|
||||
TRACE(" entry.next = 0x%p\n", (void*)entry->next);
|
||||
TRACE(" entry.event.event_time = %" B_PRId64 "\n",
|
||||
entry->event.event_time);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -1,79 +0,0 @@
|
||||
/***********************************************************************
|
||||
* AUTHOR: Marcus Overhagen
|
||||
* FILE: TimedEventQueuePrivate.cpp
|
||||
* DESCR: implements _event_queue_imp used by BTimedEventQueue,
|
||||
* not thread save!
|
||||
***********************************************************************/
|
||||
|
||||
#ifndef _TIMED_EVENT_QUEUE_PRIVATE_H
|
||||
#define _TIMED_EVENT_QUEUE_PRIVATE_H
|
||||
|
||||
#include <MediaDefs.h>
|
||||
#include <Locker.h>
|
||||
#include "MediaDebug.h"
|
||||
|
||||
struct _event_queue_imp
|
||||
{
|
||||
_event_queue_imp();
|
||||
~_event_queue_imp();
|
||||
|
||||
status_t AddEvent(const media_timed_event &event);
|
||||
status_t RemoveEvent(const media_timed_event *event);
|
||||
status_t RemoveFirstEvent(media_timed_event * outEvent);
|
||||
|
||||
bool HasEvents() const;
|
||||
int32 EventCount() const;
|
||||
|
||||
const media_timed_event * FirstEvent() const;
|
||||
bigtime_t FirstEventTime() const;
|
||||
const media_timed_event * LastEvent() const;
|
||||
bigtime_t LastEventTime() const;
|
||||
|
||||
const media_timed_event * FindFirstMatch(
|
||||
bigtime_t eventTime,
|
||||
BTimedEventQueue::time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType);
|
||||
|
||||
status_t DoForEach(
|
||||
BTimedEventQueue::for_each_hook hook,
|
||||
void *context,
|
||||
bigtime_t eventTime,
|
||||
BTimedEventQueue::time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType);
|
||||
|
||||
void SetCleanupHook(BTimedEventQueue::cleanup_hook hook, void *context);
|
||||
status_t FlushEvents(
|
||||
bigtime_t eventTime,
|
||||
BTimedEventQueue::time_direction direction,
|
||||
bool inclusive,
|
||||
int32 eventType);
|
||||
|
||||
#if DEBUG > 1
|
||||
void Dump() const;
|
||||
#endif
|
||||
|
||||
private:
|
||||
struct event_queue_entry
|
||||
{
|
||||
struct event_queue_entry *prev;
|
||||
struct event_queue_entry *next;
|
||||
media_timed_event event;
|
||||
};
|
||||
|
||||
void RemoveEntry(event_queue_entry *entry);
|
||||
void CleanupEvent(media_timed_event *event);
|
||||
|
||||
event_queue_entry *GetEnd_BeforeTime(bigtime_t eventTime, bool inclusive);
|
||||
event_queue_entry *GetStart_AfterTime(bigtime_t eventTime, bool inclusive);
|
||||
|
||||
BLocker * fLock;
|
||||
int32 fEventCount;
|
||||
event_queue_entry *fFirstEntry;
|
||||
event_queue_entry *fLastEntry;
|
||||
void * fCleanupHookContext;
|
||||
BTimedEventQueue::cleanup_hook fCleanupHook;
|
||||
};
|
||||
|
||||
#endif //_TIMED_EVENT_QUEUE_PRIVATE_H
|
||||
Reference in New Issue
Block a user