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 <<
"Restoring default signal handler";
57 sigaction(signum, &old_actions[signum],
nullptr);
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;
686 size_t buffer_count = size();
687 if (!IsValid() || buffer_count == 0)
691 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
694 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
695 std::lock_guard<std::mutex> lk(search_mutex_);
696 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
698 for (
size_t ii = 0; ii < buffer_count; ++ii)
700 auto buf = getBufferInfo_(ii);
705 if (buf->sem_id == manager_id_)
707 output.push_back(ii);
713 for (
size_t ii = 0; ii < buffer_count; ++ii)
715 auto buf = getBufferInfo_(ii);
720 if (buf->sem_id == manager_id_)
722 output.push_back(ii);
727 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << buffer_count <<
" buffers.";
733 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
735 if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
737 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
740 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
741 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
742 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
745 auto buf = getBufferInfo_(buffer);
752 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
753 return buf->writePos;
758 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
760 if (buffer >= shm_ptr_->buffer_count)
762 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
765 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
766 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
767 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
770 auto buf = getBufferInfo_(buffer);
771 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
778 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
783 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
785 if (buffer >= shm_ptr_->buffer_count)
787 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
790 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
791 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
792 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
795 auto buf = getBufferInfo_(buffer);
800 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
804 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
809 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
811 if (buffer >= shm_ptr_->buffer_count)
813 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
816 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
817 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
818 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
820 auto buf = getBufferInfo_(buffer);
821 if ((buf ==
nullptr) || buf->sem_id != manager_id_)
826 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
827 buf->readPos = buf->readPos + read;
828 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
831 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) +
")");
837 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
839 if (buffer >= shm_ptr_->buffer_count)
841 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
844 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
845 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
846 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
848 auto buf = getBufferInfo_(buffer);
853 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
855 if (buf->writePos + written > shm_ptr_->buffer_size)
857 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 <<
")";
860 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
861 buf->writePos += written;
862 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
865 Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
873 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
875 if (buffer >= shm_ptr_->buffer_count)
877 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
880 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
881 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
882 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
884 auto buf = getBufferInfo_(buffer);
889 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
890 return buf->readPos < buf->writePos;
895 if (buffer >= shm_ptr_->buffer_count)
897 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
900 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
901 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
902 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
904 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
909 if (buffer >= shm_ptr_->buffer_count)
911 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
914 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
915 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
916 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
919 auto shmBuf = getBufferInfo_(buffer);
920 if (shmBuf ==
nullptr)
924 touchBuffer_(shmBuf);
925 if (shmBuf->sem_id == manager_id_)
927 if (shmBuf->sem != BufferSemaphoreFlags::Full)
929 shmBuf->sem = BufferSemaphoreFlags::Full;
932 shmBuf->sem_id = destination;
938 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
939 if (buffer >= shm_ptr_->buffer_count)
941 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
943 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
945 auto shmBuf = getBufferInfo_(buffer);
946 if (shmBuf ==
nullptr)
952 auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
955 touchBuffer_(shmBuf);
958 shmBuf->sem = BufferSemaphoreFlags::Full;
960 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
962 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
963 shmBuf->writePos = 0;
964 shmBuf->sem = BufferSemaphoreFlags::Empty;
965 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
967 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
968 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
972 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
978 if (buffer >= shm_ptr_->buffer_count)
980 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
985 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
989 auto shmBuf = getBufferInfo_(buffer);
990 if (shmBuf ==
nullptr)
1005 if (delta > 0xFFFFFFFF)
1007 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
1011 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1015 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;
1017 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1022 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1024 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
1025 shmBuf->writePos = 0;
1026 shmBuf->sem = BufferSemaphoreFlags::Empty;
1027 shmBuf->sem_id = -1;
1028 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1030 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1035 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1039 if (delta <= shm_ptr_->buffer_timeout_us)
1043 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " <<
static_cast<void*
>(shmBuf)
1044 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
1045 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
1046 shmBuf->readPos = 0;
1047 shmBuf->sem = BufferSemaphoreFlags::Full;
1048 shmBuf->sem_id = -1;
1061 struct shmid_ds info;
1062 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1065 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1069 if ((info.shm_perm.mode & SHM_DEST) != 0)
1071 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
1085 struct shmid_ds info;
1086 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1089 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
1093 return info.shm_nattch;
1098 TLOG(19) <<
"Write BEGIN";
1099 if (buffer >= shm_ptr_->buffer_count)
1101 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1103 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1105 auto shmBuf = getBufferInfo_(buffer);
1106 if (shmBuf ==
nullptr)
1110 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1111 touchBuffer_(shmBuf);
1112 TLOG(19) <<
"Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos <<
", write size is " << size;
1113 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1115 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1116 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
1117 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1120 auto pos = GetWritePos(buffer);
1121 memcpy(pos, data, size);
1122 touchBuffer_(shmBuf);
1123 shmBuf->writePos = shmBuf->writePos + size;
1125 auto last_seen = last_seen_id_.load();
1126 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1128 TLOG(19) <<
"Write END";
1134 if (buffer >= shm_ptr_->buffer_count)
1136 Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1138 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1140 auto shmBuf = getBufferInfo_(buffer);
1141 if (shmBuf ==
nullptr)
1145 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1146 touchBuffer_(shmBuf);
1147 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1149 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1150 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
1151 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
1154 auto pos = GetReadPos(buffer);
1155 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
1156 memcpy(data, pos, size);
1157 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
1158 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
1161 shmBuf->readPos += size;
1162 touchBuffer_(shmBuf);
1170 std::ostringstream ostr;
1171 ostr <<
"ShmStruct: " << std::endl
1172 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
1173 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
1174 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
1175 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
1176 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
1177 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1178 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
1179 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1182 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1184 auto buf = getBufferInfo_(ii);
1190 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1191 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1192 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1193 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1194 <<
"sem: " << FlagToString(buf->sem) << std::endl
1195 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1196 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1205 auto buf = getBufferInfo_(buffer);
1210 return bufferStart_(buffer) + buf->readPos;
1214 auto buf = getBufferInfo_(buffer);
1219 return bufferStart_(buffer) + buf->writePos;
1224 return bufferStart_(buffer);
1229 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1230 for (
size_t ii = 0; ii < size(); ++ii)
1232 auto buf = getBufferInfo_(ii);
1233 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1238 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1240 if (buffer ==
nullptr)
1244 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1248 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) <<
")";
1251 if (buffer->sem != flags)
1253 Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1255 if (buffer->sem_id != manager_id_)
1257 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) +
")");
1260 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1264 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1265 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1266 << R
"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1272 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1274 if ((buffer ==
nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1278 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " <<
static_cast<void*
>(buffer) <<
" with sequence_id " << buffer->sequence_id;
1284 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1287 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1288 auto bufs = GetBuffersOwnedByManager(
false);
1289 for (
auto buf : bufs)
1291 auto shmBuf = getBufferInfo_(buf);
1292 if (shmBuf ==
nullptr)
1296 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1298 shmBuf->sem = BufferSemaphoreFlags::Empty;
1300 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1302 shmBuf->sem = BufferSemaphoreFlags::Full;
1304 shmBuf->sem_id = -1;
1308 if (shm_ptr_ !=
nullptr)
1310 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1315 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1317 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1318 shmctl(shm_segment_id_, IPC_RMID,
nullptr);
1319 shm_segment_id_ = -1;
1325 if (!category.empty() && !message.empty())
1327 TLOG(TLVL_ERROR) << category <<
": " << message;
1331 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.