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"
16 #define TLVL_DETACH 11
17 #define TLVL_BUFFER 40
18 #define TLVL_BUFLCK 41
20 static std::list<artdaq::SharedMemoryManager const*> instances = std::list<artdaq::SharedMemoryManager const*>();
22 static std::unordered_map<int, struct sigaction> old_actions = std::unordered_map<int, struct sigaction>();
23 static bool sighandler_init =
false;
24 static std::mutex sighandler_mutex;
26 static void signal_handler(
int signum)
29 # if TRACE_REVNUM < 1459
30 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0)
32 TRACE_STREAMER(TLVL_ERROR, TLOG2(
"SharedMemoryManager",0), 0)
34 <<
"A signal of type " << signum <<
" was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!";
35 for (
auto ii : instances)
48 pthread_sigmask(SIG_UNBLOCK,
nullptr, &set);
49 pthread_sigmask(SIG_UNBLOCK, &set,
nullptr);
51 # if TRACE_REVNUM < 1459
52 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0)
54 TRACE_STREAMER(TLVL_ERROR, TLOG2(
"SharedMemoryManager",0), 0)
56 <<
"Calling default signal handler";
57 if (signum != SIGUSR2)
59 sigaction(signum, &old_actions[signum],
nullptr);
60 kill(getpid(), signum);
65 sigaction(SIGINT, &old_actions[SIGINT],
nullptr);
66 kill(getpid(), SIGINT);
77 requested_shm_parameters_.buffer_count = buffer_count;
78 requested_shm_parameters_.buffer_size = buffer_size;
79 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
80 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
82 instances.push_back(
this);
85 std::lock_guard<std::mutex> lk(sighandler_mutex);
89 sighandler_init =
true;
90 std::vector<int> signals = {SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP};
91 for (
auto signal : signals)
93 struct sigaction old_action;
94 sigaction(signal,
nullptr, &old_action);
98 if (old_action.sa_handler != SIG_IGN)
100 struct sigaction action;
101 action.sa_handler = signal_handler;
102 sigemptyset(&action.sa_mask);
103 for (
auto sigblk : signals)
105 sigaddset(&action.sa_mask, sigblk);
110 sigaction(signal, &action,
nullptr);
112 old_actions[signal] = old_action;
120 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager called";
122 static std::mutex destructor_mutex;
123 std::lock_guard<std::mutex> lk(destructor_mutex);
124 for (
auto it = instances.begin(); it != instances.end(); ++it)
128 it = instances.erase(it);
135 std::lock_guard<std::mutex> lk(sighandler_mutex);
138 if (sighandler_init && instances.empty())
140 sighandler_init =
false;
141 for (
auto signal : old_actions)
143 sigaction(signal.first, &signal.second,
nullptr);
148 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager done";
155 if (manager_id_ == 0)
162 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
163 auto start_time = std::chrono::steady_clock::now();
165 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
169 if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0)
174 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
175 if (shm_segment_id_ == -1)
177 if (manager_id_ == 0)
179 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
180 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
182 if (shm_segment_id_ == -1)
184 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
191 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
195 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
197 if (shm_segment_id_ > -1)
200 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
201 <<
" and size " << shmSize
203 shm_ptr_ =
static_cast<ShmStruct*
>(shmat(shm_segment_id_,
nullptr, 0));
205 <<
"Attached to shared memory segment at address "
206 << std::hex << static_cast<void*>(shm_ptr_) << std::dec;
207 if ((shm_ptr_ !=
nullptr) && shm_ptr_ !=
reinterpret_cast<void*
>(-1))
209 if (manager_id_ == 0)
211 if (shm_ptr_->ready_magic == 0xCAFE1111)
213 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
214 <<
"Once the system is shut down, you can use one of the following commands "
215 <<
"to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
216 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
219 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
220 shm_ptr_->next_id = 1;
221 shm_ptr_->next_sequence_id = 0;
222 shm_ptr_->reader_pos = 0;
223 shm_ptr_->writer_pos = 0;
224 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
225 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
226 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
227 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
229 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
230 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
232 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
233 if (getBufferInfo_(ii) ==
nullptr)
237 getBufferInfo_(ii)->writePos = 0;
238 getBufferInfo_(ii)->readPos = 0;
239 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
240 getBufferInfo_(ii)->sem_id = -1;
244 shm_ptr_->ready_magic = 0xCAFE1111;
248 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
249 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
250 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
252 shm_ptr_->lowest_seq_id_read = 0;
253 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
255 requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count;
256 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
257 for (
int ii = 0; ii < shm_ptr_->buffer_count; ++ii)
259 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
264 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
266 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
267 <<
"key: 0x" << std::hex << shm_key_
268 <<
", manager ID: " << std::dec << manager_id_
269 <<
", Buffer size: " << shm_ptr_->buffer_size
270 <<
", Buffer count: " << shm_ptr_->buffer_count;
274 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
279 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
280 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
282 <<
"if a stale shared memory segment needs to "
283 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
289 TLOG(13) <<
"GetBufferForReading BEGIN";
291 std::lock_guard<std::mutex> lk(search_mutex_);
293 auto rp = shm_ptr_->reader_pos.load();
295 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
297 for (
int retry = 0; retry < 5; retry++)
302 ShmBuffer* buffer_ptr =
nullptr;
305 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
307 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
309 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
312 auto buf = getBufferInfo_(buffer);
318 sem = buf->sem.load();
319 sem_id = buf->sem_id.load();
321 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
322 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
323 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
325 if (buf->sequence_id < seqID)
328 seqID = buf->sequence_id;
331 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
339 if (buffer_ptr !=
nullptr)
341 sem = buffer_ptr->sem.load();
342 sem_id = buffer_ptr->sem_id.load();
345 if ((buffer_ptr ==
nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
352 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
353 touchBuffer_(buffer_ptr);
354 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
358 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
362 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
364 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
367 buffer_ptr->readPos = 0;
368 touchBuffer_(buffer_ptr);
369 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
371 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
374 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
376 shm_ptr_->lowest_seq_id_read = seqID;
378 last_seen_id_ = seqID;
379 if (shm_ptr_->destructive_read_mode)
381 shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
384 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
390 TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
396 TLOG(14) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
398 std::lock_guard<std::mutex> lk(search_mutex_);
400 auto wp = shm_ptr_->writer_pos.load();
402 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
405 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
407 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
411 auto buf = getBufferInfo_(buffer);
417 auto sem = buf->sem.load();
418 auto sem_id = buf->sem_id.load();
420 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
423 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
427 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
431 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
435 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
436 buf->sequence_id = ++shm_ptr_->next_sequence_id;
438 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
443 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
451 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
453 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
457 auto buf = getBufferInfo_(buffer);
463 auto sem = buf->sem.load();
464 auto sem_id = buf->sem_id.load();
466 if (sem == BufferSemaphoreFlags::Full)
469 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
473 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
477 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
481 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
482 buf->sequence_id = ++shm_ptr_->next_sequence_id;
484 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
489 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
495 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
497 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
501 auto buf = getBufferInfo_(buffer);
507 auto sem = buf->sem.load();
508 auto sem_id = buf->sem_id.load();
510 if (sem == BufferSemaphoreFlags::Reading)
513 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
517 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
521 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
525 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
526 buf->sequence_id = ++shm_ptr_->next_sequence_id;
528 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
533 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
538 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
548 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
549 std::unique_lock<std::mutex> lk(search_mutex_);
550 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
553 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
556 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
559 auto buf = getBufferInfo_(ii);
566 TLOG(25) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
": sem=" << FlagToString(buf->sem) <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
" )";
568 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_))
571 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
586 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
587 std::unique_lock<std::mutex> lk(search_mutex_);
589 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
591 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
595 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
598 auto buf = getBufferInfo_(ii);
603 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
606 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
620 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
621 std::unique_lock<std::mutex> lk(search_mutex_);
624 auto rp = shm_ptr_->reader_pos.load();
626 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
628 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
630 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
633 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
636 auto buf = getBufferInfo_(buffer);
643 TLOG(25) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
": sem=" << FlagToString(buf->sem) <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
" )"
644 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
647 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_))
649 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
663 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
665 std::lock_guard<std::mutex> lk(search_mutex_);
668 auto wp = shm_ptr_->writer_pos.load();
670 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
672 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
674 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
675 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
677 auto buf = getBufferInfo_(buffer);
682 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
684 TLOG(29) <<
"0x" << std::hex << shm_key_
686 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
695 std::deque<int> output;
696 size_t buffer_count = size();
697 if (!IsValid() || buffer_count == 0)
701 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
704 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
705 std::lock_guard<std::mutex> lk(search_mutex_);
706 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
708 for (
size_t ii = 0; ii < buffer_count; ++ii)
710 auto buf = getBufferInfo_(ii);
715 if (buf->sem_id == manager_id_)
717 output.push_back(ii);
723 for (
size_t ii = 0; ii < buffer_count; ++ii)
725 auto buf = getBufferInfo_(ii);
730 if (buf->sem_id == manager_id_)
732 output.push_back(ii);
737 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << buffer_count <<
" buffers.";
743 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
745 if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
747 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
750 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
751 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
752 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
755 auto buf = getBufferInfo_(buffer);
762 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
763 return buf->writePos;
768 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
770 if (buffer >= shm_ptr_->buffer_count)
772 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
775 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
776 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
777 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
780 auto buf = getBufferInfo_(buffer);
781 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
788 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
793 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
795 if (buffer >= shm_ptr_->buffer_count)
797 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
800 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
801 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
802 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
805 auto buf = getBufferInfo_(buffer);
810 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
814 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
819 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
821 if (buffer >= shm_ptr_->buffer_count)
823 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
826 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
827 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
828 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
830 auto buf = getBufferInfo_(buffer);
831 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
836 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
837 buf->readPos = buf->readPos + read;
838 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
841 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) +
")");
847 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
849 if (buffer >= shm_ptr_->buffer_count)
851 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
854 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
855 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
856 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
858 auto buf = getBufferInfo_(buffer);
863 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
865 if (buf->writePos + written > shm_ptr_->buffer_size)
867 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 <<
")";
870 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
871 buf->writePos += written;
872 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
875 Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
883 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
885 if (buffer >= shm_ptr_->buffer_count)
887 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
890 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
891 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
892 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
894 auto buf = getBufferInfo_(buffer);
899 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
900 return buf->readPos < buf->writePos;
905 if (buffer >= shm_ptr_->buffer_count)
907 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
910 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
911 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
912 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
914 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
919 if (buffer >= shm_ptr_->buffer_count)
921 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
924 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
925 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
926 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
929 auto shmBuf = getBufferInfo_(buffer);
930 if (shmBuf ==
nullptr)
934 touchBuffer_(shmBuf);
935 if (shmBuf->sem_id == manager_id_)
937 if (shmBuf->sem != BufferSemaphoreFlags::Full)
939 shmBuf->sem = BufferSemaphoreFlags::Full;
942 shmBuf->sem_id = destination;
948 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
949 if (buffer >= shm_ptr_->buffer_count)
951 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
953 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
955 auto shmBuf = getBufferInfo_(buffer);
956 if (shmBuf ==
nullptr)
962 auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
965 touchBuffer_(shmBuf);
968 shmBuf->sem = BufferSemaphoreFlags::Full;
970 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
972 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
973 shmBuf->writePos = 0;
974 shmBuf->sem = BufferSemaphoreFlags::Empty;
975 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
977 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
978 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
982 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
988 if (buffer >= shm_ptr_->buffer_count)
990 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
995 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
999 auto shmBuf = getBufferInfo_(buffer);
1000 if (shmBuf ==
nullptr)
1015 if (delta > 0xFFFFFFFF)
1017 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
1021 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1025 TLOG(27) <<
"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;
1027 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1032 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1034 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
1035 shmBuf->writePos = 0;
1036 shmBuf->sem = BufferSemaphoreFlags::Empty;
1037 shmBuf->sem_id = -1;
1038 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1040 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1045 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1049 if (delta <= shm_ptr_->buffer_timeout_us)
1053 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf)
1054 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
1055 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
1056 shmBuf->readPos = 0;
1057 shmBuf->sem = BufferSemaphoreFlags::Full;
1058 shmBuf->sem_id = -1;
1071 struct shmid_ds info;
1072 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1075 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1079 if ((info.shm_perm.mode & SHM_DEST) != 0)
1081 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
1095 struct shmid_ds info;
1096 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1099 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1103 return info.shm_nattch;
1108 TLOG(19) <<
"Write BEGIN";
1109 if (buffer >= shm_ptr_->buffer_count)
1111 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1113 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1115 auto shmBuf = getBufferInfo_(buffer);
1116 if (shmBuf ==
nullptr)
1120 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1121 touchBuffer_(shmBuf);
1122 TLOG(19) <<
"Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos <<
", write size is " << size;
1123 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1125 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1126 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
1127 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1130 auto pos = GetWritePos(buffer);
1131 memcpy(pos, data, size);
1132 touchBuffer_(shmBuf);
1133 shmBuf->writePos = shmBuf->writePos + size;
1135 auto last_seen = last_seen_id_.load();
1136 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1138 TLOG(19) <<
"Write END";
1144 if (buffer >= shm_ptr_->buffer_count)
1146 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1148 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1150 auto shmBuf = getBufferInfo_(buffer);
1151 if (shmBuf ==
nullptr)
1155 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1156 touchBuffer_(shmBuf);
1157 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1159 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1160 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
1161 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
1164 auto pos = GetReadPos(buffer);
1165 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
1166 memcpy(data, pos, size);
1167 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
1168 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
1171 shmBuf->readPos += size;
1172 touchBuffer_(shmBuf);
1180 std::ostringstream ostr;
1181 ostr <<
"ShmStruct: " << std::endl
1182 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
1183 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
1184 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
1185 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
1186 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
1187 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1188 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
1189 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1192 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1194 auto buf = getBufferInfo_(ii);
1200 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1201 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1202 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1203 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1204 <<
"sem: " << FlagToString(buf->sem) << std::endl
1205 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1206 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1215 auto buf = getBufferInfo_(buffer);
1220 return bufferStart_(buffer) + buf->readPos;
1224 auto buf = getBufferInfo_(buffer);
1229 return bufferStart_(buffer) + buf->writePos;
1234 return bufferStart_(buffer);
1239 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1240 for (
size_t ii = 0; ii < size(); ++ii)
1242 auto buf = getBufferInfo_(ii);
1243 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1248 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1250 if (buffer ==
nullptr)
1254 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1258 TLOG(TLVL_TRACE) <<
"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) <<
")";
1261 if (buffer->sem != flags)
1263 Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1265 if (buffer->sem_id != manager_id_)
1267 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) +
")");
1270 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1274 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1275 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1276 << R
"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1282 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1284 if ((buffer ==
nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1288 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " <<
static_cast<void*
>(buffer) <<
" with sequence_id " << buffer->sequence_id;
1294 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1297 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1298 auto bufs = GetBuffersOwnedByManager(
false);
1299 for (
auto buf : bufs)
1301 auto shmBuf = getBufferInfo_(buf);
1302 if (shmBuf ==
nullptr)
1306 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1308 shmBuf->sem = BufferSemaphoreFlags::Empty;
1310 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1312 shmBuf->sem = BufferSemaphoreFlags::Full;
1314 shmBuf->sem_id = -1;
1318 if (shm_ptr_ !=
nullptr)
1320 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1325 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1327 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1328 shmctl(shm_segment_id_, IPC_RMID,
nullptr);
1329 shm_segment_id_ = -1;
1335 if (!category.empty() && !message.empty())
1337 TLOG(TLVL_ERROR) << category <<
": " << message;
1341 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.