Skip to content

Commit d22d66b

Browse files
committed
Use common memory pool for all message listener queues
1 parent affc1e5 commit d22d66b

File tree

1 file changed

+78
-9
lines changed

1 file changed

+78
-9
lines changed

Source/Pd/MessageListener.h

Lines changed: 78 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,75 @@ class MessageListener {
1919
JUCE_DECLARE_WEAK_REFERENCEABLE(MessageListener)
2020
};
2121

22+
// A simple memory block structure to manage free list.
23+
struct MemoryBlock {
24+
void* ptr;
25+
size_t size;
26+
};
27+
28+
// A custom allocator that doesn't deallocate memory, but reuses it.
29+
template <typename T>
30+
class ReuseAllocator {
31+
public:
32+
using value_type = T;
33+
34+
ReuseAllocator() noexcept {}
35+
36+
template <typename U>
37+
ReuseAllocator(const ReuseAllocator<U>&) noexcept {}
38+
39+
T* allocate(size_t n) {
40+
size_t bytes = n * sizeof(T);
41+
42+
// Check free list for a suitable block
43+
for (auto it = freeList.begin(); it != freeList.end(); ++it) {
44+
if (it->size >= bytes) {
45+
T* result = static_cast<T*>(it->ptr);
46+
freeList.erase(it);
47+
return result;
48+
}
49+
}
50+
51+
// Allocate new memory if no suitable block is found
52+
T* newBlock = static_cast<T*>(std::malloc(bytes));
53+
if (!newBlock) {
54+
throw std::bad_alloc();
55+
}
56+
57+
return newBlock;
58+
}
59+
60+
void reserve(size_t n)
61+
{
62+
for(int i = 0; i < n; i++)
63+
{
64+
// populate the freeList with n addresses
65+
deallocate(allocate(1), 1);
66+
}
67+
}
68+
69+
void deallocate(T* p, size_t n) noexcept {
70+
size_t bytes = n * sizeof(T);
71+
72+
// Push the memory back into the free list
73+
freeList.push_back({p, bytes});
74+
}
75+
76+
template <typename U>
77+
bool operator==(const ReuseAllocator<U>&) const noexcept {
78+
return true;
79+
}
80+
81+
template <typename U>
82+
bool operator!=(const ReuseAllocator<U>&) const noexcept {
83+
return false;
84+
}
85+
86+
private:
87+
// A simple free list to hold reusable memory blocks
88+
static inline std::vector<MemoryBlock> freeList;
89+
};
90+
2291
// MessageDispatcher handles the organising of messages from Pd to the plugdata GUI
2392
// It provides an optimised way to listen to messages within pd from the message thread,
2493
// without performing and memory allocation or locking
@@ -33,13 +102,16 @@ class MessageDispatcher {
33102
};
34103

35104
static constexpr int StackSize = 1 << 20;
36-
using MessageStack = plf::stack<Message>;
37-
using AtomStack = plf::stack<t_atom>;
105+
using MessageStack = plf::stack<Message, ReuseAllocator<Message>>;
106+
using AtomStack = plf::stack<t_atom, ReuseAllocator<t_atom>>;
38107

108+
static inline ReuseAllocator<Message> messageAllocator = ReuseAllocator<Message>();
109+
static inline ReuseAllocator<t_atom> atomAllocator = ReuseAllocator<t_atom>();
110+
39111
struct MessageBuffer
40112
{
41-
MessageStack messages;
42-
AtomStack atoms;
113+
MessageStack messages = MessageStack(messageAllocator);
114+
AtomStack atoms = AtomStack(atomAllocator);
43115
};
44116

45117
public:
@@ -48,11 +120,8 @@ class MessageDispatcher {
48120
usedHashes.reserve(StackSize);
49121
nullListeners.reserve(StackSize);
50122

51-
for(auto& buffer : buffers)
52-
{
53-
buffer.messages.reserve(StackSize);
54-
buffer.atoms.reserve(StackSize);
55-
}
123+
messageAllocator.reserve(StackSize);
124+
atomAllocator.reserve(StackSize);
56125
}
57126

58127
static void enqueueMessage(void* instance, void* target, t_symbol* symbol, int argc, t_atom* argv) noexcept

0 commit comments

Comments
 (0)