1 #define TRACE_NAME "SharedMemoryManager"
6 #include <unordered_map>
7 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!?
11 #include "TRACE/tracemf.h"
12 #include "artdaq-core/Core/SharedMemoryManager.hh"
13 #include "artdaq-core/Utilities/TraceLock.hh"
14 #include "cetlib_except/exception.h"
16 #define TLVL_DETACH 34
17 #define TLVL_DESTRUCTOR 35
18 #define TLVL_ATTACH 36
19 #define TLVL_GETBUFFER 37
20 #define TLVL_BUFFER 40
21 #define TLVL_BUFLCK 41
22 #define TLVL_READREADY 42
23 #define TLVL_WRITEREADY 46
26 #define TLVL_BUFINFO 52
29 #define TLVL_CHKBUFFER 55
31 static std::list<artdaq::SharedMemoryManager const*> instances = std::list<artdaq::SharedMemoryManager const*>();
33 static std::unordered_map<int, struct sigaction> old_actions = std::unordered_map<int, struct sigaction>();
34 static bool sighandler_init =
false;
35 static std::mutex sighandler_mutex;
37 static void signal_handler(
int signum)
40 #if TRACE_REVNUM < 1459
41 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0)
43 TRACE_STREAMER(TLVL_ERROR, TLOG2(
"SharedMemoryManager", 0), 0)
45 <<
"A signal of type " << signum <<
" was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!";
46 for (
auto ii : instances)
59 pthread_sigmask(SIG_UNBLOCK,
nullptr, &set);
60 pthread_sigmask(SIG_UNBLOCK, &set,
nullptr);
62 #if TRACE_REVNUM < 1459
63 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0)
65 TRACE_STREAMER(TLVL_ERROR, TLOG2(
"SharedMemoryManager", 0), 0)
67 <<
"Restoring default signal handler";
68 sigaction(signum, &old_actions[signum],
nullptr);
78 requested_shm_parameters_.buffer_count = buffer_count;
79 requested_shm_parameters_.buffer_size = buffer_size;
80 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
81 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
83 instances.push_back(
this);
86 std::lock_guard<std::mutex> lk(sighandler_mutex);
90 sighandler_init =
true;
91 std::vector<int> signals = {SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP};
92 for (
auto signal : signals)
94 struct sigaction old_action;
95 sigaction(signal,
nullptr, &old_action);
99 if (old_action.sa_handler != SIG_IGN)
101 struct sigaction action;
102 action.sa_handler = signal_handler;
103 sigemptyset(&action.sa_mask);
104 for (
auto sigblk : signals)
106 sigaddset(&action.sa_mask, sigblk);
111 sigaction(signal, &action,
nullptr);
113 old_actions[signal] = old_action;
121 TLOG(TLVL_DESTRUCTOR) <<
"~SharedMemoryManager called";
123 static std::mutex destructor_mutex;
124 std::lock_guard<std::mutex> lk(destructor_mutex);
125 for (
auto it = instances.begin(); it != instances.end(); ++it)
129 it = instances.erase(it);
136 std::lock_guard<std::mutex> lk(sighandler_mutex);
139 if (sighandler_init && instances.empty())
141 sighandler_init =
false;
142 for (
auto signal : old_actions)
144 sigaction(signal.first, &signal.second,
nullptr);
149 TLOG(TLVL_DESTRUCTOR) <<
"~SharedMemoryManager done";
156 if (manager_id_ == 0)
163 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
164 auto start_time = std::chrono::steady_clock::now();
166 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
168 auto available = GetAvailableRAM();
170 TLOG(TLVL_INFO) <<
"Requested shared memory size " << PrintBytes(shmSize)
171 <<
" (" << requested_shm_parameters_.buffer_count <<
" buffers * " << PrintBytes(requested_shm_parameters_.buffer_size) <<
")"
172 <<
", available RAM " << PrintBytes(available);
173 if (shmSize > 0.8 * available)
175 TLOG(TLVL_WARNING) <<
"Requested shared memory size is greater than 80% of available RAM! Allocation of shared memory will likely fail!";
180 if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0)
185 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
186 if (shm_segment_id_ == -1)
188 if (manager_id_ == 0)
190 TLOG(TLVL_ATTACH) <<
"Creating shared memory segment with key " << std::hex << std::showbase << shm_key_ <<
" and size " << std::dec << shmSize;
191 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
193 if (shm_segment_id_ == -1)
195 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key " << std::hex << std::showbase << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
202 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
206 TLOG(TLVL_ATTACH) <<
"shm_key == " << std::hex << std::showbase << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
208 if (shm_segment_id_ > -1)
211 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
212 <<
" and size " << shmSize
214 shm_ptr_ =
static_cast<ShmStruct*
>(shmat(shm_segment_id_,
nullptr, 0));
216 <<
"Attached to shared memory segment at address "
217 << std::hex << std::showbase << static_cast<void*>(shm_ptr_) << std::dec;
218 if ((shm_ptr_ !=
nullptr) && shm_ptr_ !=
reinterpret_cast<void*
>(-1))
220 if (manager_id_ == 0)
222 if (shm_ptr_->ready_magic == 0xCAFE1111)
224 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
225 <<
"Once the system is shut down, you can use one of the following commands "
226 <<
"to clean up this shared memory: 'ipcrm -M " << std::hex << std::showbase << shm_key_
227 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
230 TLOG(TLVL_ATTACH) <<
"Owner initializing Shared Memory";
231 shm_ptr_->next_id = 1;
232 shm_ptr_->next_sequence_id = 0;
233 shm_ptr_->reader_pos = 0;
234 shm_ptr_->writer_pos = 0;
235 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
236 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
237 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
238 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
240 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
241 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
243 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
244 if (getBufferInfo_(ii) ==
nullptr)
248 getBufferInfo_(ii)->writePos = 0;
249 getBufferInfo_(ii)->readPos = 0;
250 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
251 getBufferInfo_(ii)->sem_id = -1;
255 shm_ptr_->ready_magic = 0xCAFE1111;
259 TLOG(TLVL_ATTACH) <<
"Waiting for owner to initalize Shared Memory";
260 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
261 TLOG(TLVL_ATTACH) <<
"Getting ID from Shared Memory";
263 shm_ptr_->lowest_seq_id_read = 0;
264 TLOG(TLVL_ATTACH) <<
"Getting Shared Memory Size parameters";
266 requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count;
267 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
268 for (
int ii = 0; ii < shm_ptr_->buffer_count; ++ii)
270 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
275 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
277 TLOG(TLVL_ATTACH) <<
"Initialization Complete: "
278 <<
"key: " << std::hex << std::showbase << shm_key_
279 <<
", manager ID: " << std::dec << manager_id_
280 <<
", Buffer size: " << shm_ptr_->buffer_size
281 <<
", Buffer count: " << shm_ptr_->buffer_count;
285 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
290 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key " << std::hex << std::showbase << shm_key_
291 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
293 <<
"if a stale shared memory segment needs to "
294 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
300 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading BEGIN";
302 if (!registered_reader_)
304 shm_ptr_->reader_count++;
305 registered_reader_ =
true;
308 std::lock_guard<std::mutex> lk(search_mutex_);
310 auto rp = shm_ptr_->reader_pos.load();
312 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
314 for (
int retry = 0; retry < 5; retry++)
319 ShmBuffer* buffer_ptr =
nullptr;
322 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
324 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
326 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
329 auto buf = getBufferInfo_(buffer);
335 sem = buf->sem.load();
336 sem_id = buf->sem_id.load();
338 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
339 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
340 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
342 if (buf->sequence_id < seqID)
345 seqID = buf->sequence_id;
348 if (seqID == last_seen_id_ + shm_ptr_->reader_count)
356 if (buffer_ptr !=
nullptr)
358 sem = buffer_ptr->sem.load();
359 sem_id = buffer_ptr->sem_id.load();
360 seqID = buffer_ptr->sequence_id.load();
363 TLOG(TLVL_GETBUFFER + 2) <<
"GetBufferForReading: Mode: " << std::boolalpha << shm_ptr_->destructive_read_mode <<
", seqID: " << seqID <<
", last_seen_id_: " << last_seen_id_ <<
", reader_count: " << shm_ptr_->reader_count;
365 if (shm_ptr_->destructive_read_mode && last_seen_id_ > 0
366 && shm_ptr_->reader_count > 1
367 && seqID != last_seen_id_ + shm_ptr_->reader_count
368 && seqID > last_seen_id_ - shm_ptr_->reader_count
369 && seqID < last_seen_id_ + 2 * shm_ptr_->reader_count)
371 TLOG(TLVL_GETBUFFER + 2) <<
"GetBufferForReading: Skipping due to seqID check";
374 TLOG(TLVL_GETBUFFER + 2) <<
"GetBufferForReading: After seqID check";
376 if ((buffer_ptr ==
nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
383 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading Found buffer " << buffer_num;
384 touchBuffer_(buffer_ptr);
385 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
389 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
393 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
395 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
398 buffer_ptr->readPos = 0;
399 touchBuffer_(buffer_ptr);
400 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
402 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
405 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
407 shm_ptr_->lowest_seq_id_read = seqID;
409 last_seen_id_ = seqID;
410 if (shm_ptr_->destructive_read_mode)
412 shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
415 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading returning " << buffer_num;
421 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading returning -1 because no buffers are ready";
427 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
429 if (!registered_writer_)
431 shm_ptr_->writer_count++;
432 registered_writer_ =
true;
435 std::lock_guard<std::mutex> lk(search_mutex_);
437 auto wp = shm_ptr_->writer_pos.load();
439 TLOG(TLVL_GETBUFFER) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
442 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
444 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
448 auto buf = getBufferInfo_(buffer);
454 auto sem = buf->sem.load();
455 auto sem_id = buf->sem_id.load();
457 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
460 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
464 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
468 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
472 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
473 buf->sequence_id = ++shm_ptr_->next_sequence_id;
475 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
480 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting returning empty buffer " << buffer;
488 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
490 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
494 auto buf = getBufferInfo_(buffer);
500 auto sem = buf->sem.load();
501 auto sem_id = buf->sem_id.load();
503 if (sem == BufferSemaphoreFlags::Full && sem_id == -1)
506 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
510 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
514 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
518 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
519 buf->sequence_id = ++shm_ptr_->next_sequence_id;
521 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
526 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting returning full buffer (overwrite mode) " << buffer;
532 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
534 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
538 auto buf = getBufferInfo_(buffer);
544 auto sem = buf->sem.load();
545 auto sem_id = buf->sem_id.load();
547 if (sem == BufferSemaphoreFlags::Reading)
550 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
554 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
558 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
562 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
563 buf->sequence_id = ++shm_ptr_->next_sequence_id;
565 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
570 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting clobbering reader on buffer " << buffer <<
" (overwrite mode)";
575 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
585 TLOG(TLVL_READREADY) << std::hex << std::showbase << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
586 std::unique_lock<std::mutex> lk(search_mutex_);
587 TLOG(TLVL_READREADY) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
590 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
593 TLOG(TLVL_READREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
596 auto buf = getBufferInfo_(ii);
603 TLOG(TLVL_READREADY + 2) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
": sem=" << FlagToString(buf->sem) <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
" )";
605 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
608 TLOG(TLVL_READREADY + 3) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
623 TLOG(TLVL_WRITEREADY) << std::hex << std::showbase << shm_key_ <<
" WriteReadyCount BEGIN" << std::dec;
624 std::unique_lock<std::mutex> lk(search_mutex_);
626 TLOG(TLVL_WRITEREADY) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
628 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
632 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
635 auto buf = getBufferInfo_(ii);
640 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
643 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
657 TLOG(TLVL_READREADY) << std::hex << std::showbase << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
658 std::unique_lock<std::mutex> lk(search_mutex_);
661 auto rp = shm_ptr_->reader_pos.load();
663 TLOG(TLVL_READREADY) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
665 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
667 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
670 TLOG(TLVL_READREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
673 auto buf = getBufferInfo_(buffer);
680 TLOG(TLVL_READREADY + 2) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
": sem=" << FlagToString(buf->sem) <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
" )"
681 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
684 if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
686 TLOG(TLVL_READREADY + 3) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
700 TLOG(TLVL_WRITEREADY) << std::hex << std::showbase << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
702 std::lock_guard<std::mutex> lk(search_mutex_);
705 auto wp = shm_ptr_->writer_pos.load();
707 TLOG(TLVL_WRITEREADY) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
709 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
711 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
712 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
714 auto buf = getBufferInfo_(buffer);
719 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
721 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_
723 <<
" ReadyForWrite: Buffer " << ii <<
" is either empty or available for overwrite.";
732 std::deque<int> output;
733 size_t buffer_count = size();
734 if (!IsValid() || buffer_count == 0)
738 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
741 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
742 std::lock_guard<std::mutex> lk(search_mutex_);
743 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
745 for (
size_t ii = 0; ii < buffer_count; ++ii)
747 auto buf = getBufferInfo_(ii);
752 if (buf->sem_id == manager_id_)
754 output.push_back(ii);
760 for (
size_t ii = 0; ii < buffer_count; ++ii)
762 auto buf = getBufferInfo_(ii);
767 if (buf->sem_id == manager_id_)
769 output.push_back(ii);
774 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << buffer_count <<
" buffers.";
780 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
782 if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
784 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
787 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
788 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
789 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
792 auto buf = getBufferInfo_(buffer);
799 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
800 return buf->writePos;
805 TLOG(TLVL_POS) <<
"ResetReadPos(" << buffer <<
") called.";
807 if (buffer >= shm_ptr_->buffer_count)
809 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
812 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
813 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
814 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
817 auto buf = getBufferInfo_(buffer);
818 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
825 TLOG(TLVL_POS) <<
"ResetReadPos(" << buffer <<
") ended.";
830 TLOG(TLVL_POS + 1) <<
"ResetWritePos(" << buffer <<
") called.";
832 if (buffer >= shm_ptr_->buffer_count)
834 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
837 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
838 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
839 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
842 auto buf = getBufferInfo_(buffer);
847 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
851 TLOG(TLVL_POS + 1) <<
"ResetWritePos(" << buffer <<
") ended.";
856 TLOG(TLVL_POS) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
858 if (buffer >= shm_ptr_->buffer_count)
860 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
863 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
864 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
865 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
867 auto buf = getBufferInfo_(buffer);
868 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
873 TLOG(TLVL_POS) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
874 buf->readPos = buf->readPos + read;
875 TLOG(TLVL_POS) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
878 Detach(
true,
"LogicError",
"Cannot increment Read pos by 0! (buffer=" + std::to_string(buffer) +
", readPos=" + std::to_string(buf->readPos) +
", writePos=" + std::to_string(buf->writePos) +
")");
884 TLOG(TLVL_POS + 1) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
886 if (buffer >= shm_ptr_->buffer_count)
888 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
891 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
892 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
893 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
895 auto buf = getBufferInfo_(buffer);
900 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
902 if (buf->writePos + written > shm_ptr_->buffer_size)
904 TLOG(TLVL_ERROR) <<
"Requested write size is larger than the buffer size! (sz=" << std::dec << shm_ptr_->buffer_size <<
", cur + req=" << std::dec << buf->writePos + written <<
", diff=" << std::dec << (buf->writePos + written - shm_ptr_->buffer_size) <<
")";
907 TLOG(TLVL_POS + 1) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
908 buf->writePos += written;
909 TLOG(TLVL_POS + 1) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
912 Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
920 TLOG(TLVL_POS + 2) <<
"MoreDataInBuffer(" << buffer <<
") called.";
922 if (buffer >= shm_ptr_->buffer_count)
924 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
927 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
928 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
929 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
931 auto buf = getBufferInfo_(buffer);
936 TLOG(TLVL_POS + 2) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
937 return buf->readPos < buf->writePos;
942 if (buffer >= shm_ptr_->buffer_count)
944 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
947 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
948 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
949 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
951 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
956 if (buffer >= shm_ptr_->buffer_count)
958 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
961 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
962 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
963 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
966 auto shmBuf = getBufferInfo_(buffer);
967 if (shmBuf ==
nullptr)
971 touchBuffer_(shmBuf);
972 if (shmBuf->sem_id == manager_id_)
974 if (shmBuf->sem != BufferSemaphoreFlags::Full)
976 shmBuf->sem = BufferSemaphoreFlags::Full;
979 shmBuf->sem_id = destination;
985 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
986 if (buffer >= shm_ptr_->buffer_count)
988 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
990 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
992 auto shmBuf = getBufferInfo_(buffer);
993 if (shmBuf ==
nullptr)
999 auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
1002 touchBuffer_(shmBuf);
1004 shmBuf->readPos = 0;
1006 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
1008 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" (SeqID " << shmBuf->sequence_id <<
") to Empty state";
1009 shmBuf->writePos = 0;
1010 shmBuf->sem = BufferSemaphoreFlags::Empty;
1011 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
1013 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
1014 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1018 shmBuf->sem = BufferSemaphoreFlags::Full;
1020 shmBuf->sem_id = -1;
1021 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
1026 if (buffer >= shm_ptr_->buffer_count)
1028 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1033 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1037 auto shmBuf = getBufferInfo_(buffer);
1038 if (shmBuf ==
nullptr)
1053 if (delta > 0xFFFFFFFF)
1055 TLOG(TLVL_RESET) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
1059 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1063 TLOG(TLVL_RESET) <<
"Buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf) <<
" is stale, time=" <<
TimeUtils::gettimeofday_us() <<
", last touch=" << shmBuf->last_touch_time <<
", d=" << delta <<
", timeout=" << shm_ptr_->buffer_timeout_us;
1065 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1070 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1072 TLOG(TLVL_RESET) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
1073 shmBuf->writePos = 0;
1074 shmBuf->sem = BufferSemaphoreFlags::Empty;
1075 shmBuf->sem_id = -1;
1076 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1078 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1083 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1087 if (delta <= shm_ptr_->buffer_timeout_us)
1091 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf)
1092 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
1093 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
1094 shmBuf->readPos = 0;
1095 shmBuf->sem = BufferSemaphoreFlags::Full;
1096 shmBuf->sem_id = -1;
1109 struct shmid_ds info;
1110 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1113 TLOG(TLVL_BUFINFO) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1117 if ((info.shm_perm.mode & SHM_DEST) != 0)
1119 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
1133 struct shmid_ds info;
1134 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1137 TLOG(TLVL_BUFINFO) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1141 return info.shm_nattch;
1146 TLOG(TLVL_WRITE) <<
"Write BEGIN";
1147 if (buffer >= shm_ptr_->buffer_count)
1149 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1151 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1153 auto shmBuf = getBufferInfo_(buffer);
1154 if (shmBuf ==
nullptr)
1158 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1159 touchBuffer_(shmBuf);
1160 TLOG(TLVL_WRITE) <<
"Buffer Write Pos is " << std::dec << shmBuf->writePos <<
", write size is " << size;
1161 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1163 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << std::dec << shm_ptr_->buffer_size
1164 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
1165 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1168 auto pos = GetWritePos(buffer);
1169 memcpy(pos, data, size);
1170 touchBuffer_(shmBuf);
1171 shmBuf->writePos = shmBuf->writePos + size;
1173 auto last_seen = last_seen_id_.load();
1174 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1176 TLOG(TLVL_WRITE) <<
"Write END";
1182 if (buffer >= shm_ptr_->buffer_count)
1184 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1186 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1188 auto shmBuf = getBufferInfo_(buffer);
1189 if (shmBuf ==
nullptr)
1193 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1194 touchBuffer_(shmBuf);
1195 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1197 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1198 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
1199 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
1202 auto pos = GetReadPos(buffer);
1203 TLOG(TLVL_READ) <<
"Before memcpy in Read(), size is " << size;
1204 memcpy(data, pos, size);
1205 TLOG(TLVL_READ) <<
"After memcpy in Read()";
1206 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
1209 shmBuf->readPos += size;
1210 touchBuffer_(shmBuf);
1218 if (shm_ptr_ ==
nullptr)
1220 return "Not connected to shared memory";
1222 std::ostringstream ostr;
1223 ostr <<
"ShmStruct: " << std::endl
1224 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
1225 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
1226 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
1227 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
1228 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
1229 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1230 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
1231 <<
"Number of Writers: " << shm_ptr_->writer_count << std::endl
1232 <<
"Number of Readers: " << shm_ptr_->reader_count << std::endl
1233 <<
"Ready Magic Bytes: " << std::hex << std::showbase << shm_ptr_->ready_magic << std::dec << std::endl
1236 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1238 auto buf = getBufferInfo_(ii);
1244 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1245 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1246 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1247 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1248 <<
"sem: " << FlagToString(buf->sem) << std::endl
1249 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1250 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1259 auto buf = getBufferInfo_(buffer);
1264 return bufferStart_(buffer) + buf->readPos;
1268 auto buf = getBufferInfo_(buffer);
1273 return bufferStart_(buffer) + buf->writePos;
1278 return bufferStart_(buffer);
1283 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1284 for (
size_t ii = 0; ii < size(); ++ii)
1286 auto buf = getBufferInfo_(ii);
1287 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1292 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1294 if (buffer ==
nullptr)
1298 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1302 TLOG(TLVL_CHKBUFFER) <<
"checkBuffer_: Checking that buffer with SeqID " << buffer->sequence_id <<
" has sem_id " << manager_id_ <<
" (Current: " << buffer->sem_id <<
") and is in state " << FlagToString(flags) <<
" (current: " << FlagToString(buffer->sem) <<
")";
1305 if (buffer->sem != flags)
1307 Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1309 if (buffer->sem_id != manager_id_)
1311 Detach(
true,
"OwnerAccessViolation",
"Shared Memory buffer is not owned by this manager instance! (Expected: " + std::to_string(manager_id_) +
", Actual: " + std::to_string(buffer->sem_id) +
")");
1314 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1318 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer with SeqID " << buffer->sequence_id <<
"!"
1319 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1320 << R
"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1326 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1328 if ((buffer ==
nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1333 TLOG(TLVL_CHKBUFFER + 1) <<
"touchBuffer_: Touching buffer at " <<
static_cast<void*
>(buffer) <<
" with sequence_id " << buffer->sequence_id;
1339 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1342 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1343 auto bufs = GetBuffersOwnedByManager(
false);
1344 for (
auto buf : bufs)
1346 auto shmBuf = getBufferInfo_(buf);
1347 if (shmBuf ==
nullptr)
1351 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1353 shmBuf->sem = BufferSemaphoreFlags::Empty;
1355 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1357 shmBuf->sem = BufferSemaphoreFlags::Full;
1359 shmBuf->sem_id = -1;
1361 if (registered_reader_)
1363 shm_ptr_->reader_count--;
1364 registered_reader_ =
false;
1366 if (registered_writer_)
1368 shm_ptr_->writer_count--;
1369 registered_writer_ =
false;
1373 if (shm_ptr_ !=
nullptr)
1375 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1380 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1382 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1383 shmctl(shm_segment_id_, IPC_RMID,
nullptr);
1384 shm_segment_id_ = -1;
1390 if (!category.empty() && !message.empty())
1392 TLOG(TLVL_ERROR) << category <<
": " << message;
1396 TRACE_CNTL(
"modeM", (uint64_t)0);
1397 throw cet::exception(category) << message;
void Detach(bool throwException=false, const std::string &category="", const std::string &message="", bool force=false)
Detach from the Shared Memory segment, optionally throwing a cet::exception with the specified proper...
bool Read(int buffer, void *data, size_t size)
Read size bytes of data from buffer into the given pointer.
void IncrementReadPos(int buffer, size_t read)
Increment the read position for a given buffer.
bool Attach(size_t timeout_usec=0)
Reconnect to the shared memory segment.
std::deque< int > GetBuffersOwnedByManager(bool locked=true)
Get the list of all buffers currently owned by this manager instance.
void ResetReadPos(int buffer)
Set the read position of the given buffer to the beginning of the buffer.
bool MoreDataInBuffer(int buffer)
Determine if more data is available to be read, based on the read position and data size...
virtual std::string toString()
Write information about the SharedMemory to a string.
size_t GetElapsedTimeMicroseconds(std::chrono::steady_clock::time_point then, std::chrono::steady_clock::time_point now=std::chrono::steady_clock::now())
Gets the number of microseconds in the given time interval
std::vector< std::pair< int, BufferSemaphoreFlags > > GetBufferReport()
Get a report on the status of each buffer.
BufferSemaphoreFlags
The BufferSemaphoreFlags enumeration represents the different possible "states" of a given shared mem...
size_t BufferDataSize(int buffer)
Get the current size of the buffer's data.
virtual ~SharedMemoryManager() noexcept
SharedMemoryManager Destructor.
The SharedMemoryManager creates a Shared Memory area which is divided into a number of fixed-size buf...
size_t ReadReadyCount()
Count the number of buffers that are ready for reading.
bool ResetBuffer(int buffer)
Resets the buffer from Reading to Full. This operation will only have an effect if performed by the o...
bool IncrementWritePos(int buffer, size_t written)
Increment the write position for a given buffer.
bool IsEndOfData() const
Determine whether the Shared Memory is marked for destruction (End of Data)
bool CheckBuffer(int buffer, BufferSemaphoreFlags flags)
Check both semaphore conditions (Mode flag and manager ID) for a given buffer.
void MarkBufferFull(int buffer, int destination=-1)
Release a buffer from a writer, marking it Full and ready for a reader.
void MarkBufferEmpty(int buffer, bool force=false, bool detachOnException=true)
Release a buffer from a reader, marking it Empty and ready to accept more data.
void ResetWritePos(int buffer)
Set the write position of the given buffer to the beginning of the buffer.
void * GetWritePos(int buffer)
Get a pointer to the current write position of the buffer.
size_t Write(int buffer, void *data, size_t size)
Write size bytes of data from the given pointer to a buffer.
void * GetReadPos(int buffer)
Get a pointer to the current read position of the buffer.
void * GetBufferStart(int buffer)
Get a pointer to the start position of the buffer.
SharedMemoryManager(uint32_t shm_key, size_t buffer_count=0, size_t buffer_size=0, uint64_t buffer_timeout_us=100 *1000000, bool destructive_read_mode=true)
SharedMemoryManager Constructor.
bool ReadyForRead()
Whether any buffer is ready for read.
int GetBufferForWriting(bool overwrite)
Finds a buffer that is ready to be written to, and reserves it for the calling manager.
uint16_t GetAttachedCount() const
Get the number of attached SharedMemoryManagers.
size_t WriteReadyCount(bool overwrite)
Count the number of buffers that are ready for writing.
uint64_t gettimeofday_us()
Get the current time of day in microseconds (from gettimeofday system call)
int GetBufferForReading()
Finds a buffer that is ready to be read, and reserves it for the calling manager. ...
virtual bool ReadyForWrite(bool overwrite)
Whether any buffer is available for write.