From 7771139cdf652d3712c0b96b8e9e2dda6db6191e Mon Sep 17 00:00:00 2001 From: Dario Casalinuovo Date: Mon, 3 Aug 2015 01:35:09 +0200 Subject: [PATCH] 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! --- headers/os/media/TimedEventQueue.h | 3 +- src/kits/media/MediaEventLooper.cpp | 118 +++++++++++----------- src/kits/media/TimedEventQueuePrivate.cpp | 2 - 3 files changed, 62 insertions(+), 61 deletions(-) diff --git a/headers/os/media/TimedEventQueue.h b/headers/os/media/TimedEventQueue.h index 190a792b1b..732fd1727b 100644 --- a/headers/os/media/TimedEventQueue.h +++ b/headers/os/media/TimedEventQueue.h @@ -38,9 +38,8 @@ struct media_timed_event { int32 data; int64 bigdata; 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]; }; diff --git a/src/kits/media/MediaEventLooper.cpp b/src/kits/media/MediaEventLooper.cpp index 0ce44f56f8..d072bc3efb 100644 --- a/src/kits/media/MediaEventLooper.cpp +++ b/src/kits/media/MediaEventLooper.cpp @@ -1,4 +1,5 @@ /* + * Copyright (c) 2015 Dario Casalinuovo * Copyright (c) 2002, 2003 Marcus Overhagen * * Permission is hereby granted, free of charge, to any person obtaining @@ -212,68 +213,71 @@ BMediaEventLooper::ControlLoop() { CALLED(); - bool is_realtime = false; status_t err; - bigtime_t latency; - bigtime_t waituntil; - bigtime_t lateness; - for (;;) { - // 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). + bigtime_t waitUntil = 0; + bigtime_t lateness = 0; + bool hasRealtime = false; + bool hasEvent = false; - latency = fEventLatency + fSchedulingLatency; - waituntil = B_INFINITE_TIMEOUT; - if (fEventQueue.HasEvents()) { - const media_timed_event *firstEvent = fEventQueue.FirstEvent(); - waituntil = TimeSource()->RealTimeFor(firstEvent->event_time, latency); - is_realtime = false; - lateness = firstEvent->queued_time - waituntil; - if (lateness > 0) { -// if (lateness > 1000) -// printf("node %02ld handling %12Ld at %12Ld -- %Ld late, queued at %Ld now %12Ld \n", -// ID(), fEventQueue.FirstEventTime(), TimeSource()->Now(), lateness, -// firstEvent->queued_time, TimeSource()->RealTime()); - is_realtime = false; - break; - } -// printf("node %02ld waiting for %12Ld that will happen at %12Ld\n", ID(), fEventQueue.FirstEventTime(), waituntil); - } - if (fRealTimeQueue.HasEvents()) { - const media_timed_event *firstEvent = fRealTimeQueue.FirstEvent(); - bigtime_t temp; - temp = firstEvent->event_time - fSchedulingLatency; - lateness = firstEvent->queued_time - temp; - if (lateness > 0) { - is_realtime = true; - break; - } - if (temp < waituntil) { - waituntil = temp; - is_realtime = true; - } - } - lateness = 0; // remove any extraneous value if we get this far - err = WaitForMessage(waituntil); - if (err == B_TIMED_OUT) - break; + // 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 + + fSchedulingLatency = estimate_max_scheduling_latency(fControlThread); + while (true) { + 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). + + waitUntil = B_INFINITE_TIMEOUT; + hasRealtime = fRealTimeQueue.HasEvents(); + hasEvent = fEventQueue.HasEvents(); + + if (hasEvent) { + waitUntil = TimeSource()->RealTimeFor( + fEventQueue.FirstEvent()->event_time, + fEventLatency + fSchedulingLatency); + lateness = waitUntil; + } else if (!hasEvent && !hasRealtime) + goto ahead; + + if (hasEvent && hasRealtime) { + if (fRealTimeQueue.FirstEventTime() + - fSchedulingLatency <= waitUntil) { + hasEvent = false; + } else + hasRealtime = false; } - /// 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; } } diff --git a/src/kits/media/TimedEventQueuePrivate.cpp b/src/kits/media/TimedEventQueuePrivate.cpp index 3cc337c50a..6b5be4d988 100644 --- a/src/kits/media/TimedEventQueuePrivate.cpp +++ b/src/kits/media/TimedEventQueuePrivate.cpp @@ -78,8 +78,6 @@ _event_queue_imp::AddEvent(const media_timed_event &event) return B_BAD_VALUE; } - *(bigtime_t *)&event.queued_time = BTimeSource::RealTime(); - //create a new queue if (fFirstEntry == NULL) { ASSERT(fEventCount == 0);