Chris@1742: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@1742: Chris@1742: /* Chris@1742: Sonic Visualiser Chris@1742: An audio file viewer and annotation editor. Chris@1742: Centre for Digital Music, Queen Mary, University of London. Chris@1742: Chris@1742: This program is free software; you can redistribute it and/or Chris@1742: modify it under the terms of the GNU General Public License as Chris@1742: published by the Free Software Foundation; either version 2 of the Chris@1742: License, or (at your option) any later version. See the file Chris@1742: COPYING included with this distribution for more information. Chris@1742: */ Chris@1742: Chris@1742: #include "ById.h" Chris@1742: Chris@1742: #include Chris@1742: #include Chris@1742: Chris@1768: //#define DEBUG_BY_ID 1 Chris@1768: Chris@1742: int IdAlloc::getNextId() Chris@1742: { Chris@1742: static int nextId = 0; Chris@1742: static QMutex mutex; Chris@1742: QMutexLocker locker(&mutex); Chris@1742: int i = nextId; Chris@1742: if (nextId == INT_MAX) { Chris@1742: nextId = INT_MIN; Chris@1742: } else { Chris@1742: ++nextId; Chris@1742: if (nextId == 0 || nextId == NO_ID) { Chris@1742: throw std::runtime_error("Internal ID limit exceeded!"); Chris@1742: } Chris@1742: } Chris@1742: return i; Chris@1742: } Chris@1742: Chris@1806: // "warning: expression with side effects will be evaluated despite Chris@1806: // being used as an operand to 'typeid'" Chris@1808: #ifdef __clang__ Chris@1807: #pragma clang diagnostic ignored "-Wpotentially-evaluated-expression" Chris@1808: #endif Chris@1806: Chris@1742: class AnyById::Impl Chris@1742: { Chris@1742: public: Chris@1742: ~Impl() { Chris@1742: QMutexLocker locker(&m_mutex); Chris@1742: bool empty = true; Chris@1742: for (const auto &p: m_items) { Chris@1742: if (p.second && p.second.use_count() > 0) { Chris@1742: empty = false; Chris@1742: break; Chris@1742: } Chris@1742: } Chris@1742: if (!empty) { Chris@1742: SVCERR << "WARNING: ById map is not empty at close; some items have not been released" << endl; Chris@1742: SVCERR << " Unreleased items are:" << endl; Chris@1742: for (const auto &p: m_items) { Chris@1759: auto ptr = p.second; Chris@1759: if (ptr && ptr.use_count() > 0) { Chris@1759: QString message = QString("id #%1: type %2") Chris@1759: .arg(p.first).arg(typeid(*ptr.get()).name()); Chris@1759: if (auto qobj = std::dynamic_pointer_cast(ptr)) { Chris@1759: message += QString(", object name \"%1\"") Chris@1759: .arg(qobj->objectName()); Chris@1759: } Chris@1759: message += QString(", use count %1").arg(ptr.use_count()); Chris@1759: SVCERR << " - " << message << endl; Chris@1742: } Chris@1742: } Chris@1742: } Chris@1742: } Chris@1742: Chris@1752: int add(std::shared_ptr item) { Chris@1752: int id = item->getUntypedId(); Chris@1744: if (id == IdAlloc::NO_ID) { Chris@1752: throw std::logic_error("item id should never be NO_ID"); Chris@1744: } Chris@1768: #ifdef DEBUG_BY_ID Chris@1761: SVCERR << "ById::add(#" << id << ") of type " Chris@1761: << typeid(*item.get()).name() << endl; Chris@1768: #endif Chris@1742: QMutexLocker locker(&m_mutex); Chris@1742: if (m_items.find(id) != m_items.end()) { Chris@1742: SVCERR << "ById::add: item with id " << id Chris@1742: << " is already recorded (existing item type is " Chris@1742: << typeid(*m_items.find(id)->second.get()).name() Chris@1742: << ", proposed is " Chris@1742: << typeid(*item.get()).name() << ")" << endl; Chris@1742: throw std::logic_error("item id is already recorded in add"); Chris@1742: } Chris@1742: m_items[id] = item; Chris@1752: return id; Chris@1742: } Chris@1742: Chris@1742: void release(int id) { Chris@1744: if (id == IdAlloc::NO_ID) { Chris@1744: return; Chris@1744: } Chris@1768: #ifdef DEBUG_BY_ID Chris@1761: SVCERR << "ById::release(#" << id << ")" << endl; Chris@1768: #endif Chris@1742: QMutexLocker locker(&m_mutex); Chris@1742: if (m_items.find(id) == m_items.end()) { Chris@1742: SVCERR << "ById::release: unknown item id " << id << endl; Chris@1742: throw std::logic_error("unknown item id in release"); Chris@1742: } Chris@1742: m_items.erase(id); Chris@1742: } Chris@1742: Chris@1742: std::shared_ptr get(int id) const { Chris@1744: if (id == IdAlloc::NO_ID) { Chris@1744: return {}; // this id cannot be added: avoid locking Chris@1744: } Chris@1742: QMutexLocker locker(&m_mutex); Chris@1742: const auto &itr = m_items.find(id); Chris@1742: if (itr != m_items.end()) { Chris@1742: return itr->second; Chris@1742: } else { Chris@1742: return {}; Chris@1742: } Chris@1742: } Chris@1742: Chris@1742: private: Chris@1742: mutable QMutex m_mutex; Chris@1742: std::unordered_map> m_items; Chris@1742: }; Chris@1742: Chris@1752: int Chris@1752: AnyById::add(std::shared_ptr item) Chris@1742: { Chris@1752: return impl().add(item); Chris@1742: } Chris@1742: Chris@1742: void Chris@1742: AnyById::release(int id) Chris@1742: { Chris@1742: impl().release(id); Chris@1742: } Chris@1742: Chris@1742: std::shared_ptr Chris@1742: AnyById::get(int id) Chris@1742: { Chris@1742: return impl().get(id); Chris@1742: } Chris@1742: Chris@1742: AnyById::Impl & Chris@1742: AnyById::impl() Chris@1742: { Chris@1742: static Impl impl; Chris@1742: return impl; Chris@1742: }