libASPL
Loading...
Searching...
No Matches
Dispatcher.hpp
Go to the documentation of this file.
1// Copyright (c) libASPL authors
2// Licensed under MIT
3
4//! @file aspl/Dispatcher.hpp
5//! @brief Object dispatcher.
6
7#pragma once
8
10#include <aspl/Tracer.hpp>
11
12#include <CoreAudio/AudioServerPlugIn.h>
13
14#include <atomic>
15#include <memory>
16#include <mutex>
17#include <shared_mutex>
18#include <unordered_map>
19#include <unordered_set>
20#include <vector>
21
22namespace aspl {
23
24class Object;
25
26//! Object dispatcher.
27//!
28//! Every object registers itself here in its constructor, and
29//! unregisters in destructor.
30//!
31//! Dispatcher allocates object identifiers and maps identifiers to objects.
32//! Freed identifiers are reused.
33//!
34//! The allocation algorithm tries to delay identifier reuse for a while, to
35//! give you a chance to catch bugs with looking up recently freed objects.
36//!
37//! Dispatcher stores weak references to objects and thus does not affect
38//! their reference counter.
40{
41public:
42 //! Construct dispatcher.
43 //! Use tracer if you want to debug dispatcher itself, it is quite verbose.
44 Dispatcher(std::shared_ptr<Tracer> tracer = {}, AudioObjectID hintMaximumID = 1000);
45
46 Dispatcher(const Dispatcher&) = delete;
47 Dispatcher& operator=(const Dispatcher&) = delete;
48
49 //! Find registered object by ID.
50 //! Returns null if there is no such object.
51 //! @note
52 //! This method can be called from realtime threads.
53 std::shared_ptr<Object> FindObject(AudioObjectID objectID) const;
54
55 //! Register new object.
56 //! If objectID is kAudioObjectUnknown (zero), a new ID is allocated.
57 //! Otherwise, given ID is used.
58 //! @note
59 //! This method should not be called from realtime threads.
60 AudioObjectID RegisterObject(Object& object,
61 AudioObjectID objectID = kAudioObjectUnknown);
62
63 //! Unregister previously registered object.
64 //! It's guaranteed that after this method returns, the registered
65 //! object is not accessed by dispatcher anymore and can be destroyed.
66 //! @note
67 //! This method should not be called from realtime threads.
68 void UnregisterObject(AudioObjectID objectID);
69
70private:
71 struct Registration
72 {
73 explicit Registration(Object* obj)
74 : object(obj)
75 {
76 }
77
78 std::atomic<Object*> object;
79 std::shared_mutex mutex;
80 };
81
82 AudioObjectID AllocateID();
83 void FreeID(AudioObjectID objectID);
84
85 AudioObjectID FindFreeID(AudioObjectID startID) const;
86 bool IsFree(AudioObjectID objectID) const;
87 void SetFree(AudioObjectID objectID, bool free);
88
89 using BitChunk = UInt64;
90 static constexpr size_t BitsPerChunk = sizeof(BitChunk) * 8;
91
92 // Optional tracer.
93 const std::shared_ptr<Tracer> tracer_;
94
95 // Identifiers that should not be auto-allocated.
96 const std::unordered_set<AudioObjectID> reservedIDs_ = {
97 kAudioObjectUnknown,
98 kAudioObjectPlugInObject,
99 };
100
101 // Registered objects.
102 // We store raw pointers instead of shared_ptr to allow object registration
103 // to happen in Object constructor. At that point, there is no shared_ptr
104 // yet and shared_from_this() can't be used either. Thus, we delay
105 // shared_from_this() call to the time when FindObject() is called.
106 DoubleBuffer<std::unordered_map<AudioObjectID, std::shared_ptr<Registration>>>
107 registeredObjects_;
108
109 // Serializes (de)registration operations and protects fields below.
110 std::mutex registrationMutex_;
111
112 // Bitset with allocated identifiers.
113 std::vector<BitChunk> allocatedBits_;
114
115 size_t numAllocatedIDs_ = 0;
116 AudioObjectID lastAllocatedID_ = 0;
117
118 // The maximum value below which we're currently trying to keep identifiers.
119 // If the next identifier exceeds this value, and there are quite a lot of
120 // free identifiers below the maximum, we'll reuse one of them. Otherwise,
121 // if there are too little free identifiers below the maximum, we'll increase
122 // the maximum instead.
123 AudioObjectID desiredMaximumID_;
124};
125
126} // namespace aspl
Double buffer.
Operation tracer.
Object dispatcher.
void UnregisterObject(AudioObjectID objectID)
Unregister previously registered object. It's guaranteed that after this method returns,...
AudioObjectID RegisterObject(Object &object, AudioObjectID objectID=kAudioObjectUnknown)
Register new object. If objectID is kAudioObjectUnknown (zero), a new ID is allocated....
std::shared_ptr< Object > FindObject(AudioObjectID objectID) const
Find registered object by ID. Returns null if there is no such object.
Dispatcher(std::shared_ptr< Tracer > tracer={}, AudioObjectID hintMaximumID=1000)
Construct dispatcher. Use tracer if you want to debug dispatcher itself, it is quite verbose.
Base class for audio objects.
Definition Object.hpp:55