Cbeam
Loading...
Searching...
No Matches
stable_reference_buffer.hpp
Go to the documentation of this file.
1/*
2Copyright (c) 2025 acrion innovations GmbH
3Authors: Stefan Zipproth, s.zipproth@acrion.ch
4
5This file is part of Cbeam, see https://github.com/acrion/cbeam and https://cbeam.org
6
7Cbeam is offered under a commercial and under the AGPL license.
8For commercial licensing, contact us at https://acrion.ch/sales. For AGPL licensing, see below.
9
10AGPL licensing:
11
12Cbeam is free software: you can redistribute it and/or modify
13it under the terms of the GNU Affero General Public License as published by
14the Free Software Foundation, either version 3 of the License, or
15(at your option) any later version.
16
17Cbeam is distributed in the hope that it will be useful,
18but WITHOUT ANY WARRANTY; without even the implied warranty of
19MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20GNU Affero General Public License for more details.
21
22You should have received a copy of the GNU Affero General Public License
23along with Cbeam. If not, see <https://www.gnu.org/licenses/>.
24*/
25
26#pragma once
27
28#include <cbeam/concurrency/process.hpp> // for cbeam::concurrency::get_current_process_id
29#include <cbeam/container/buffer.hpp> // for cbeam::container::buffer
30#include <cbeam/container/stable_interprocess_map.hpp> // for cbeam::container::stable_interprocess_map
31#include <cbeam/convert/string.hpp> // for cbeam::convert::to_string
32#include <cbeam/error/logic_error.hpp> // for cbeam::error::logic_error
33#include <cbeam/error/runtime_error.hpp> // for cbeam::error::runtime_error
34#include <cbeam/lifecycle/singleton.hpp> // for cbeam::lifecycle::singleton
35#include <cbeam/logging/log_manager.hpp> // for CBEAM_LOG_DEBUG, CBEAM_LOG
36
37#include <stdlib.h> // for free
38
39#include <cassert> // for assert
40#include <cstddef> // for size_t, std::size_t
41#include <cstdint> // for uint8_t
42
43#include <memory> // for std::shared_ptr, std::__shared_ptr_access, std::allocator
44#include <set> // for std::operator!=, std::set
45#include <string> // for std::operator+, std::to_string, std::char_traits, std::basic_string, std::string
46
47namespace cbeam::container
48{
63 {
64 public:
82 {
83 public:
86 {
87 auto lock = _use_count->get_lock_guard();
88 _use_count->foreach ([this](auto it)
89 { _old_entries.insert(it.first); return true; });
90 _use_count->insert(nullptr, get_initial_use_count() + 1);
91 // CBEAM_LOG_DEBUG("Incrementing initial useCount to " + std::to_string(get_initial_use_count()) + " to delay deallocation of memory created in this scope");
92 }
93
96 {
97 auto lock = _use_count->get_lock_guard();
98 bool done;
99 do
100 {
101 done = false;
102 _use_count->foreach ([this, &done](auto it)
103 {
104 if (_old_entries.count(it.first) == 0)
105 {
106 if (--it.second == 0)
107 {
108 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: Deallocating " + convert::to_string((void*)it.first) + " in context of destruction of class delay_deallocation");
109 free(it.first);
110 _use_count->erase(it.first);
111 done = true;
112 return false;
113 }
114 else
115 {
116 _old_entries.insert(it.first); // protect this entry in case done is set to true and we perform a second scan
117 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: Removed reference to " + convert::to_string((void*)it.first) + " (" + std::to_string(it.second) + " left) in context of destruction of class delay_deallocation");
118 }
119
120 return true;
121 }
122
123 return true; });
124 } while (done);
125 _use_count->insert(nullptr, get_initial_use_count() - 1);
126 // CBEAM_LOG_DEBUG("Resetting initial useCount to " + std::to_string(get_initial_use_count()) + " to leave scope of delayed deallocation");
127 }
128
129 private:
130 std::set<uint8_t*> _old_entries;
131 std::shared_ptr<stable_interprocess_map<uint8_t*, int>> _use_count{get_use_count()};
132 };
133
135 : buffer()
136 {
137 }
138
142 stable_reference_buffer(const std::size_t size, const size_t size_of_type = 1)
143 : buffer(size, size_of_type)
144 {
145 _use_count->update_or_insert(
146 _buffer, [](int& count)
147 { ++count; },
148 get_initial_use_count());
149
150 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: Allocated " + convert::to_string((void*)_buffer) + " with useCount=" + std::to_string(get_initial_use_count()));
151 }
152
154 : buffer(base)
155 {
156 _use_count->update_or_insert(
157 _buffer, [](int& count)
158 { ++count; },
159 get_initial_use_count());
160
161 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: Allocated " + convert::to_string((void*)_buffer) + " with useCount=" + std::to_string(get_initial_use_count()));
162 }
163
165 ~stable_reference_buffer() noexcept override
166 {
168 }
169
173 explicit stable_reference_buffer(const void* address)
174 {
175 _buffer = (uint8_t*)address;
176
177 _use_count->update(
178 _buffer, [](int& count)
179 { ++count; },
180 "cbeam::container::container::stable_reference_buffer: memory address " + convert::to_string((void*)address) + " was not created by cbeam::stable_reference_buffer");
181
182 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: " + std::to_string(_use_count->at(_buffer)) + ". reference to " + convert::to_string((void*)_buffer) + " (added from raw pointer)");
183 }
184
187 {
188 if (other._buffer == nullptr)
189 {
190 throw cbeam::error::runtime_error("cbeam::container::container::stable_reference_buffer: copy constructor has been passed a default constructed (therefore invalid) instance of Memory");
191 }
192
193 _buffer = other._buffer;
194
195 _use_count->update(_buffer, [this, &other](int& count)
196 { ++count; });
197
198 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: " + std::to_string(_use_count->at(_buffer)) + ". reference to " + convert::to_string((void*)_buffer) + " (added from copy constructor)");
199 }
200
202 void append(const void* bufferToAppend, const size_t lengthOfBuffer) override
203 {
204 auto lock = _use_count->get_lock_guard();
205
206 if (_size == 0 && _buffer != nullptr)
207 {
208 throw cbeam::error::logic_error("cbeam::container::container::stable_reference_buffer::append: This instance was created from a raw pointer without knowledge of its buffer length, so appending is not possible.");
209 }
210
211 uint8_t* old_buffer = _buffer;
212
213 buffer::append(bufferToAppend, lengthOfBuffer);
214
215 if (_buffer != old_buffer)
216 {
217 if (old_buffer)
218 {
219 _use_count->insert(_buffer, _use_count->at(old_buffer));
220 _use_count->erase(old_buffer);
221 }
222 else
223 {
224 _use_count->insert(_buffer, get_initial_use_count());
225 }
226 }
227 }
228
248 void* safe_get() const noexcept
249 {
250 if (use_count() <= 1)
251 {
252 CBEAM_LOG("Error: Attempt to access the raw pointer via cbeam::container::stable_reference_buffer::safe_get() without adequate reference count. This operation is blocked and returns a nullptr to prevent unsafe memory access, as the buffer could be invalidated upon destruction of this instance.");
253 return nullptr;
254 }
255
256 return get();
257 }
258
261 {
262 if (this != &other)
263 {
264 if (other._buffer == nullptr)
265 {
266 throw cbeam::error::runtime_error("cbeam::container::container::stable_reference_buffer: copy assignment operator has been passed a default constructed (therefore invalid) instance of Memory");
267 }
268
269 auto lock = _use_count->get_lock_guard();
270 reset();
271
272 assert(is_known(other._buffer));
273 _buffer = other._buffer;
274 _use_count->update(_buffer, [this, &other](auto& count)
275 { ++count; });
276 CBEAM_LOG_DEBUG("cbeam::container::container::stable_reference_buffer: " + std::to_string(_use_count->at(_buffer)) + ". reference to " + convert::to_string((void*)_buffer) + " (added from copy assignment operator)");
277 }
278 return *this;
279 }
280
282 {
283 if (this == &other)
284 {
285 return *this;
286 }
287
288 auto lock = _use_count->get_lock_guard();
289 reset();
290 buffer::operator=(other);
291
292 if (is_known(_buffer))
293 {
294 _use_count->insert(_buffer, _use_count->at(_buffer) + 1);
295 }
296 else
297 {
298 _use_count->insert(_buffer, get_initial_use_count());
299 }
300
301 return *this;
302 }
303
305 static bool is_known(const void* address)
306 {
307 if (!address)
308 {
309 return false; // performance optimization for nullptr
310 }
311
312 auto use_count = get_use_count();
313 auto lock = use_count->get_lock_guard();
314 return use_count->count((uint8_t*)address) == 1;
315 }
316
319 size_t use_count() const
320 {
321 if (_buffer == nullptr)
322 {
323 return 0;
324 }
325 return _use_count->at_or_default(_buffer, 0); // return 0 if the buffer is unknown
326 }
327
329 void reset() noexcept override
330 {
331 try
332 {
333 auto lock = _use_count->get_lock_guard();
334
335 if (is_known(_buffer))
336 {
337 auto updated_value = _use_count->update(_buffer, [](auto& count)
338 { --count; });
339
340 if (updated_value == 0)
341 {
342 CBEAM_LOG_DEBUG("Deallocating " + convert::to_string((void*)_buffer));
343 _use_count->erase(_buffer);
345 }
346 else
347 {
348 CBEAM_LOG_DEBUG("Removed reference to " + convert::to_string((void*)_buffer) + " (" + std::to_string(_use_count->at(_buffer)) + " left)");
349
350 if (updated_value < 0)
351 {
352 CBEAM_LOG("cbeam::stable_reference_buffer::reset: Detected invalid pointer to " + convert::to_string((void*)_buffer));
353 assert(false);
354 }
355 }
356 }
357 _buffer = nullptr;
358 _size = 0;
359 }
360 catch (const std::exception& ex)
361 {
362 CBEAM_LOG("cbeam::container::container::stable_reference_buffer: ");
363 assert(false);
364 }
365 }
366
367 using buffer::swap;
368
372 {
373 auto lock = _use_count->get_lock_guard();
374 buffer::swap(other);
375 }
376
377 private:
388 static std::shared_ptr<stable_interprocess_map<uint8_t*, int>> get_use_count()
389 {
391 "cbeam::memory::stable_reference_buffer::_use_count",
392 std::to_string(concurrency::get_current_process_id()) + ".srb.cbeam",
393 1024);
394 }
395
398 static int get_initial_use_count()
399 {
400 return get_use_count()->at_or_default(nullptr, 1); // we use the key nullptr to store the initial use count
401 }
402
418 std::shared_ptr<stable_interprocess_map<uint8_t*, int>> _use_count{get_use_count()};
419 };
420}
virtual void * get() const noexcept
return a pointer to the managed memory block
Definition buffer.hpp:141
virtual void swap(buffer &other) noexcept
Swaps the contents of this shared_buffer with another shared_buffer.
Definition buffer.hpp:156
virtual void append(const void *buffer_to_append, const std::size_t length_of_buffer)
append the given buffer to the end of the current buffer. If there is no current buffer yet,...
Definition buffer.hpp:96
buffer()=default
Will not create any memory block. Use append to create one or append bytes to an existing one.
std::size_t _size
Definition buffer.hpp:163
virtual void reset() noexcept
Resets the shared_buffer instance, deallocating the managed memory block.
Definition buffer.hpp:147
uint8_t * _buffer
Definition buffer.hpp:164
virtual buffer & operator=(const buffer &other)
make a deep copy of the other buffer, overwriting the content of this buffer
Definition buffer.hpp:118
virtual std::size_t size() const noexcept
returns the size of the buffer in bytes
Definition buffer.hpp:112
~delay_deallocation() noexcept
Exit the protected scope, potentially leading to deallocation of memory.
Definition stable_reference_buffer.hpp:95
delay_deallocation()
Begin a scope that prevents deallocation of memory created within it.
Definition stable_reference_buffer.hpp:85
stable_reference_buffer & operator=(const buffer &other) override
make a deep copy of the other buffer, overwriting the content of this buffer
Definition stable_reference_buffer.hpp:281
~stable_reference_buffer() noexcept override
deallocate the managed memory block, in case this instance olds the last reference to it....
Definition stable_reference_buffer.hpp:165
stable_reference_buffer()
Definition stable_reference_buffer.hpp:134
stable_reference_buffer(const buffer &base)
Definition stable_reference_buffer.hpp:153
stable_reference_buffer(const void *address)
create a managed memory block from address. If this address is a known address, it only increases the...
Definition stable_reference_buffer.hpp:173
void swap(stable_reference_buffer &other)
Swaps the contents of this shared_buffer with another shared_buffer.
Definition stable_reference_buffer.hpp:371
size_t use_count() const
Get the use count of the managed memory block.
Definition stable_reference_buffer.hpp:319
static bool is_known(const void *address)
return if the given address is one of the managed addresses
Definition stable_reference_buffer.hpp:305
void append(const void *bufferToAppend, const size_t lengthOfBuffer) override
append the given buffer to the end of the current buffer. If there is no current buffer yet,...
Definition stable_reference_buffer.hpp:202
stable_reference_buffer & operator=(const stable_reference_buffer &other)
copy assignment means that the copied instance increases the reference count of the managed memory ad...
Definition stable_reference_buffer.hpp:260
stable_reference_buffer(const stable_reference_buffer &other)
copy construction means that the copied instance increases the reference count of the managed memory ...
Definition stable_reference_buffer.hpp:186
void reset() noexcept override
Resets the shared_buffer instance, potentially deallocating the managed memory block.
Definition stable_reference_buffer.hpp:329
stable_reference_buffer(const std::size_t size, const size_t size_of_type=1)
Create a managed memory block with optional element size.
Definition stable_reference_buffer.hpp:142
void * safe_get() const noexcept
Provides safe access to the raw pointer of the managed memory buffer.
Definition stable_reference_buffer.hpp:248
A Cbeam-specific logic error that also behaves like std::logic_error.
Definition logic_error.hpp:46
A Cbeam-specific runtime error that also acts like std::runtime_error.
Definition runtime_error.hpp:46
Definition singleton.hpp:153
#define CBEAM_LOG(s)
Logs a message using cbeam::logging::log_manager.
Definition log_manager.hpp:124
#define CBEAM_LOG_DEBUG(s)
Logs a debug message if CBEAM_DEBUG_LOGGING is enabled.
Definition log_manager.hpp:138
process_id_type get_current_process_id()
Retrieves the current process's identifier in a platform-independent manner.
Definition process.hpp:57
Offers advanced container types with unique approaches to stability and interprocess sharing....
Definition buffer.hpp:44
std::string to_string(const container::buffer &b)
Creates a std::string from the contents of a container::buffer.
Definition buffer.hpp:42