BMediaEventLooper: Rewrite ControlLoop()

* The first problem was the O(n^2) complexity of the algorithm, it's
  now linear and try to act in a circular way by dispatching
  events and reading the port in a balanced way. This exclude
  a certain degree of possible deadlocks.
* Add detection and escape when the system try to kill the
  thread. This solve some blocking issues on exit et similia
  that i had with libjackcompat.
* The algorithm choose soon which event to focus on.
* Lateness is calculated just before the event is dispatched
  as it is the more appropriate place, otherwise we would be
  calculating something imprecise/guessed.
* Remove timed_event_queue::queued_time. It's more precise to
  just use the RealTime() before to Dispatch the event.
* It should solve the BSoundPlayer lateness problems.
* With those improvements the media_kit is not going to lock
  completely under stress conditions, instead it try to work
  in a best effort shape.
* There's still room for improvements, for example i'm considering some
  strategies in lateness situations such as update scheduling latency,
  try to decrease waiting time and detect when we are too early on
  the other hand to recover when the load go down.
* Thanks to Julian Harnath for sharing his WIP patch which helped
  with some controls such as avoiding negative lateness.
* Comments are welcome!
This commit is contained in:
Dario Casalinuovo
2015-08-03 01:35:09 +02:00
parent 8ecc32df11
commit 7771139cdf
3 changed files with 62 additions and 61 deletions
+1 -2
View File
@@ -38,9 +38,8 @@ struct media_timed_event {
int32 data; int32 data;
int64 bigdata; int64 bigdata;
char user_data[64]; char user_data[64];
bigtime_t queued_time; // Real time when put in queue
uint32 _reserved_media_timed_event_[6]; uint32 _reserved_media_timed_event_[8];
}; };
+61 -57
View File
@@ -1,4 +1,5 @@
/* /*
* Copyright (c) 2015 Dario Casalinuovo <[email protected]>
* Copyright (c) 2002, 2003 Marcus Overhagen <[email protected]> * Copyright (c) 2002, 2003 Marcus Overhagen <[email protected]>
* *
* Permission is hereby granted, free of charge, to any person obtaining * Permission is hereby granted, free of charge, to any person obtaining
@@ -212,68 +213,71 @@ BMediaEventLooper::ControlLoop()
{ {
CALLED(); CALLED();
bool is_realtime = false;
status_t err; status_t err;
bigtime_t latency; bigtime_t waitUntil = 0;
bigtime_t waituntil; bigtime_t lateness = 0;
bigtime_t lateness; bool hasRealtime = false;
for (;;) { bool hasEvent = false;
// while there are no events or it is not time for the earliest event,
// process messages using WaitForMessages. Whenever this funtion times out,
// we need to handle the next event
for (;;) {
if (RunState() == B_QUITTING)
return;
// BMediaEventLooper compensates your performance time by adding the event latency
// (see SetEventLatency()) and the scheduling latency (or, for real-time events,
// only the scheduling latency).
latency = fEventLatency + fSchedulingLatency; // While there are no events or it is not time for the earliest event,
waituntil = B_INFINITE_TIMEOUT; // process messages using WaitForMessages. Whenever this funtion times out,
if (fEventQueue.HasEvents()) { // we need to handle the next event
const media_timed_event *firstEvent = fEventQueue.FirstEvent();
waituntil = TimeSource()->RealTimeFor(firstEvent->event_time, latency); fSchedulingLatency = estimate_max_scheduling_latency(fControlThread);
is_realtime = false; while (true) {
lateness = firstEvent->queued_time - waituntil; if (RunState() == B_QUITTING)
if (lateness > 0) { return;
// if (lateness > 1000)
// printf("node %02ld handling %12Ld at %12Ld -- %Ld late, queued at %Ld now %12Ld \n", // BMediaEventLooper compensates your performance time by adding
// ID(), fEventQueue.FirstEventTime(), TimeSource()->Now(), lateness, // the event latency (see SetEventLatency()) and the scheduling
// firstEvent->queued_time, TimeSource()->RealTime()); // latency (or, for real-time events, only the scheduling latency).
is_realtime = false;
break; waitUntil = B_INFINITE_TIMEOUT;
} hasRealtime = fRealTimeQueue.HasEvents();
// printf("node %02ld waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil); hasEvent = fEventQueue.HasEvents();
}
if (fRealTimeQueue.HasEvents()) { if (hasEvent) {
const media_timed_event *firstEvent = fRealTimeQueue.FirstEvent(); waitUntil = TimeSource()->RealTimeFor(
bigtime_t temp; fEventQueue.FirstEvent()->event_time,
temp = firstEvent->event_time - fSchedulingLatency; fEventLatency + fSchedulingLatency);
lateness = firstEvent->queued_time - temp; lateness = waitUntil;
if (lateness > 0) { } else if (!hasEvent && !hasRealtime)
is_realtime = true; goto ahead;
break;
} if (hasEvent && hasRealtime) {
if (temp < waituntil) { if (fRealTimeQueue.FirstEventTime()
waituntil = temp; - fSchedulingLatency <= waitUntil) {
is_realtime = true; hasEvent = false;
} } else
} hasRealtime = false;
lateness = 0; // remove any extraneous value if we get this far
err = WaitForMessage(waituntil);
if (err == B_TIMED_OUT)
break;
} }
/// we have timed out - so handle the next event
media_timed_event event;
if (is_realtime)
err = fRealTimeQueue.RemoveFirstEvent(&event);
else
err = fEventQueue.RemoveFirstEvent(&event);
// printf("node %02ld handling %12Ld at %12Ld\n", ID(), event.event_time, TimeSource()->Now()); if (hasRealtime) {
waitUntil = fRealTimeQueue.FirstEventTime()
- fSchedulingLatency;
lateness = waitUntil;
}
if (err == B_OK) DispatchEvent(&event, lateness, is_realtime); if (waitUntil <= TimeSource()->RealTime())
waitUntil = 0;
ahead:
err = WaitForMessage(waitUntil);
if (err == B_TIMED_OUT) {
media_timed_event event;
if (hasEvent)
err = fEventQueue.RemoveFirstEvent(&event);
else
err = fRealTimeQueue.RemoveFirstEvent(&event);
if (err == B_OK) {
lateness -= TimeSource()->RealTime();
if (lateness < 0)
lateness = 0;
DispatchEvent(&event, lateness, hasRealtime);
}
} else if (err != B_OK)
return;
} }
} }
@@ -78,8 +78,6 @@ _event_queue_imp::AddEvent(const media_timed_event &event)
return B_BAD_VALUE; return B_BAD_VALUE;
} }
*(bigtime_t *)&event.queued_time = BTimeSource::RealTime();
//create a new queue //create a new queue
if (fFirstEntry == NULL) { if (fFirstEntry == NULL) {
ASSERT(fEventCount == 0); ASSERT(fEventCount == 0);