annotate base/ById.cpp @ 1752:6d09d68165a4 by-id

Further review of ById: make IDs only available when adding a model to the ById store, not by querying the item directly. This means any id encountered in the wild must have been added to the store at some point (even if later released), which simplifies reasoning about lifecycles
author Chris Cannam
date Fri, 05 Jul 2019 15:28:07 +0100
parents b92bdcd4954b
children b679bae1627b
rev   line source
Chris@1742 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@1742 2
Chris@1742 3 /*
Chris@1742 4 Sonic Visualiser
Chris@1742 5 An audio file viewer and annotation editor.
Chris@1742 6 Centre for Digital Music, Queen Mary, University of London.
Chris@1742 7
Chris@1742 8 This program is free software; you can redistribute it and/or
Chris@1742 9 modify it under the terms of the GNU General Public License as
Chris@1742 10 published by the Free Software Foundation; either version 2 of the
Chris@1742 11 License, or (at your option) any later version. See the file
Chris@1742 12 COPYING included with this distribution for more information.
Chris@1742 13 */
Chris@1742 14
Chris@1742 15 #include "ById.h"
Chris@1742 16
Chris@1742 17 #include <unordered_map>
Chris@1742 18 #include <typeinfo>
Chris@1742 19
Chris@1742 20 int IdAlloc::getNextId()
Chris@1742 21 {
Chris@1742 22 static int nextId = 0;
Chris@1742 23 static QMutex mutex;
Chris@1742 24 QMutexLocker locker(&mutex);
Chris@1742 25 int i = nextId;
Chris@1742 26 if (nextId == INT_MAX) {
Chris@1742 27 nextId = INT_MIN;
Chris@1742 28 } else {
Chris@1742 29 ++nextId;
Chris@1742 30 if (nextId == 0 || nextId == NO_ID) {
Chris@1742 31 throw std::runtime_error("Internal ID limit exceeded!");
Chris@1742 32 }
Chris@1742 33 }
Chris@1742 34 return i;
Chris@1742 35 }
Chris@1742 36
Chris@1742 37 class AnyById::Impl
Chris@1742 38 {
Chris@1742 39 public:
Chris@1742 40 ~Impl() {
Chris@1742 41 QMutexLocker locker(&m_mutex);
Chris@1742 42 bool empty = true;
Chris@1742 43 for (const auto &p: m_items) {
Chris@1742 44 if (p.second && p.second.use_count() > 0) {
Chris@1742 45 empty = false;
Chris@1742 46 break;
Chris@1742 47 }
Chris@1742 48 }
Chris@1742 49 if (!empty) {
Chris@1742 50 SVCERR << "WARNING: ById map is not empty at close; some items have not been released" << endl;
Chris@1742 51 SVCERR << " Unreleased items are:" << endl;
Chris@1742 52 for (const auto &p: m_items) {
Chris@1742 53 if (p.second && p.second.use_count() > 0) {
Chris@1742 54 SVCERR << " - id #" << p.first
Chris@1742 55 << ": type " << typeid(*p.second.get()).name()
Chris@1742 56 << ", use count " << p.second.use_count() << endl;
Chris@1742 57 }
Chris@1742 58 }
Chris@1742 59 }
Chris@1742 60 }
Chris@1742 61
Chris@1752 62 int add(std::shared_ptr<WithId> item) {
Chris@1752 63 int id = item->getUntypedId();
Chris@1744 64 if (id == IdAlloc::NO_ID) {
Chris@1752 65 throw std::logic_error("item id should never be NO_ID");
Chris@1744 66 }
Chris@1742 67 QMutexLocker locker(&m_mutex);
Chris@1742 68 if (m_items.find(id) != m_items.end()) {
Chris@1742 69 SVCERR << "ById::add: item with id " << id
Chris@1742 70 << " is already recorded (existing item type is "
Chris@1742 71 << typeid(*m_items.find(id)->second.get()).name()
Chris@1742 72 << ", proposed is "
Chris@1742 73 << typeid(*item.get()).name() << ")" << endl;
Chris@1742 74 throw std::logic_error("item id is already recorded in add");
Chris@1742 75 }
Chris@1742 76 m_items[id] = item;
Chris@1752 77 return id;
Chris@1742 78 }
Chris@1742 79
Chris@1742 80 void release(int id) {
Chris@1744 81 if (id == IdAlloc::NO_ID) {
Chris@1744 82 return;
Chris@1744 83 }
Chris@1752 84 SVCERR << "ById::release(" << id << ")" << endl;
Chris@1742 85 QMutexLocker locker(&m_mutex);
Chris@1742 86 if (m_items.find(id) == m_items.end()) {
Chris@1742 87 SVCERR << "ById::release: unknown item id " << id << endl;
Chris@1742 88 throw std::logic_error("unknown item id in release");
Chris@1742 89 }
Chris@1742 90 m_items.erase(id);
Chris@1742 91 }
Chris@1742 92
Chris@1742 93 std::shared_ptr<WithId> get(int id) const {
Chris@1744 94 if (id == IdAlloc::NO_ID) {
Chris@1744 95 return {}; // this id cannot be added: avoid locking
Chris@1744 96 }
Chris@1742 97 QMutexLocker locker(&m_mutex);
Chris@1742 98 const auto &itr = m_items.find(id);
Chris@1742 99 if (itr != m_items.end()) {
Chris@1742 100 return itr->second;
Chris@1742 101 } else {
Chris@1742 102 return {};
Chris@1742 103 }
Chris@1742 104 }
Chris@1742 105
Chris@1742 106 private:
Chris@1742 107 mutable QMutex m_mutex;
Chris@1742 108 std::unordered_map<int, std::shared_ptr<WithId>> m_items;
Chris@1742 109 };
Chris@1742 110
Chris@1752 111 int
Chris@1752 112 AnyById::add(std::shared_ptr<WithId> item)
Chris@1742 113 {
Chris@1752 114 return impl().add(item);
Chris@1742 115 }
Chris@1742 116
Chris@1742 117 void
Chris@1742 118 AnyById::release(int id)
Chris@1742 119 {
Chris@1742 120 impl().release(id);
Chris@1742 121 }
Chris@1742 122
Chris@1742 123 std::shared_ptr<WithId>
Chris@1742 124 AnyById::get(int id)
Chris@1742 125 {
Chris@1742 126 return impl().get(id);
Chris@1742 127 }
Chris@1742 128
Chris@1742 129 AnyById::Impl &
Chris@1742 130 AnyById::impl()
Chris@1742 131 {
Chris@1742 132 static Impl impl;
Chris@1742 133 return impl;
Chris@1742 134 }