Mercurial > hg > svcore
changeset 1796:ff8c57c364a0
Make EventSeries threadsafe
author | Chris Cannam |
---|---|
date | Mon, 30 Sep 2019 20:28:03 +0100 |
parents | 94b488d4b299 |
children | e8b552549225 |
files | base/EventSeries.cpp base/EventSeries.h |
diffstat | 2 files changed, 113 insertions(+), 14 deletions(-) [+] |
line wrap: on
line diff
--- a/base/EventSeries.cpp Mon Sep 30 12:36:44 2019 +0100 +++ b/base/EventSeries.cpp Mon Sep 30 20:28:03 2019 +0100 @@ -14,6 +14,47 @@ #include "EventSeries.h" +#include <QMutexLocker> + +EventSeries::EventSeries(const EventSeries &other) : + EventSeries(other, QMutexLocker(&other.m_mutex)) +{ +} + +EventSeries::EventSeries(const EventSeries &other, const QMutexLocker &) : + m_events(other.m_events), + m_seams(other.m_seams), + m_finalDurationlessEventFrame(other.m_finalDurationlessEventFrame) +{ +} + +EventSeries & +EventSeries::operator=(const EventSeries &other) +{ + QMutexLocker locker(&m_mutex), otherLocker(&other.m_mutex); + m_events = other.m_events; + m_seams = other.m_seams; + m_finalDurationlessEventFrame = other.m_finalDurationlessEventFrame; + return *this; +} + +EventSeries & +EventSeries::operator=(EventSeries &&other) +{ + QMutexLocker locker(&m_mutex), otherLocker(&other.m_mutex); + m_events = std::move(other.m_events); + m_seams = std::move(other.m_seams); + m_finalDurationlessEventFrame = std::move(other.m_finalDurationlessEventFrame); + return *this; +} + +bool +EventSeries::operator==(const EventSeries &other) const +{ + QMutexLocker locker(&m_mutex); + return m_events == other.m_events; +} + EventSeries EventSeries::fromEvents(const EventVector &v) { @@ -27,12 +68,14 @@ bool EventSeries::isEmpty() const { + QMutexLocker locker(&m_mutex); return m_events.empty(); } int EventSeries::count() const { + QMutexLocker locker(&m_mutex); if (m_events.size() > INT_MAX) { throw std::logic_error("too many events"); } @@ -42,6 +85,8 @@ void EventSeries::add(const Event &p) { + QMutexLocker locker(&m_mutex); + bool isUnique = true; auto pitr = lower_bound(m_events.begin(), m_events.end(), p); @@ -87,6 +132,8 @@ void EventSeries::remove(const Event &p) { + QMutexLocker locker(&m_mutex); + // If we are removing the last (unique) example of an event, // then we also need to remove it from the seam map. If this // is only one of multiple identical events, then we don't. @@ -198,12 +245,14 @@ bool EventSeries::contains(const Event &p) const { + QMutexLocker locker(&m_mutex); return binary_search(m_events.begin(), m_events.end(), p); } void EventSeries::clear() { + QMutexLocker locker(&m_mutex); m_events.clear(); m_seams.clear(); m_finalDurationlessEventFrame = 0; @@ -212,6 +261,7 @@ sv_frame_t EventSeries::getStartFrame() const { + QMutexLocker locker(&m_mutex); if (m_events.empty()) return 0; return m_events.begin()->getFrame(); } @@ -219,6 +269,8 @@ sv_frame_t EventSeries::getEndFrame() const { + QMutexLocker locker(&m_mutex); + sv_frame_t latest = 0; if (m_events.empty()) return latest; @@ -239,6 +291,8 @@ EventSeries::getEventsSpanning(sv_frame_t frame, sv_frame_t duration) const { + QMutexLocker locker(&m_mutex); + EventVector span; const sv_frame_t start = frame; @@ -286,6 +340,8 @@ sv_frame_t duration, int overspill) const { + QMutexLocker locker(&m_mutex); + EventVector span; const sv_frame_t start = frame; @@ -336,6 +392,8 @@ EventSeries::getEventsStartingWithin(sv_frame_t frame, sv_frame_t duration) const { + QMutexLocker locker(&m_mutex); + EventVector span; const sv_frame_t start = frame; @@ -358,6 +416,8 @@ EventVector EventSeries::getEventsCovering(sv_frame_t frame) const { + QMutexLocker locker(&m_mutex); + EventVector cover; // first find any zero-duration events @@ -400,12 +460,16 @@ EventVector EventSeries::getAllEvents() const { + QMutexLocker locker(&m_mutex); + return m_events; } bool EventSeries::getEventPreceding(const Event &e, Event &preceding) const { + QMutexLocker locker(&m_mutex); + auto pitr = lower_bound(m_events.begin(), m_events.end(), e); if (pitr == m_events.end() || *pitr != e) { return false; @@ -421,6 +485,8 @@ bool EventSeries::getEventFollowing(const Event &e, Event &following) const { + QMutexLocker locker(&m_mutex); + auto pitr = lower_bound(m_events.begin(), m_events.end(), e); if (pitr == m_events.end() || *pitr != e) { return false; @@ -441,6 +507,8 @@ Direction direction, Event &found) const { + QMutexLocker locker(&m_mutex); + auto pitr = lower_bound(m_events.begin(), m_events.end(), Event(startSearchAt)); @@ -475,6 +543,8 @@ Event EventSeries::getEventByIndex(int index) const { + QMutexLocker locker(&m_mutex); + if (index < 0 || index >= count()) { throw std::logic_error("index out of range"); } @@ -484,6 +554,8 @@ int EventSeries::getIndexForEvent(const Event &e) const { + QMutexLocker locker(&m_mutex); + auto pitr = lower_bound(m_events.begin(), m_events.end(), e); auto d = distance(m_events.begin(), pitr); if (d < 0 || d > INT_MAX) return 0; @@ -495,7 +567,17 @@ QString indent, QString extraAttributes) const { - toXml(out, indent, extraAttributes, Event::ExportNameOptions()); + QMutexLocker locker(&m_mutex); + + out << indent << QString("<dataset id=\"%1\" %2>\n") + .arg(getExportId()) + .arg(extraAttributes); + + for (const auto &p: m_events) { + p.toXml(out, indent + " ", "", {}); + } + + out << indent << "</dataset>\n"; } void @@ -504,6 +586,8 @@ QString extraAttributes, Event::ExportNameOptions options) const { + QMutexLocker locker(&m_mutex); + out << indent << QString("<dataset id=\"%1\" %2>\n") .arg(getExportId()) .arg(extraAttributes); @@ -524,6 +608,8 @@ sv_frame_t resolution, Event fillEvent) const { + QMutexLocker locker(&m_mutex); + QString s; const sv_frame_t end = startFrame + duration;
--- a/base/EventSeries.h Mon Sep 30 12:36:44 2019 +0100 +++ b/base/EventSeries.h Mon Sep 30 20:28:03 2019 +0100 @@ -21,6 +21,8 @@ #include <set> #include <functional> +#include <QMutex> + //#define DEBUG_EVENT_SERIES 1 /** @@ -40,7 +42,7 @@ * does work, and should be acceptable in interactive use, but it is * very slow in bulk. * - * EventSeries is not thread-safe. + * EventSeries is thread-safe. */ class EventSeries : public XmlExportable { @@ -48,14 +50,12 @@ EventSeries() : m_finalDurationlessEventFrame(0) { } ~EventSeries() =default; - EventSeries(const EventSeries &) =default; + EventSeries(const EventSeries &); - EventSeries &operator=(const EventSeries &) =delete; - EventSeries &operator=(EventSeries &&) =delete; + EventSeries &operator=(const EventSeries &); + EventSeries &operator=(EventSeries &&); - bool operator==(const EventSeries &other) const { - return m_events == other.m_events; - } + bool operator==(const EventSeries &other) const; static EventSeries fromEvents(const EventVector &ee); @@ -233,6 +233,10 @@ Event fillEvent) const; private: + mutable QMutex m_mutex; + + EventSeries::EventSeries(const EventSeries &other, const QMutexLocker &); + /** * This vector contains all events in the series, in the normal * sort order. For backward compatibility we must support series @@ -278,9 +282,12 @@ */ sv_frame_t m_finalDurationlessEventFrame; - /** Create a seam at the given frame, copying from the prior seam - * if there is one. If a seam already exists at the given frame, - * leave it untouched. + /** + * Create a seam at the given frame, copying from the prior seam + * if there is one. If a seam already exists at the given frame, + * leave it untouched. + * + * Call with m_mutex locked. */ void createSeam(sv_frame_t frame) { auto itr = m_seams.lower_bound(frame); @@ -298,6 +305,15 @@ } } + /** + * Return true if the two seam map entries contain the same set of + * events. + * + * Precondition: no duplicates, i.e. no event appears more than + * once in s1 or more than once in s2. + * + * Call with m_mutex locked. + */ bool seamsEqual(const std::vector<Event> &s1, const std::vector<Event> &s2) const { @@ -305,9 +321,6 @@ return false; } - // precondition: no event appears more than once in s1 or more - // than once in s2 - #ifdef DEBUG_EVENT_SERIES for (int i = 0; in_range_for(s1, i); ++i) { for (int j = i + 1; in_range_for(s1, j); ++j) {