1 #define TRACE_NAME "SharedMemoryManager"
3 #include <unordered_map>
7 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!?
12 #include "cetlib_except/exception.h"
13 #include "artdaq-core/Core/SharedMemoryManager.hh"
14 #include "artdaq-core/Utilities/TraceLock.hh"
16 #define TLVL_DETACH 11
17 #define TLVL_BUFFER 40
18 #define TLVL_BUFLCK 41
20 static std::set<artdaq::SharedMemoryManager const*> instances = std::set<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 void signal_handler(
int signum)
27 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!";
28 for (
auto ii : instances)
41 pthread_sigmask(SIG_UNBLOCK, NULL, &set);
42 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
44 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0) <<
"Calling default signal handler";
45 if (signum != SIGUSR2)
47 sigaction(signum, &old_actions[signum], NULL);
48 kill(getpid(), signum);
53 sigaction(SIGINT, &old_actions[SIGINT], NULL);
54 kill(getpid(), SIGINT);
66 requested_shm_parameters_.buffer_count = buffer_count;
67 requested_shm_parameters_.buffer_size = buffer_size;
68 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
69 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
71 instances.insert(
this);
74 static std::mutex sighandler_mutex;
75 std::unique_lock<std::mutex> lk(sighandler_mutex);
79 sighandler_init =
true;
80 std::vector<int> signals = { SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2 };
81 for (
auto signal : signals)
83 struct sigaction old_action;
84 sigaction(signal, NULL, &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, NULL);
101 old_actions[signal] = old_action;
109 instances.erase(
this);
110 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager called";
112 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager done";
119 if (manager_id_ == 0)
return;
122 auto start_time = std::chrono::steady_clock::now();
124 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
126 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
127 if (shm_segment_id_ == -1 && requested_shm_parameters_.buffer_count > 0 && manager_id_ <= 0)
129 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
130 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
133 if (shm_segment_id_ == -1)
135 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << errno <<
" (" << strerror(errno) <<
")";
142 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
146 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << shm_segment_id_;
148 if (shm_segment_id_ > -1)
151 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
152 <<
" and size " << shmSize
154 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
156 <<
"Attached to shared memory segment at address "
157 << std::hex << (
void*)shm_ptr_ << std::dec;
158 if (shm_ptr_ && shm_ptr_ != (
void *)-1)
160 if (manager_id_ == 0)
162 if (shm_ptr_->ready_magic == 0xCAFE1111)
164 TLOG(TLVL_ERROR) <<
"Owner encountered already-initialized Shared Memory!";
167 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
168 shm_ptr_->next_id = 1;
169 shm_ptr_->next_sequence_id = 0;
170 shm_ptr_->reader_pos = 0;
171 shm_ptr_->writer_pos = 0;
172 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
173 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
174 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
175 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
177 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
179 if (!getBufferInfo_(ii))
return;
180 getBufferInfo_(ii)->writePos = 0;
181 getBufferInfo_(ii)->readPos = 0;
182 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
183 getBufferInfo_(ii)->sem_id = -1;
187 shm_ptr_->ready_magic = 0xCAFE1111;
191 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
192 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
193 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
195 shm_ptr_->lowest_seq_id_read = 0;
196 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
199 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
200 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
201 <<
"key: 0x" << std::hex << shm_key_
202 <<
", manager ID: " << std::dec << manager_id_
203 <<
", Buffer size: " << shm_ptr_->buffer_size
204 <<
", Buffer count: " << shm_ptr_->buffer_count;
209 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
215 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
216 <<
", errno = " << strerror(errno) <<
". Please check "
217 <<
"if a stale shared memory segment needs to "
218 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
225 TLOG(13) <<
"GetBufferForReading BEGIN";
227 std::unique_lock<std::mutex> lk(search_mutex_);
229 auto rp = shm_ptr_->reader_pos.load();
231 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
236 ShmBuffer* buffer_ptr =
nullptr;
238 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
240 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
243 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode="<<shm_ptr_->destructive_read_mode;
246 auto buf = getBufferInfo_(buffer);
248 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(buf->sem)
249 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << buf->sem_id <<
", seq_id=" << buf->sequence_id<<
" )";
250 if ( buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_)
251 && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_) )
253 if (buf->sequence_id < seqID)
256 seqID = buf->sequence_id;
259 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
break;
264 if (!buffer_ptr || (buffer_ptr && buffer_ptr->sem_id != -1 && buffer_ptr->sem_id != manager_id_))
271 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
272 buffer_ptr->sem_id = manager_id_;
273 buffer_ptr->sem = BufferSemaphoreFlags::Reading;
274 if (buffer_ptr->sem_id != manager_id_) {
275 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
278 buffer_ptr->readPos = 0;
279 touchBuffer_(buffer_ptr);
280 if (buffer_ptr->sem_id != manager_id_) {
281 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
284 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
286 shm_ptr_->lowest_seq_id_read = seqID;
288 last_seen_id_ = seqID;
289 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
291 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
297 if(buffer_num==-1) TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
298 else TLOG(13) <<
"GetBufferForReading returning with buffer " << buffer_num;
304 TLOG(14) <<
"GetBufferForWriting BEGIN";
305 std::unique_lock<std::mutex> lk(search_mutex_);
307 auto wp = shm_ptr_->writer_pos.load();
309 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
312 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
314 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
318 auto buf = getBufferInfo_(buffer);
320 if (buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
322 buf->sem_id = manager_id_;
323 buf->sem = BufferSemaphoreFlags::Writing;
325 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
326 if (buf->sem_id != manager_id_)
continue;
327 buf->sequence_id = ++shm_ptr_->next_sequence_id;
329 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
337 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
339 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
343 auto buf = getBufferInfo_(buffer);
345 if (buf->sem == BufferSemaphoreFlags::Full)
347 buf->sem_id = manager_id_;
348 buf->sem = BufferSemaphoreFlags::Writing;
349 if (buf->sem_id != manager_id_)
continue;
350 buf->sequence_id = ++shm_ptr_->next_sequence_id;
352 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
354 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
360 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
362 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
366 auto buf = getBufferInfo_(buffer);
368 if (buf->sem == BufferSemaphoreFlags::Reading)
370 buf->sem_id = manager_id_;
371 buf->sem = BufferSemaphoreFlags::Writing;
372 if (buf->sem_id != manager_id_)
continue;
373 buf->sequence_id = ++shm_ptr_->next_sequence_id;
375 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
376 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
383 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
389 if (!IsValid())
return 0;
390 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN";
391 std::unique_lock<std::mutex> lk(search_mutex_);
392 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
395 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
397 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
399 auto buf = getBufferInfo_(ii);
401 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 <<
" )";
402 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_))
404 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
413 if (!IsValid())
return 0;
414 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN";
415 std::unique_lock<std::mutex> lk(search_mutex_);
417 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
419 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
421 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
423 auto buf = getBufferInfo_(ii);
425 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
426 || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
428 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is writing and marked for overwrite.";
437 if (!IsValid())
return false;
438 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN";
439 std::unique_lock<std::mutex> lk(search_mutex_);
442 auto rp = shm_ptr_->reader_pos.load();
444 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
446 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
448 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
449 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
451 auto buf = getBufferInfo_(buffer);
453 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 <<
" )" <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
454 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_))
456 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
465 if (!IsValid())
return false;
466 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN";
468 std::unique_lock<std::mutex> lk(search_mutex_);
471 auto wp = shm_ptr_->writer_pos.load();
473 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
475 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
477 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
478 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
480 auto buf = getBufferInfo_(buffer);
482 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
483 || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
485 TLOG(29) <<
"0x" << std::hex << shm_key_
487 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is writing and marked for overwrite.";
496 std::deque<int> output;
497 if (!IsValid())
return output;
498 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
501 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
502 std::unique_lock<std::mutex> lk(search_mutex_);
503 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
505 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
507 auto buf = getBufferInfo_(ii);
509 if (buf->sem_id == manager_id_)
511 output.push_back(ii);
517 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
519 auto buf = getBufferInfo_(ii);
521 if (buf->sem_id == manager_id_)
523 output.push_back(ii);
528 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << shm_ptr_->buffer_count <<
" buffers.";
534 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
536 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
538 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
539 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
540 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
543 auto buf = getBufferInfo_(buffer);
547 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
548 return buf->writePos;
554 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
556 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
558 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
559 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
560 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
563 auto buf = getBufferInfo_(buffer);
568 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
573 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
575 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
577 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
578 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
579 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
582 auto buf = getBufferInfo_(buffer);
584 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
588 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
593 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
595 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
597 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
598 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
599 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
601 auto buf = getBufferInfo_(buffer);
604 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
605 buf->readPos = buf->readPos + read;
606 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
607 if (read == 0) 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) +
")");
612 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
614 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
616 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
617 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
618 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
620 auto buf = getBufferInfo_(buffer);
621 if (!buf)
return false;
623 if (buf->writePos + written > shm_ptr_->buffer_size)
625 TLOG(TLVL_ERROR) <<
"Requested write size is larger than the buffer size! (sz=" << std::hex << shm_ptr_->buffer_size <<
", cur + req=" << buf->writePos + written <<
")";
628 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
629 buf->writePos += written;
630 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
631 if (written == 0) Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
638 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
640 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
642 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
643 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
644 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
646 auto buf = getBufferInfo_(buffer);
647 if (!buf)
return false;
648 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
649 return buf->readPos < buf->writePos;
654 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
656 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
657 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
658 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
660 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
665 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
667 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
668 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
669 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
672 auto shmBuf = getBufferInfo_(buffer);
674 touchBuffer_(shmBuf);
675 if (shmBuf->sem_id == manager_id_)
677 if (shmBuf->sem != BufferSemaphoreFlags::Full)
678 shmBuf->sem = BufferSemaphoreFlags::Full;
680 shmBuf->sem_id = destination;
686 TLOG(18) <<
"MarkBufferEmpty BEGIN";
687 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
688 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
690 auto shmBuf = getBufferInfo_(buffer);
694 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
true);
696 touchBuffer_(shmBuf);
699 shmBuf->sem = BufferSemaphoreFlags::Full;
701 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
703 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
704 shmBuf->writePos = 0;
705 shmBuf->sem = BufferSemaphoreFlags::Empty;
706 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
708 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
709 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
713 TLOG(18) <<
"MarkBufferEmpty END";
718 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
720 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
721 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
722 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
725 auto shmBuf = getBufferInfo_(buffer);
726 if (!shmBuf)
return false;
738 if (delta > 0xFFFFFFFF)
740 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
744 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
return false;
745 TLOG(27) <<
"Buffer " << buffer <<
" at " << (
void*)shmBuf <<
" is stale, time=" <<
TimeUtils::gettimeofday_us() <<
", last touch=" << shmBuf->last_touch_time <<
", d=" << delta <<
", timeout=" << shm_ptr_->buffer_timeout_us;
747 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
751 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full)
753 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
754 shmBuf->writePos = 0;
755 shmBuf->sem = BufferSemaphoreFlags::Empty;
757 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
761 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
765 if (delta <= shm_ptr_->buffer_timeout_us)
return false;
766 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " << (
void*)shmBuf
767 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
768 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
770 shmBuf->sem = BufferSemaphoreFlags::Full;
779 if (!IsValid())
return true;
781 struct shmid_ds info;
782 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
785 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
789 if (info.shm_perm.mode & SHM_DEST)
791 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
800 if (!IsValid())
return 0;
802 struct shmid_ds info;
803 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
806 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
810 return info.shm_nattch;
815 TLOG(19) <<
"Write BEGIN";
816 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
817 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
819 auto shmBuf = getBufferInfo_(buffer);
820 if (!shmBuf)
return -1;
821 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
822 touchBuffer_(shmBuf);
823 TLOG(19) <<
"Buffer Write Pos is " << shmBuf->writePos <<
", write size is " << size;
824 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
826 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
827 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
828 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
831 auto pos = GetWritePos(buffer);
832 memcpy(pos, data, size);
833 shmBuf->writePos = shmBuf->writePos + size;
835 auto last_seen = last_seen_id_.load();
836 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
838 TLOG(19) <<
"Write END";
844 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
845 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
847 auto shmBuf = getBufferInfo_(buffer);
848 if (!shmBuf)
return false;
849 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
850 touchBuffer_(shmBuf);
851 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
853 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
854 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
855 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
858 auto pos = GetReadPos(buffer);
859 memcpy(data, pos, size);
860 shmBuf->readPos += size;
861 touchBuffer_(shmBuf);
862 return checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
867 std::ostringstream ostr;
868 ostr <<
"ShmStruct: " << std::endl
869 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
870 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
871 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
872 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
873 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
874 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
875 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
876 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::endl << std::endl;
878 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
880 auto buf = getBufferInfo_(ii);
883 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
884 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
885 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
886 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
887 <<
"sem: " << FlagToString(buf->sem) << std::endl
888 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
889 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl << std::endl;
897 auto buf = getBufferInfo_(buffer);
898 if (!buf)
return nullptr;
899 return bufferStart_(buffer) + buf->readPos;
903 auto buf = getBufferInfo_(buffer);
904 if (!buf)
return nullptr;
905 return bufferStart_(buffer) + buf->writePos;
910 return bufferStart_(buffer);
915 auto output = std::list<std::pair<int, BufferSemaphoreFlags>>();
916 for (
size_t ii = 0; ii < size(); ++ii)
918 auto buf = getBufferInfo_(ii);
919 output.emplace_back(std::make_pair(buf->sem_id.load(), buf->sem.load()));
924 uint8_t* artdaq::SharedMemoryManager::dataStart_()
const
926 if (shm_ptr_ ==
nullptr)
return nullptr;
927 return reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + shm_ptr_->buffer_count *
sizeof(ShmBuffer);
930 uint8_t* artdaq::SharedMemoryManager::bufferStart_(
int buffer)
932 if (shm_ptr_ ==
nullptr)
return nullptr;
933 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
934 return dataStart_() + buffer * shm_ptr_->buffer_size;
937 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(
int buffer)
939 if (shm_ptr_ ==
nullptr)
return nullptr;
940 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
941 return reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + buffer *
sizeof(ShmBuffer));
944 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
950 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
954 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) <<
")";
957 if (buffer->sem != flags) Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
958 if (buffer->sem_id != manager_id_) 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) +
")");
960 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
964 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
965 <<
" ID: " << buffer->sem_id <<
" (" << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (" << FlagToString(flags) <<
"). "
966 <<
"ID -1 is okay if desired flag is \"Full\" or \"Empty\".";
972 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
974 if (buffer->sem_id != -1 && buffer->sem_id != manager_id_)
return;
975 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " << (
void*)buffer <<
" with sequence_id " << buffer->sequence_id;
981 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
984 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
985 auto bufs = GetBuffersOwnedByManager(
false);
986 for (
auto buf : bufs)
988 auto shmBuf = getBufferInfo_(buf);
989 if (!shmBuf)
continue;
990 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
992 shmBuf->sem = BufferSemaphoreFlags::Empty;
994 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
996 shmBuf->sem = BufferSemaphoreFlags::Full;
1004 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1009 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1011 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1012 shmctl(shm_segment_id_, IPC_RMID, NULL);
1013 shm_segment_id_ = -1;
1016 if (category.size() > 0 && message.size() > 0)
1018 TLOG(TLVL_ERROR) << category <<
": " << message;
1022 throw cet::exception(category) << message;
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.
constexpr size_t GetElapsedTimeMilliseconds(std::chrono::steady_clock::time_point then, std::chrono::steady_clock::time_point now=std::chrono::steady_clock::now())
Gets the number of milliseconds in the given time interval
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.
std::list< 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.
void Attach()
Reconnect to the shared memory segment.
void Detach(bool throwException=false, std::string category="", std::string message="", bool force=false)
Detach from the Shared Memory segment, optionally throwing a cet::exception with the specified proper...
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.