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 "artdaq-core/Core/SharedMemoryManager.hh"
12 #include "artdaq-core/Utilities/TraceLock.hh"
13 #include "cetlib_except/exception.h"
14 #include "TRACE/tracemf.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 0x" << std::hex << 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 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
192 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
196 TLOG(TLVL_ATTACH) <<
"shm_key == 0x" << std::hex << 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 << 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 0x" << std::hex << 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: 0x" << std::hex << 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 0x" << std::hex << 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 std::lock_guard<std::mutex> lk(search_mutex_);
294 auto rp = shm_ptr_->reader_pos.load();
296 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
298 for (
int retry = 0; retry < 5; retry++)
303 ShmBuffer* buffer_ptr =
nullptr;
306 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
308 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
310 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
313 auto buf = getBufferInfo_(buffer);
319 sem = buf->sem.load();
320 sem_id = buf->sem_id.load();
322 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
323 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
324 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
326 if (buf->sequence_id < seqID)
329 seqID = buf->sequence_id;
332 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
340 if (buffer_ptr !=
nullptr)
342 sem = buffer_ptr->sem.load();
343 sem_id = buffer_ptr->sem_id.load();
346 if ((buffer_ptr ==
nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
353 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading Found buffer " << buffer_num;
354 touchBuffer_(buffer_ptr);
355 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
359 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
363 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
365 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
368 buffer_ptr->readPos = 0;
369 touchBuffer_(buffer_ptr);
370 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
372 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
375 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
377 shm_ptr_->lowest_seq_id_read = seqID;
379 last_seen_id_ = seqID;
380 if (shm_ptr_->destructive_read_mode)
382 shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
385 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading returning " << buffer_num;
391 TLOG(TLVL_GETBUFFER) <<
"GetBufferForReading returning -1 because no buffers are ready";
397 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
399 std::lock_guard<std::mutex> lk(search_mutex_);
401 auto wp = shm_ptr_->writer_pos.load();
403 TLOG(TLVL_GETBUFFER) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
406 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
408 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
412 auto buf = getBufferInfo_(buffer);
418 auto sem = buf->sem.load();
419 auto sem_id = buf->sem_id.load();
421 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
424 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
428 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
432 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
436 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
437 buf->sequence_id = ++shm_ptr_->next_sequence_id;
439 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
444 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting returning empty buffer " << buffer;
452 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
454 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
458 auto buf = getBufferInfo_(buffer);
464 auto sem = buf->sem.load();
465 auto sem_id = buf->sem_id.load();
467 if (sem == BufferSemaphoreFlags::Full)
470 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
474 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
478 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
482 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
483 buf->sequence_id = ++shm_ptr_->next_sequence_id;
485 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
490 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting returning full buffer (overwrite mode) " << buffer;
496 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
498 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
502 auto buf = getBufferInfo_(buffer);
508 auto sem = buf->sem.load();
509 auto sem_id = buf->sem_id.load();
511 if (sem == BufferSemaphoreFlags::Reading)
514 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
518 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
522 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
526 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
527 buf->sequence_id = ++shm_ptr_->next_sequence_id;
529 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
534 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting clobbering reader on buffer " << buffer <<
" (overwrite mode)";
539 TLOG(TLVL_GETBUFFER + 1) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
549 TLOG(TLVL_READREADY) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
550 std::unique_lock<std::mutex> lk(search_mutex_);
551 TLOG(TLVL_READREADY) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
554 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
557 TLOG(TLVL_READREADY + 1) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
560 auto buf = getBufferInfo_(ii);
567 TLOG(TLVL_READREADY + 2) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
": sem=" << FlagToString(buf->sem) <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
" )";
569 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_))
572 TLOG(TLVL_READREADY + 3) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
587 TLOG(TLVL_WRITEREADY) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
588 std::unique_lock<std::mutex> lk(search_mutex_);
590 TLOG(TLVL_WRITEREADY) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
592 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
596 TLOG(TLVL_WRITEREADY + 1) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
599 auto buf = getBufferInfo_(ii);
604 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
607 TLOG(TLVL_WRITEREADY + 1) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
621 TLOG(TLVL_READREADY) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
622 std::unique_lock<std::mutex> lk(search_mutex_);
625 auto rp = shm_ptr_->reader_pos.load();
627 TLOG(TLVL_READREADY) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
629 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
631 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
634 TLOG(TLVL_READREADY + 1) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
637 auto buf = getBufferInfo_(buffer);
644 TLOG(TLVL_READREADY + 2) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
": sem=" << FlagToString(buf->sem) <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
" )"
645 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
648 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_))
650 TLOG(TLVL_READREADY + 3) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
664 TLOG(TLVL_WRITEREADY) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
666 std::lock_guard<std::mutex> lk(search_mutex_);
669 auto wp = shm_ptr_->writer_pos.load();
671 TLOG(TLVL_WRITEREADY) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
673 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
675 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
676 TLOG(TLVL_WRITEREADY+ 1) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
678 auto buf = getBufferInfo_(buffer);
683 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
685 TLOG(TLVL_WRITEREADY+1) <<
"0x" << std::hex << shm_key_
687 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
696 std::deque<int> output;
697 size_t buffer_count = size();
698 if (!IsValid() || buffer_count == 0)
702 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
705 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
706 std::lock_guard<std::mutex> lk(search_mutex_);
707 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
709 for (
size_t ii = 0; ii < buffer_count; ++ii)
711 auto buf = getBufferInfo_(ii);
716 if (buf->sem_id == manager_id_)
718 output.push_back(ii);
724 for (
size_t ii = 0; ii < buffer_count; ++ii)
726 auto buf = getBufferInfo_(ii);
731 if (buf->sem_id == manager_id_)
733 output.push_back(ii);
738 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << buffer_count <<
" buffers.";
744 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
746 if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
748 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
751 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
752 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
753 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
756 auto buf = getBufferInfo_(buffer);
763 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
764 return buf->writePos;
769 TLOG(TLVL_POS) <<
"ResetReadPos(" << buffer <<
") called.";
771 if (buffer >= shm_ptr_->buffer_count)
773 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
776 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
777 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
778 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
781 auto buf = getBufferInfo_(buffer);
782 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
789 TLOG(TLVL_POS) <<
"ResetReadPos(" << buffer <<
") ended.";
794 TLOG(TLVL_POS + 1) <<
"ResetWritePos(" << buffer <<
") called.";
796 if (buffer >= shm_ptr_->buffer_count)
798 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
801 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
802 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
803 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
806 auto buf = getBufferInfo_(buffer);
811 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
815 TLOG(TLVL_POS+ 1) <<
"ResetWritePos(" << buffer <<
") ended.";
820 TLOG(TLVL_POS) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
822 if (buffer >= shm_ptr_->buffer_count)
824 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
827 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
828 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
829 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
831 auto buf = getBufferInfo_(buffer);
832 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
837 TLOG(TLVL_POS) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
838 buf->readPos = buf->readPos + read;
839 TLOG(TLVL_POS) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
842 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) +
")");
848 TLOG(TLVL_POS + 1) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
850 if (buffer >= shm_ptr_->buffer_count)
852 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
855 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
856 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
857 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
859 auto buf = getBufferInfo_(buffer);
864 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
866 if (buf->writePos + written > shm_ptr_->buffer_size)
868 TLOG(TLVL_ERROR) <<
"Requested write size is larger than the buffer size! (sz=" << std::hex << shm_ptr_->buffer_size <<
", cur + req=" << std::dec << buf->writePos + written <<
")";
871 TLOG(TLVL_POS+ 1) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
872 buf->writePos += written;
873 TLOG(TLVL_POS+1) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
876 Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
884 TLOG(TLVL_POS + 2) <<
"MoreDataInBuffer(" << buffer <<
") called.";
886 if (buffer >= shm_ptr_->buffer_count)
888 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
891 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
892 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
893 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
895 auto buf = getBufferInfo_(buffer);
900 TLOG(TLVL_POS + 2) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
901 return buf->readPos < buf->writePos;
906 if (buffer >= shm_ptr_->buffer_count)
908 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
911 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
912 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
913 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
915 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
920 if (buffer >= shm_ptr_->buffer_count)
922 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
925 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
926 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
927 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
930 auto shmBuf = getBufferInfo_(buffer);
931 if (shmBuf ==
nullptr)
935 touchBuffer_(shmBuf);
936 if (shmBuf->sem_id == manager_id_)
938 if (shmBuf->sem != BufferSemaphoreFlags::Full)
940 shmBuf->sem = BufferSemaphoreFlags::Full;
943 shmBuf->sem_id = destination;
949 TLOG(TLVL_POS +3 ) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
950 if (buffer >= shm_ptr_->buffer_count)
952 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
954 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
956 auto shmBuf = getBufferInfo_(buffer);
957 if (shmBuf ==
nullptr)
963 auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
966 touchBuffer_(shmBuf);
969 shmBuf->sem = BufferSemaphoreFlags::Full;
971 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
973 TLOG(TLVL_POS + 3) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
974 shmBuf->writePos = 0;
975 shmBuf->sem = BufferSemaphoreFlags::Empty;
976 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
978 TLOG(TLVL_POS+3) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
979 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
983 TLOG(TLVL_POS+3) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
989 if (buffer >= shm_ptr_->buffer_count)
991 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
996 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1000 auto shmBuf = getBufferInfo_(buffer);
1001 if (shmBuf ==
nullptr)
1016 if (delta > 0xFFFFFFFF)
1018 TLOG(TLVL_RESET) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
1022 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1026 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;
1028 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1033 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1035 TLOG(TLVL_RESET) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
1036 shmBuf->writePos = 0;
1037 shmBuf->sem = BufferSemaphoreFlags::Empty;
1038 shmBuf->sem_id = -1;
1039 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1041 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1046 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1050 if (delta <= shm_ptr_->buffer_timeout_us)
1054 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf)
1055 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
1056 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
1057 shmBuf->readPos = 0;
1058 shmBuf->sem = BufferSemaphoreFlags::Full;
1059 shmBuf->sem_id = -1;
1072 struct shmid_ds info;
1073 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1076 TLOG(TLVL_BUFINFO) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1080 if ((info.shm_perm.mode & SHM_DEST) != 0)
1082 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
1096 struct shmid_ds info;
1097 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1100 TLOG(TLVL_BUFINFO) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1104 return info.shm_nattch;
1109 TLOG(TLVL_WRITE) <<
"Write BEGIN";
1110 if (buffer >= shm_ptr_->buffer_count)
1112 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1114 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1116 auto shmBuf = getBufferInfo_(buffer);
1117 if (shmBuf ==
nullptr)
1121 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1122 touchBuffer_(shmBuf);
1123 TLOG(TLVL_WRITE) <<
"Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos <<
", write size is " << size;
1124 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1126 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1127 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
1128 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1131 auto pos = GetWritePos(buffer);
1132 memcpy(pos, data, size);
1133 touchBuffer_(shmBuf);
1134 shmBuf->writePos = shmBuf->writePos + size;
1136 auto last_seen = last_seen_id_.load();
1137 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1139 TLOG(TLVL_WRITE) <<
"Write END";
1145 if (buffer >= shm_ptr_->buffer_count)
1147 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1149 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1151 auto shmBuf = getBufferInfo_(buffer);
1152 if (shmBuf ==
nullptr)
1156 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1157 touchBuffer_(shmBuf);
1158 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1160 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1161 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
1162 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
1165 auto pos = GetReadPos(buffer);
1166 TLOG(TLVL_READ) <<
"Before memcpy in Read(), size is " << size;
1167 memcpy(data, pos, size);
1168 TLOG(TLVL_READ) <<
"After memcpy in Read()";
1169 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
1172 shmBuf->readPos += size;
1173 touchBuffer_(shmBuf);
1181 std::ostringstream ostr;
1182 ostr <<
"ShmStruct: " << std::endl
1183 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
1184 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
1185 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
1186 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
1187 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
1188 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1189 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
1190 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1193 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1195 auto buf = getBufferInfo_(ii);
1201 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1202 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1203 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1204 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1205 <<
"sem: " << FlagToString(buf->sem) << std::endl
1206 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1207 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1216 auto buf = getBufferInfo_(buffer);
1221 return bufferStart_(buffer) + buf->readPos;
1225 auto buf = getBufferInfo_(buffer);
1230 return bufferStart_(buffer) + buf->writePos;
1235 return bufferStart_(buffer);
1240 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1241 for (
size_t ii = 0; ii < size(); ++ii)
1243 auto buf = getBufferInfo_(ii);
1244 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1249 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1251 if (buffer ==
nullptr)
1255 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1259 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) <<
")";
1262 if (buffer->sem != flags)
1264 Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1266 if (buffer->sem_id != manager_id_)
1268 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) +
")");
1271 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1275 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1276 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1277 << R
"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1283 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1285 if ((buffer ==
nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1289 TLOG(TLVL_CHKBUFFER + 1) <<
"touchBuffer_: Touching buffer at " <<
static_cast<void*
>(buffer) <<
" with sequence_id " << buffer->sequence_id;
1295 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1298 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1299 auto bufs = GetBuffersOwnedByManager(
false);
1300 for (
auto buf : bufs)
1302 auto shmBuf = getBufferInfo_(buf);
1303 if (shmBuf ==
nullptr)
1307 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1309 shmBuf->sem = BufferSemaphoreFlags::Empty;
1311 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1313 shmBuf->sem = BufferSemaphoreFlags::Full;
1315 shmBuf->sem_id = -1;
1319 if (shm_ptr_ !=
nullptr)
1321 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1326 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1328 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1329 shmctl(shm_segment_id_, IPC_RMID,
nullptr);
1330 shm_segment_id_ = -1;
1336 if (!category.empty() && !message.empty())
1338 TLOG(TLVL_ERROR) << category <<
": " << message;
1342 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.