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, SIGHUP };
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);
128 if (requested_shm_parameters_.buffer_count > 0 && manager_id_ <= 0)
133 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
134 if (shm_segment_id_ == -1)
136 if (manager_id_ == 0)
138 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
139 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
141 if (shm_segment_id_ == -1)
143 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
150 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
154 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
156 if (shm_segment_id_ > -1)
159 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
160 <<
" and size " << shmSize
162 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
164 <<
"Attached to shared memory segment at address "
165 << std::hex << (
void*)shm_ptr_ << std::dec;
166 if (shm_ptr_ && shm_ptr_ != (
void *)-1)
168 if (manager_id_ == 0)
170 if (shm_ptr_->ready_magic == 0xCAFE1111)
172 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
173 <<
"Once the system is shut down, you can use one of the following commands "
174 <<
"to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
175 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
178 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
179 shm_ptr_->next_id = 1;
180 shm_ptr_->next_sequence_id = 0;
181 shm_ptr_->reader_pos = 0;
182 shm_ptr_->writer_pos = 0;
183 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
184 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
185 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
186 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
188 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
190 if (!getBufferInfo_(ii))
return;
191 getBufferInfo_(ii)->writePos = 0;
192 getBufferInfo_(ii)->readPos = 0;
193 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
194 getBufferInfo_(ii)->sem_id = -1;
198 shm_ptr_->ready_magic = 0xCAFE1111;
202 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
203 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
204 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
206 shm_ptr_->lowest_seq_id_read = 0;
207 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
210 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
211 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
212 <<
"key: 0x" << std::hex << shm_key_
213 <<
", manager ID: " << std::dec << manager_id_
214 <<
", Buffer size: " << shm_ptr_->buffer_size
215 <<
", Buffer count: " << shm_ptr_->buffer_count;
220 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
226 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
227 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")" <<
". Please check "
228 <<
"if a stale shared memory segment needs to "
229 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
236 TLOG(13) <<
"GetBufferForReading BEGIN";
238 std::unique_lock<std::mutex> lk(search_mutex_);
240 auto rp = shm_ptr_->reader_pos.load();
242 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
244 for (
int retry = 0; retry < 5; retry++)
249 ShmBuffer* buffer_ptr =
nullptr;
252 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
254 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
257 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
260 auto buf = getBufferInfo_(buffer);
263 sem = buf->sem.load();
264 sem_id = buf->sem_id.load();
266 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
267 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
268 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_)
269 && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
271 if (buf->sequence_id < seqID)
274 seqID = buf->sequence_id;
277 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
break;
284 sem = buffer_ptr->sem.load();
285 sem_id = buffer_ptr->sem_id.load();
288 if (!buffer_ptr || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
295 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
296 touchBuffer_(buffer_ptr);
297 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
298 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
continue;
299 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false)) {
300 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
303 buffer_ptr->readPos = 0;
304 touchBuffer_(buffer_ptr);
305 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false)) {
306 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
309 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
311 shm_ptr_->lowest_seq_id_read = seqID;
313 last_seen_id_ = seqID;
314 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
316 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
322 TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
328 TLOG(14) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
330 std::unique_lock<std::mutex> lk(search_mutex_);
332 auto wp = shm_ptr_->writer_pos.load();
334 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
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);
346 auto sem = buf->sem.load();
347 auto sem_id = buf->sem_id.load();
349 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
352 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
353 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
354 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
355 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
356 buf->sequence_id = ++shm_ptr_->next_sequence_id;
358 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
360 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
368 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
370 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
374 auto buf = getBufferInfo_(buffer);
377 auto sem = buf->sem.load();
378 auto sem_id = buf->sem_id.load();
380 if (sem == BufferSemaphoreFlags::Full)
383 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
384 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
385 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
386 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
387 buf->sequence_id = ++shm_ptr_->next_sequence_id;
389 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
391 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
397 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
399 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
403 auto buf = getBufferInfo_(buffer);
406 auto sem = buf->sem.load();
407 auto sem_id = buf->sem_id.load();
409 if (sem == BufferSemaphoreFlags::Reading)
412 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
413 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
414 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
415 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
416 buf->sequence_id = ++shm_ptr_->next_sequence_id;
418 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
420 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
425 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
431 if (!IsValid())
return 0;
432 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
433 std::unique_lock<std::mutex> lk(search_mutex_);
434 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
437 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
439 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
441 auto buf = getBufferInfo_(ii);
443 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 <<
" )";
444 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_))
446 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
456 if (!IsValid())
return 0;
457 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
458 std::unique_lock<std::mutex> lk(search_mutex_);
460 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
462 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
464 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
466 auto buf = getBufferInfo_(ii);
468 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
469 || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
471 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
480 if (!IsValid())
return false;
481 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
482 std::unique_lock<std::mutex> lk(search_mutex_);
485 auto rp = shm_ptr_->reader_pos.load();
487 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
489 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
491 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
492 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
494 auto buf = getBufferInfo_(buffer);
496 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_;
497 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_))
499 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
509 if (!IsValid())
return false;
510 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
512 std::unique_lock<std::mutex> lk(search_mutex_);
515 auto wp = shm_ptr_->writer_pos.load();
517 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
519 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
521 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
522 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
524 auto buf = getBufferInfo_(buffer);
526 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
527 || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
529 TLOG(29) <<
"0x" << std::hex << shm_key_
531 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
540 std::deque<int> output;
541 if (!IsValid())
return output;
542 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
545 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
546 std::unique_lock<std::mutex> lk(search_mutex_);
547 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
549 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
551 auto buf = getBufferInfo_(ii);
553 if (buf->sem_id == manager_id_)
555 output.push_back(ii);
561 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
563 auto buf = getBufferInfo_(ii);
565 if (buf->sem_id == manager_id_)
567 output.push_back(ii);
572 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << shm_ptr_->buffer_count <<
" buffers.";
578 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
580 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
582 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
583 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
584 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
587 auto buf = getBufferInfo_(buffer);
591 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
592 return buf->writePos;
597 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
599 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
601 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
602 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
603 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
606 auto buf = getBufferInfo_(buffer);
607 if (!buf || buf->sem_id != manager_id_)
return;
611 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
616 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
618 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
620 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
621 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
622 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
625 auto buf = getBufferInfo_(buffer);
627 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
631 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
636 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
638 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
640 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
641 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
642 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
644 auto buf = getBufferInfo_(buffer);
645 if (!buf || buf->sem_id != manager_id_)
return;
647 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
648 buf->readPos = buf->readPos + read;
649 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
650 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) +
")");
655 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
657 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
659 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
660 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
661 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
663 auto buf = getBufferInfo_(buffer);
664 if (!buf)
return false;
665 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
667 if (buf->writePos + written > shm_ptr_->buffer_size)
669 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 <<
")";
672 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
673 buf->writePos += written;
674 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
675 if (written == 0) Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
682 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
684 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
686 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
687 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
688 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
690 auto buf = getBufferInfo_(buffer);
691 if (!buf)
return false;
692 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
693 return buf->readPos < buf->writePos;
698 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
700 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
701 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
702 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
704 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
709 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
711 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
712 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
713 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
716 auto shmBuf = getBufferInfo_(buffer);
718 touchBuffer_(shmBuf);
719 if (shmBuf->sem_id == manager_id_)
721 if (shmBuf->sem != BufferSemaphoreFlags::Full)
722 shmBuf->sem = BufferSemaphoreFlags::Full;
724 shmBuf->sem_id = destination;
730 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
731 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
732 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
734 auto shmBuf = getBufferInfo_(buffer);
738 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
true);
740 touchBuffer_(shmBuf);
743 shmBuf->sem = BufferSemaphoreFlags::Full;
745 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
747 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
748 shmBuf->writePos = 0;
749 shmBuf->sem = BufferSemaphoreFlags::Empty;
750 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
752 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
753 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
757 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;;
762 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
764 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
765 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
766 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
769 auto shmBuf = getBufferInfo_(buffer);
770 if (!shmBuf)
return false;
782 if (delta > 0xFFFFFFFF)
784 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
788 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
return false;
789 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;
791 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
796 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && (shmBuf->sequence_id < last_seen_id_ || manager_id_ == 0))
798 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
799 shmBuf->writePos = 0;
800 shmBuf->sem = BufferSemaphoreFlags::Empty;
802 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
806 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
810 if (delta <= shm_ptr_->buffer_timeout_us)
return false;
811 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " << (
void*)shmBuf
812 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
813 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
815 shmBuf->sem = BufferSemaphoreFlags::Full;
824 if (!IsValid())
return true;
826 struct shmid_ds info;
827 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
830 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
834 if (info.shm_perm.mode & SHM_DEST)
836 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
845 if (!IsValid())
return 0;
847 struct shmid_ds info;
848 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
851 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
855 return info.shm_nattch;
860 TLOG(19) <<
"Write BEGIN";
861 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
862 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
864 auto shmBuf = getBufferInfo_(buffer);
865 if (!shmBuf)
return -1;
866 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
867 touchBuffer_(shmBuf);
868 TLOG(19) <<
"Buffer Write Pos is " << shmBuf->writePos <<
", write size is " << size;
869 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
871 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
872 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
873 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
876 auto pos = GetWritePos(buffer);
877 memcpy(pos, data, size);
878 touchBuffer_(shmBuf);
879 shmBuf->writePos = shmBuf->writePos + size;
881 auto last_seen = last_seen_id_.load();
882 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
884 TLOG(19) <<
"Write END";
890 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
891 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
893 auto shmBuf = getBufferInfo_(buffer);
894 if (!shmBuf)
return false;
895 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
896 touchBuffer_(shmBuf);
897 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
899 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
900 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
901 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
904 auto pos = GetReadPos(buffer);
905 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
906 memcpy(data, pos, size);
907 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
908 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
911 shmBuf->readPos += size;
912 touchBuffer_(shmBuf);
920 std::ostringstream ostr;
921 ostr <<
"ShmStruct: " << std::endl
922 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
923 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
924 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
925 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
926 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
927 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
928 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
929 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl << std::endl;
931 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
933 auto buf = getBufferInfo_(ii);
936 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
937 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
938 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
939 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
940 <<
"sem: " << FlagToString(buf->sem) << std::endl
941 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
942 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl << std::endl;
950 auto buf = getBufferInfo_(buffer);
951 if (!buf)
return nullptr;
952 return bufferStart_(buffer) + buf->readPos;
956 auto buf = getBufferInfo_(buffer);
957 if (!buf)
return nullptr;
958 return bufferStart_(buffer) + buf->writePos;
963 return bufferStart_(buffer);
968 auto output = std::list<std::pair<int, BufferSemaphoreFlags>>();
969 for (
size_t ii = 0; ii < size(); ++ii)
971 auto buf = getBufferInfo_(ii);
972 output.emplace_back(std::make_pair(buf->sem_id.load(), buf->sem.load()));
977 uint8_t* artdaq::SharedMemoryManager::dataStart_()
const
979 if (shm_ptr_ ==
nullptr)
return nullptr;
980 return reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + shm_ptr_->buffer_count *
sizeof(ShmBuffer);
983 uint8_t* artdaq::SharedMemoryManager::bufferStart_(
int buffer)
985 if (shm_ptr_ ==
nullptr)
return nullptr;
986 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
987 return dataStart_() + buffer * shm_ptr_->buffer_size;
990 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(
int buffer)
992 if (shm_ptr_ ==
nullptr)
return nullptr;
993 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
994 return reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + buffer *
sizeof(ShmBuffer));
997 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1003 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1007 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) <<
")";
1010 if (buffer->sem != flags) Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1011 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) +
")");
1013 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1017 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1018 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1019 <<
"ID -1 is okay if expected flag is \"Full\" or \"Empty\".";
1025 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1027 if (!buffer || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
return;
1028 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " << (
void*)buffer <<
" with sequence_id " << buffer->sequence_id;
1034 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1037 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1038 auto bufs = GetBuffersOwnedByManager(
false);
1039 for (
auto buf : bufs)
1041 auto shmBuf = getBufferInfo_(buf);
1042 if (!shmBuf)
continue;
1043 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1045 shmBuf->sem = BufferSemaphoreFlags::Empty;
1047 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1049 shmBuf->sem = BufferSemaphoreFlags::Full;
1051 shmBuf->sem_id = -1;
1057 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1062 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1064 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1065 shmctl(shm_segment_id_, IPC_RMID, NULL);
1066 shm_segment_id_ = -1;
1069 if (category.size() > 0 && message.size() > 0)
1071 TLOG(TLVL_ERROR) << category <<
": " << message;
1075 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.