summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2014-03-31 05:26:26 +0000
committerDavid Robillard <d@drobilla.net>2014-03-31 05:26:26 +0000
commit33ef5af23ef2eccb11de1ddbd860020f2fd8baae (patch)
tree7bb32fd4ab24bc7ed48447cedfd5351fabc297d8
parenta1da3ac5c84d2cffe25fba199815cd4dc839c862 (diff)
downloadingen-33ef5af23ef2eccb11de1ddbd860020f2fd8baae.tar.gz
ingen-33ef5af23ef2eccb11de1ddbd860020f2fd8baae.tar.bz2
ingen-33ef5af23ef2eccb11de1ddbd860020f2fd8baae.zip
Yet another attempt at fixing event deadlock issues.
This time, completely isolate head and tail pointers to read and write methods, and never empty the list. git-svn-id: http://svn.drobilla.net/lad/trunk/ingen@5355 a436a847-0d15-0410-975c-d299462d15a1
-rw-r--r--src/server/Engine.cpp2
-rw-r--r--src/server/PostProcessor.cpp56
2 files changed, 32 insertions, 26 deletions
diff --git a/src/server/Engine.cpp b/src/server/Engine.cpp
index 1b6d54ca..71573ecb 100644
--- a/src/server/Engine.cpp
+++ b/src/server/Engine.cpp
@@ -363,7 +363,7 @@ Engine::enqueue_event(Event* ev)
unsigned
Engine::process_events()
{
- const size_t MAX_EVENTS_PER_CYCLE = _process_context.nframes() / 4;
+ const size_t MAX_EVENTS_PER_CYCLE = _process_context.nframes() / 8;
return _pre_processor->process(
_process_context, *_post_processor, MAX_EVENTS_PER_CYCLE);
}
diff --git a/src/server/PostProcessor.cpp b/src/server/PostProcessor.cpp
index af429c01..b16aca2a 100644
--- a/src/server/PostProcessor.cpp
+++ b/src/server/PostProcessor.cpp
@@ -26,10 +26,19 @@ using namespace std;
namespace Ingen {
namespace Server {
+class Sentinel : public Event {
+public:
+ Sentinel(Engine& engine) : Event(engine) {}
+
+ bool pre_process() { return false; }
+ void execute(ProcessContext& context) {}
+ void post_process() {}
+};
+
PostProcessor::PostProcessor(Engine& engine)
: _engine(engine)
- , _head(NULL)
- , _tail(NULL)
+ , _head(new Sentinel(engine))
+ , _tail(_head.load())
, _max_time(0)
{
}
@@ -45,15 +54,9 @@ PostProcessor::append(ProcessContext& context, Event* first, Event* last)
assert(last);
assert(!last->next());
- /* Note that tail is only used here, not in process(). The head must be
- checked first here, since if it is NULL the tail pointer is junk. */
- if (!_head) {
- _tail = last;
- _head = first;
- } else {
- _tail.load()->next(first);
- _tail = last;
- }
+ // The only place where _tail is written or next links are changed
+ _tail.load()->next(first);
+ _tail.store(last);
}
bool
@@ -67,32 +70,35 @@ PostProcessor::process()
{
const FrameTime end_time = _max_time;
- Event* ev = _head.load();
- if (!ev) {
+ /* We can never empty the list and set _head = _tail = NULL since this
+ would cause a race with append. Instead, head is an already
+ post-processed node, or initially a sentinel. */
+ Event* ev = _head.load();
+ Event* next = (Event*)ev->next();
+ if (!next || next->time() >= end_time) {
// Process audio thread notifications until end
_engine.process_context().emit_notifications(end_time);
return;
}
- while (ev && ev->time() < end_time) {
- Event* const next = (Event*)ev->next();
+ do {
+ // Delete previously post-processed ev and move to next
+ delete ev;
+ ev = next;
// Process audio thread notifications up until this event's time
_engine.process_context().emit_notifications(ev->time());
- // Process and delete this event
+ // Post-process event
ev->post_process();
- delete ev;
+ next = ev->next(); // [1] (see below)
+ } while (next && next->time() < end_time);
- ev = next;
- }
-
- // Since _head was not NULL, we know it hasn't been changed since
+ /* Reached the tail (as far as we're concerned). There may be successors
+ by now if append() has been called since [1], but that's fine. Now, ev
+ points to the last post-processed event, which will be the new head. */
+ assert(ev);
_head = ev;
-
- /* If next is NULL, then _tail may now be invalid. However, it would cause
- a race to reset _tail here. Instead, append() checks only _head for
- emptiness, and resets the tail appropriately. */
// Process remaining audio thread notifications until end
_engine.process_context().emit_notifications(end_time);