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);
170 if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0)
175 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
176 if (shm_segment_id_ == -1)
178 if (manager_id_ == 0)
180 TLOG(TLVL_ATTACH) <<
"Creating shared memory segment with key " << std::hex << std::showbase << shm_key_ <<
" and size " << std::dec << shmSize;
181 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
183 if (shm_segment_id_ == -1)
185 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key " << std::hex << std::showbase << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
192 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
196 TLOG(TLVL_ATTACH) <<
"shm_key == " << std::hex << std::showbase << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
198 if (shm_segment_id_ > -1)
201 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
202 <<
" and size " << shmSize
204 shm_ptr_ =
static_cast<ShmStruct*
>(shmat(shm_segment_id_,
nullptr, 0));
206 <<
"Attached to shared memory segment at address "
207 << std::hex << std::showbase << static_cast<void*>(shm_ptr_) << std::dec;
208 if ((shm_ptr_ !=
nullptr) && shm_ptr_ !=
reinterpret_cast<void*
>(-1))
210 if (manager_id_ == 0)
212 if (shm_ptr_->ready_magic == 0xCAFE1111)
214 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
215 <<
"Once the system is shut down, you can use one of the following commands "
216 <<
"to clean up this shared memory: 'ipcrm -M " << std::hex << std::showbase << shm_key_
217 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
220 TLOG(TLVL_ATTACH) <<
"Owner initializing Shared Memory";
221 shm_ptr_->next_id = 1;
222 shm_ptr_->next_sequence_id = 0;
223 shm_ptr_->reader_pos = 0;
224 shm_ptr_->writer_pos = 0;
225 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
226 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
227 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
228 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
230 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
231 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
233 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
234 if (getBufferInfo_(ii) ==
nullptr)
238 getBufferInfo_(ii)->writePos = 0;
239 getBufferInfo_(ii)->readPos = 0;
240 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
241 getBufferInfo_(ii)->sem_id = -1;
245 shm_ptr_->ready_magic = 0xCAFE1111;
249 TLOG(TLVL_ATTACH) <<
"Waiting for owner to initalize Shared Memory";
250 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
251 TLOG(TLVL_ATTACH) <<
"Getting ID from Shared Memory";
253 shm_ptr_->lowest_seq_id_read = 0;
254 TLOG(TLVL_ATTACH) <<
"Getting Shared Memory Size parameters";
256 requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count;
257 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
258 for (
int ii = 0; ii < shm_ptr_->buffer_count; ++ii)
260 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
265 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
267 TLOG(TLVL_ATTACH) <<
"Initialization Complete: "
268 <<
"key: " << std::hex << std::showbase << shm_key_
269 <<
", manager ID: " << std::dec << manager_id_
270 <<
", Buffer size: " << shm_ptr_->buffer_size
271 <<
", Buffer count: " << shm_ptr_->buffer_count;
275 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
280 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key " << std::hex << std::showbase << shm_key_
281 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
283 <<
"if a stale shared memory segment needs to "
284 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
290 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading BEGIN";
292 if (!registered_reader_)
294 shm_ptr_->reader_count++;
295 registered_reader_ =
true;
298 std::lock_guard<std::mutex> lk(search_mutex_);
300 auto rp = shm_ptr_->reader_pos.load();
302 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
304 for (
int retry = 0; retry < 5; retry++)
309 ShmBuffer* buffer_ptr =
nullptr;
312 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
314 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
316 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
319 auto buf = getBufferInfo_(buffer);
325 sem = buf->sem.load();
326 sem_id = buf->sem_id.load();
328 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
329 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
330 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
332 if (buf->sequence_id < seqID)
335 seqID = buf->sequence_id;
338 if (seqID == last_seen_id_ + shm_ptr_->reader_count)
346 if (buffer_ptr !=
nullptr)
348 sem = buffer_ptr->sem.load();
349 sem_id = buffer_ptr->sem_id.load();
350 seqID = buffer_ptr->sequence_id.load();
353 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;
355 if (shm_ptr_->destructive_read_mode && last_seen_id_ > 0
356 && shm_ptr_->reader_count > 1
357 && seqID != last_seen_id_ + shm_ptr_->reader_count
358 && seqID > last_seen_id_ - shm_ptr_->reader_count
359 && seqID < last_seen_id_ + 2 * shm_ptr_->reader_count)
361 TLOG(TLVL_GETBUFFER + 2) <<
"GetBufferForReading: Skipping due to seqID check";
364 TLOG(TLVL_GETBUFFER + 2) <<
"GetBufferForReading: After seqID check";
366 if ((buffer_ptr ==
nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
373 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading Found buffer " << buffer_num;
374 touchBuffer_(buffer_ptr);
375 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
379 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
383 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
385 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
388 buffer_ptr->readPos = 0;
389 touchBuffer_(buffer_ptr);
390 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
392 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
395 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
397 shm_ptr_->lowest_seq_id_read = seqID;
399 last_seen_id_ = seqID;
400 if (shm_ptr_->destructive_read_mode)
402 shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
405 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading returning " << buffer_num;
411 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading returning -1 because no buffers are ready";
417 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
419 if (!registered_writer_)
421 shm_ptr_->writer_count++;
422 registered_writer_ =
true;
425 std::lock_guard<std::mutex> lk(search_mutex_);
427 auto wp = shm_ptr_->writer_pos.load();
429 TLOG(TLVL_GETBUFFER) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
432 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
434 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
438 auto buf = getBufferInfo_(buffer);
444 auto sem = buf->sem.load();
445 auto sem_id = buf->sem_id.load();
447 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
450 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
454 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
458 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
462 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
463 buf->sequence_id = ++shm_ptr_->next_sequence_id;
465 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
470 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting returning empty buffer " << buffer;
478 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
480 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
484 auto buf = getBufferInfo_(buffer);
490 auto sem = buf->sem.load();
491 auto sem_id = buf->sem_id.load();
493 if (sem == BufferSemaphoreFlags::Full)
496 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
500 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
504 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
508 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
509 buf->sequence_id = ++shm_ptr_->next_sequence_id;
511 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
516 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting returning full buffer (overwrite mode) " << buffer;
522 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
524 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
528 auto buf = getBufferInfo_(buffer);
534 auto sem = buf->sem.load();
535 auto sem_id = buf->sem_id.load();
537 if (sem == BufferSemaphoreFlags::Reading)
540 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
544 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
548 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
552 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
553 buf->sequence_id = ++shm_ptr_->next_sequence_id;
555 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
560 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting clobbering reader on buffer " << buffer <<
" (overwrite mode)";
565 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
575 TLOG(TLVL_READREADY) << std::hex << std::showbase << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
576 std::unique_lock<std::mutex> lk(search_mutex_);
577 TLOG(TLVL_READREADY) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
580 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
583 TLOG(TLVL_READREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
586 auto buf = getBufferInfo_(ii);
593 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 <<
" )";
595 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_))
598 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.";
613 TLOG(TLVL_WRITEREADY) << std::hex << std::showbase << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
614 std::unique_lock<std::mutex> lk(search_mutex_);
616 TLOG(TLVL_WRITEREADY) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
618 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
622 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
625 auto buf = getBufferInfo_(ii);
630 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
633 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
647 TLOG(TLVL_READREADY) << std::hex << std::showbase << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
648 std::unique_lock<std::mutex> lk(search_mutex_);
651 auto rp = shm_ptr_->reader_pos.load();
653 TLOG(TLVL_READREADY) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
655 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
657 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
660 TLOG(TLVL_READREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
663 auto buf = getBufferInfo_(buffer);
670 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 <<
" )"
671 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
674 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_))
676 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.";
690 TLOG(TLVL_WRITEREADY) << std::hex << std::showbase << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
692 std::lock_guard<std::mutex> lk(search_mutex_);
695 auto wp = shm_ptr_->writer_pos.load();
697 TLOG(TLVL_WRITEREADY) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
699 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
701 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
702 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
704 auto buf = getBufferInfo_(buffer);
709 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
711 TLOG(TLVL_WRITEREADY + 1) << std::hex << std::showbase << shm_key_
713 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
722 std::deque<int> output;
723 size_t buffer_count = size();
724 if (!IsValid() || buffer_count == 0)
728 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
731 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
732 std::lock_guard<std::mutex> lk(search_mutex_);
733 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
735 for (
size_t ii = 0; ii < buffer_count; ++ii)
737 auto buf = getBufferInfo_(ii);
742 if (buf->sem_id == manager_id_)
744 output.push_back(ii);
750 for (
size_t ii = 0; ii < buffer_count; ++ii)
752 auto buf = getBufferInfo_(ii);
757 if (buf->sem_id == manager_id_)
759 output.push_back(ii);
764 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << buffer_count <<
" buffers.";
770 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
772 if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
774 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
777 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
778 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
779 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
782 auto buf = getBufferInfo_(buffer);
789 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
790 return buf->writePos;
795 TLOG(TLVL_POS) <<
"ResetReadPos(" << buffer <<
") called.";
797 if (buffer >= shm_ptr_->buffer_count)
799 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
802 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
803 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
804 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
807 auto buf = getBufferInfo_(buffer);
808 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
815 TLOG(TLVL_POS) <<
"ResetReadPos(" << buffer <<
") ended.";
820 TLOG(TLVL_POS + 1) <<
"ResetWritePos(" << buffer <<
") called.";
822 if (buffer >= shm_ptr_->buffer_count)
824 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
827 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
828 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
829 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
832 auto buf = getBufferInfo_(buffer);
837 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
841 TLOG(TLVL_POS + 1) <<
"ResetWritePos(" << buffer <<
") ended.";
846 TLOG(TLVL_POS) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
848 if (buffer >= shm_ptr_->buffer_count)
850 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
853 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
854 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
855 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
857 auto buf = getBufferInfo_(buffer);
858 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
863 TLOG(TLVL_POS) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
864 buf->readPos = buf->readPos + read;
865 TLOG(TLVL_POS) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
868 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) +
")");
874 TLOG(TLVL_POS + 1) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
876 if (buffer >= shm_ptr_->buffer_count)
878 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
881 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
882 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
883 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
885 auto buf = getBufferInfo_(buffer);
890 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
892 if (buf->writePos + written > shm_ptr_->buffer_size)
894 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) <<
")";
897 TLOG(TLVL_POS + 1) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
898 buf->writePos += written;
899 TLOG(TLVL_POS + 1) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
902 Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
910 TLOG(TLVL_POS + 2) <<
"MoreDataInBuffer(" << buffer <<
") called.";
912 if (buffer >= shm_ptr_->buffer_count)
914 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
917 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
918 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
919 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
921 auto buf = getBufferInfo_(buffer);
926 TLOG(TLVL_POS + 2) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
927 return buf->readPos < buf->writePos;
932 if (buffer >= shm_ptr_->buffer_count)
934 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
937 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
938 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
939 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
941 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
946 if (buffer >= shm_ptr_->buffer_count)
948 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
951 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
952 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
953 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
956 auto shmBuf = getBufferInfo_(buffer);
957 if (shmBuf ==
nullptr)
961 touchBuffer_(shmBuf);
962 if (shmBuf->sem_id == manager_id_)
964 if (shmBuf->sem != BufferSemaphoreFlags::Full)
966 shmBuf->sem = BufferSemaphoreFlags::Full;
969 shmBuf->sem_id = destination;
975 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
976 if (buffer >= shm_ptr_->buffer_count)
978 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
980 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
982 auto shmBuf = getBufferInfo_(buffer);
983 if (shmBuf ==
nullptr)
989 auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
992 touchBuffer_(shmBuf);
995 shmBuf->sem = BufferSemaphoreFlags::Full;
997 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
999 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
1000 shmBuf->writePos = 0;
1001 shmBuf->sem = BufferSemaphoreFlags::Empty;
1002 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
1004 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
1005 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1008 shmBuf->sem_id = -1;
1009 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
1014 if (buffer >= shm_ptr_->buffer_count)
1016 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1021 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1025 auto shmBuf = getBufferInfo_(buffer);
1026 if (shmBuf ==
nullptr)
1041 if (delta > 0xFFFFFFFF)
1043 TLOG(TLVL_RESET) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
1047 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1051 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;
1053 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1058 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1060 TLOG(TLVL_RESET) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
1061 shmBuf->writePos = 0;
1062 shmBuf->sem = BufferSemaphoreFlags::Empty;
1063 shmBuf->sem_id = -1;
1064 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1066 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1071 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1075 if (delta <= shm_ptr_->buffer_timeout_us)
1079 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf)
1080 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
1081 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
1082 shmBuf->readPos = 0;
1083 shmBuf->sem = BufferSemaphoreFlags::Full;
1084 shmBuf->sem_id = -1;
1097 struct shmid_ds info;
1098 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1101 TLOG(TLVL_BUFINFO) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1105 if ((info.shm_perm.mode & SHM_DEST) != 0)
1107 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
1121 struct shmid_ds info;
1122 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1125 TLOG(TLVL_BUFINFO) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1129 return info.shm_nattch;
1134 TLOG(TLVL_WRITE) <<
"Write BEGIN";
1135 if (buffer >= shm_ptr_->buffer_count)
1137 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1139 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1141 auto shmBuf = getBufferInfo_(buffer);
1142 if (shmBuf ==
nullptr)
1146 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1147 touchBuffer_(shmBuf);
1148 TLOG(TLVL_WRITE) <<
"Buffer Write Pos is " << std::dec << shmBuf->writePos <<
", write size is " << size;
1149 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1151 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << std::dec << shm_ptr_->buffer_size
1152 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
1153 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1156 auto pos = GetWritePos(buffer);
1157 memcpy(pos, data, size);
1158 touchBuffer_(shmBuf);
1159 shmBuf->writePos = shmBuf->writePos + size;
1161 auto last_seen = last_seen_id_.load();
1162 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1164 TLOG(TLVL_WRITE) <<
"Write END";
1170 if (buffer >= shm_ptr_->buffer_count)
1172 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1174 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1176 auto shmBuf = getBufferInfo_(buffer);
1177 if (shmBuf ==
nullptr)
1181 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1182 touchBuffer_(shmBuf);
1183 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1185 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1186 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
1187 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
1190 auto pos = GetReadPos(buffer);
1191 TLOG(TLVL_READ) <<
"Before memcpy in Read(), size is " << size;
1192 memcpy(data, pos, size);
1193 TLOG(TLVL_READ) <<
"After memcpy in Read()";
1194 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
1197 shmBuf->readPos += size;
1198 touchBuffer_(shmBuf);
1206 if (shm_ptr_ ==
nullptr)
1208 return "Not connected to shared memory";
1210 std::ostringstream ostr;
1211 ostr <<
"ShmStruct: " << std::endl
1212 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
1213 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
1214 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
1215 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
1216 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
1217 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1218 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
1219 <<
"Number of Writers: " << shm_ptr_->writer_count << std::endl
1220 <<
"Number of Readers: " << shm_ptr_->reader_count << std::endl
1221 <<
"Ready Magic Bytes: " << std::hex << std::showbase << shm_ptr_->ready_magic << std::dec << std::endl
1224 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1226 auto buf = getBufferInfo_(ii);
1232 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1233 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1234 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1235 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1236 <<
"sem: " << FlagToString(buf->sem) << std::endl
1237 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1238 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1247 auto buf = getBufferInfo_(buffer);
1252 return bufferStart_(buffer) + buf->readPos;
1256 auto buf = getBufferInfo_(buffer);
1261 return bufferStart_(buffer) + buf->writePos;
1266 return bufferStart_(buffer);
1271 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1272 for (
size_t ii = 0; ii < size(); ++ii)
1274 auto buf = getBufferInfo_(ii);
1275 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1280 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1282 if (buffer ==
nullptr)
1286 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1290 TLOG(TLVL_CHKBUFFER) <<
"checkBuffer_: Checking that buffer " << buffer->sequence_id <<
" has sem_id " << manager_id_ <<
" (Current: " << buffer->sem_id <<
") and is in state " << FlagToString(flags) <<
" (current: " << FlagToString(buffer->sem) <<
")";
1293 if (buffer->sem != flags)
1295 Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1297 if (buffer->sem_id != manager_id_)
1299 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) +
")");
1302 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1306 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1307 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1308 << R
"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1314 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1316 if ((buffer ==
nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1320 TLOG(TLVL_CHKBUFFER + 1) <<
"touchBuffer_: Touching buffer at " <<
static_cast<void*
>(buffer) <<
" with sequence_id " << buffer->sequence_id;
1326 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1329 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1330 auto bufs = GetBuffersOwnedByManager(
false);
1331 for (
auto buf : bufs)
1333 auto shmBuf = getBufferInfo_(buf);
1334 if (shmBuf ==
nullptr)
1338 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1340 shmBuf->sem = BufferSemaphoreFlags::Empty;
1342 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1344 shmBuf->sem = BufferSemaphoreFlags::Full;
1346 shmBuf->sem_id = -1;
1348 if (registered_reader_)
1350 shm_ptr_->reader_count--;
1351 registered_reader_ =
false;
1353 if (registered_writer_)
1355 shm_ptr_->writer_count--;
1356 registered_writer_ =
false;
1360 if (shm_ptr_ !=
nullptr)
1362 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1367 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1369 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1370 shmctl(shm_segment_id_, IPC_RMID,
nullptr);
1371 shm_segment_id_ = -1;
1377 if (!category.empty() && !message.empty())
1379 TLOG(TLVL_ERROR) << category <<
": " << message;
1383 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.