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 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0) <<
"A signal of type " << signum <<
" was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!";
30 for (
auto ii : instances)
43 pthread_sigmask(SIG_UNBLOCK,
nullptr, &set);
44 pthread_sigmask(SIG_UNBLOCK, &set,
nullptr);
46 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0) <<
"Calling default signal handler";
47 if (signum != SIGUSR2)
49 sigaction(signum, &old_actions[signum],
nullptr);
50 kill(getpid(), signum);
55 sigaction(SIGINT, &old_actions[SIGINT],
nullptr);
56 kill(getpid(), SIGINT);
67 requested_shm_parameters_.buffer_count = buffer_count;
68 requested_shm_parameters_.buffer_size = buffer_size;
69 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
70 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
72 instances.push_back(
this);
75 std::lock_guard<std::mutex> lk(sighandler_mutex);
79 sighandler_init =
true;
80 std::vector<int> signals = {SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP};
81 for (
auto signal : signals)
83 struct sigaction old_action;
84 sigaction(signal,
nullptr, &old_action);
88 if (old_action.sa_handler != SIG_IGN)
90 struct sigaction action;
91 action.sa_handler = signal_handler;
92 sigemptyset(&action.sa_mask);
93 for (
auto sigblk : signals)
95 sigaddset(&action.sa_mask, sigblk);
100 sigaction(signal, &action,
nullptr);
102 old_actions[signal] = old_action;
110 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager called";
112 static std::mutex destructor_mutex;
113 std::lock_guard<std::mutex> lk(destructor_mutex);
114 for (
auto it = instances.begin(); it != instances.end(); ++it)
118 it = instances.erase(it);
125 std::lock_guard<std::mutex> lk(sighandler_mutex);
128 if (sighandler_init && instances.empty())
130 sighandler_init =
false;
131 for (
auto signal : old_actions)
133 sigaction(signal.first, &signal.second,
nullptr);
138 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager done";
145 if (manager_id_ == 0)
152 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
153 auto start_time = std::chrono::steady_clock::now();
155 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
159 if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0)
164 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
165 if (shm_segment_id_ == -1)
167 if (manager_id_ == 0)
169 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
170 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
172 if (shm_segment_id_ == -1)
174 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
181 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
185 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
187 if (shm_segment_id_ > -1)
190 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
191 <<
" and size " << shmSize
193 shm_ptr_ =
static_cast<ShmStruct*
>(shmat(shm_segment_id_,
nullptr, 0));
195 <<
"Attached to shared memory segment at address "
196 << std::hex << static_cast<void*>(shm_ptr_) << std::dec;
197 if ((shm_ptr_ !=
nullptr) && shm_ptr_ !=
reinterpret_cast<void*
>(-1))
199 if (manager_id_ == 0)
201 if (shm_ptr_->ready_magic == 0xCAFE1111)
203 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
204 <<
"Once the system is shut down, you can use one of the following commands "
205 <<
"to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
206 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
209 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
210 shm_ptr_->next_id = 1;
211 shm_ptr_->next_sequence_id = 0;
212 shm_ptr_->reader_pos = 0;
213 shm_ptr_->writer_pos = 0;
214 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
215 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
216 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
217 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
219 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
220 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
222 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
223 if (getBufferInfo_(ii) ==
nullptr)
227 getBufferInfo_(ii)->writePos = 0;
228 getBufferInfo_(ii)->readPos = 0;
229 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
230 getBufferInfo_(ii)->sem_id = -1;
234 shm_ptr_->ready_magic = 0xCAFE1111;
238 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
239 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
240 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
242 shm_ptr_->lowest_seq_id_read = 0;
243 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
245 requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count;
246 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
247 for (
int ii = 0; ii < shm_ptr_->buffer_count; ++ii)
249 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
254 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
256 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
257 <<
"key: 0x" << std::hex << shm_key_
258 <<
", manager ID: " << std::dec << manager_id_
259 <<
", Buffer size: " << shm_ptr_->buffer_size
260 <<
", Buffer count: " << shm_ptr_->buffer_count;
264 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
269 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
270 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
272 <<
"if a stale shared memory segment needs to "
273 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
279 TLOG(13) <<
"GetBufferForReading BEGIN";
281 std::lock_guard<std::mutex> lk(search_mutex_);
283 auto rp = shm_ptr_->reader_pos.load();
285 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
287 for (
int retry = 0; retry < 5; retry++)
292 ShmBuffer* buffer_ptr =
nullptr;
295 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
297 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
299 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
302 auto buf = getBufferInfo_(buffer);
308 sem = buf->sem.load();
309 sem_id = buf->sem_id.load();
311 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
312 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
313 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
315 if (buf->sequence_id < seqID)
318 seqID = buf->sequence_id;
321 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
329 if (buffer_ptr !=
nullptr)
331 sem = buffer_ptr->sem.load();
332 sem_id = buffer_ptr->sem_id.load();
335 if ((buffer_ptr ==
nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
342 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
343 touchBuffer_(buffer_ptr);
344 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
348 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
352 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
354 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
357 buffer_ptr->readPos = 0;
358 touchBuffer_(buffer_ptr);
359 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
361 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
364 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
366 shm_ptr_->lowest_seq_id_read = seqID;
368 last_seen_id_ = seqID;
369 if (shm_ptr_->destructive_read_mode)
371 shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
374 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
380 TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
386 TLOG(14) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
388 std::lock_guard<std::mutex> lk(search_mutex_);
390 auto wp = shm_ptr_->writer_pos.load();
392 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
395 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
397 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
401 auto buf = getBufferInfo_(buffer);
407 auto sem = buf->sem.load();
408 auto sem_id = buf->sem_id.load();
410 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
413 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
417 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
421 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
425 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
426 buf->sequence_id = ++shm_ptr_->next_sequence_id;
428 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
433 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
441 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
443 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
447 auto buf = getBufferInfo_(buffer);
453 auto sem = buf->sem.load();
454 auto sem_id = buf->sem_id.load();
456 if (sem == BufferSemaphoreFlags::Full)
459 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
463 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
467 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
471 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
472 buf->sequence_id = ++shm_ptr_->next_sequence_id;
474 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
479 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
485 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
487 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
491 auto buf = getBufferInfo_(buffer);
497 auto sem = buf->sem.load();
498 auto sem_id = buf->sem_id.load();
500 if (sem == BufferSemaphoreFlags::Reading)
503 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
507 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
511 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
515 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
516 buf->sequence_id = ++shm_ptr_->next_sequence_id;
518 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
523 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
528 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
538 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
539 std::unique_lock<std::mutex> lk(search_mutex_);
540 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
543 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
546 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
549 auto buf = getBufferInfo_(ii);
556 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 <<
" )";
558 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_))
561 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
576 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
577 std::unique_lock<std::mutex> lk(search_mutex_);
579 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
581 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
585 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
588 auto buf = getBufferInfo_(ii);
593 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
596 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
610 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
611 std::unique_lock<std::mutex> lk(search_mutex_);
614 auto rp = shm_ptr_->reader_pos.load();
616 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
618 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
620 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
623 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
626 auto buf = getBufferInfo_(buffer);
633 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 <<
" )"
634 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
637 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_))
639 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
653 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
655 std::lock_guard<std::mutex> lk(search_mutex_);
658 auto wp = shm_ptr_->writer_pos.load();
660 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
662 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
664 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
665 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
667 auto buf = getBufferInfo_(buffer);
672 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
674 TLOG(29) <<
"0x" << std::hex << shm_key_
676 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
685 std::deque<int> output;
690 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
693 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
694 std::lock_guard<std::mutex> lk(search_mutex_);
695 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
697 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
699 auto buf = getBufferInfo_(ii);
704 if (buf->sem_id == manager_id_)
706 output.push_back(ii);
712 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
714 auto buf = getBufferInfo_(ii);
719 if (buf->sem_id == manager_id_)
721 output.push_back(ii);
726 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << shm_ptr_->buffer_count <<
" buffers.";
732 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
734 if (buffer >= shm_ptr_->buffer_count)
736 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
739 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
740 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
741 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
744 auto buf = getBufferInfo_(buffer);
751 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
752 return buf->writePos;
757 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
759 if (buffer >= shm_ptr_->buffer_count)
761 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
764 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
765 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
766 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
769 auto buf = getBufferInfo_(buffer);
770 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
777 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
782 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
784 if (buffer >= shm_ptr_->buffer_count)
786 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
789 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
790 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
791 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
794 auto buf = getBufferInfo_(buffer);
799 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
803 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
808 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
810 if (buffer >= shm_ptr_->buffer_count)
812 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
815 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
816 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
817 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
819 auto buf = getBufferInfo_(buffer);
820 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
825 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
826 buf->readPos = buf->readPos + read;
827 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
830 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) +
")");
836 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
838 if (buffer >= shm_ptr_->buffer_count)
840 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
843 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
844 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
845 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
847 auto buf = getBufferInfo_(buffer);
852 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
854 if (buf->writePos + written > shm_ptr_->buffer_size)
856 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 <<
")";
859 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
860 buf->writePos += written;
861 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
864 Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
872 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
874 if (buffer >= shm_ptr_->buffer_count)
876 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
879 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
880 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
881 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
883 auto buf = getBufferInfo_(buffer);
888 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
889 return buf->readPos < buf->writePos;
894 if (buffer >= shm_ptr_->buffer_count)
896 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
899 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
900 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
901 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
903 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
908 if (buffer >= shm_ptr_->buffer_count)
910 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
913 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
914 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
915 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
918 auto shmBuf = getBufferInfo_(buffer);
919 if (shmBuf ==
nullptr)
923 touchBuffer_(shmBuf);
924 if (shmBuf->sem_id == manager_id_)
926 if (shmBuf->sem != BufferSemaphoreFlags::Full)
928 shmBuf->sem = BufferSemaphoreFlags::Full;
931 shmBuf->sem_id = destination;
937 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
938 if (buffer >= shm_ptr_->buffer_count)
940 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
942 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
944 auto shmBuf = getBufferInfo_(buffer);
945 if (shmBuf ==
nullptr)
951 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
true);
953 touchBuffer_(shmBuf);
956 shmBuf->sem = BufferSemaphoreFlags::Full;
958 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
960 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
961 shmBuf->writePos = 0;
962 shmBuf->sem = BufferSemaphoreFlags::Empty;
963 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
965 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
966 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
970 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
976 if (buffer >= shm_ptr_->buffer_count)
978 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
983 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
987 auto shmBuf = getBufferInfo_(buffer);
988 if (shmBuf ==
nullptr)
1003 if (delta > 0xFFFFFFFF)
1005 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
1009 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1013 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;
1015 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1020 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1022 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
1023 shmBuf->writePos = 0;
1024 shmBuf->sem = BufferSemaphoreFlags::Empty;
1025 shmBuf->sem_id = -1;
1026 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1028 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1033 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1037 if (delta <= shm_ptr_->buffer_timeout_us)
1041 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf)
1042 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
1043 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
1044 shmBuf->readPos = 0;
1045 shmBuf->sem = BufferSemaphoreFlags::Full;
1046 shmBuf->sem_id = -1;
1059 struct shmid_ds info;
1060 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1063 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1067 if ((info.shm_perm.mode & SHM_DEST) != 0)
1069 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
1083 struct shmid_ds info;
1084 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1087 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1091 return info.shm_nattch;
1096 TLOG(19) <<
"Write BEGIN";
1097 if (buffer >= shm_ptr_->buffer_count)
1099 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1101 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1103 auto shmBuf = getBufferInfo_(buffer);
1104 if (shmBuf ==
nullptr)
1108 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1109 touchBuffer_(shmBuf);
1110 TLOG(19) <<
"Buffer Write Pos is " << shmBuf->writePos <<
", write size is " << size;
1111 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1113 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1114 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
1115 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1118 auto pos = GetWritePos(buffer);
1119 memcpy(pos, data, size);
1120 touchBuffer_(shmBuf);
1121 shmBuf->writePos = shmBuf->writePos + size;
1123 auto last_seen = last_seen_id_.load();
1124 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1126 TLOG(19) <<
"Write END";
1132 if (buffer >= shm_ptr_->buffer_count)
1134 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1136 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1138 auto shmBuf = getBufferInfo_(buffer);
1139 if (shmBuf ==
nullptr)
1143 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1144 touchBuffer_(shmBuf);
1145 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1147 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1148 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
1149 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
1152 auto pos = GetReadPos(buffer);
1153 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
1154 memcpy(data, pos, size);
1155 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
1156 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
1159 shmBuf->readPos += size;
1160 touchBuffer_(shmBuf);
1168 std::ostringstream ostr;
1169 ostr <<
"ShmStruct: " << std::endl
1170 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
1171 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
1172 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
1173 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
1174 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
1175 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1176 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
1177 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1180 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1182 auto buf = getBufferInfo_(ii);
1188 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1189 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1190 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1191 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1192 <<
"sem: " << FlagToString(buf->sem) << std::endl
1193 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1194 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1203 auto buf = getBufferInfo_(buffer);
1208 return bufferStart_(buffer) + buf->readPos;
1212 auto buf = getBufferInfo_(buffer);
1217 return bufferStart_(buffer) + buf->writePos;
1222 return bufferStart_(buffer);
1227 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1228 for (
size_t ii = 0; ii < size(); ++ii)
1230 auto buf = getBufferInfo_(ii);
1231 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1236 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1238 if (buffer ==
nullptr)
1242 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1246 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) <<
")";
1249 if (buffer->sem != flags)
1251 Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1253 if (buffer->sem_id != manager_id_)
1255 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) +
")");
1258 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1262 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1263 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1264 << R
"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1270 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1272 if ((buffer ==
nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1276 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " <<
static_cast<void*
>(buffer) <<
" with sequence_id " << buffer->sequence_id;
1282 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1285 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1286 auto bufs = GetBuffersOwnedByManager(
false);
1287 for (
auto buf : bufs)
1289 auto shmBuf = getBufferInfo_(buf);
1290 if (shmBuf ==
nullptr)
1294 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1296 shmBuf->sem = BufferSemaphoreFlags::Empty;
1298 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1300 shmBuf->sem = BufferSemaphoreFlags::Full;
1302 shmBuf->sem_id = -1;
1306 if (shm_ptr_ !=
nullptr)
1308 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1313 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1315 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1316 shmctl(shm_segment_id_, IPC_RMID,
nullptr);
1317 shm_segment_id_ = -1;
1323 if (!category.empty() && !message.empty())
1325 TLOG(TLVL_ERROR) << category <<
": " << message;
1329 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.
constexpr 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)
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.