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 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)
40 pthread_sigmask(SIG_UNBLOCK, NULL, &set);
41 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
43 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0) <<
"Calling default signal handler";
44 if (signum != SIGUSR2)
46 sigaction(signum, &old_actions[signum], NULL);
47 kill(getpid(), signum);
52 sigaction(SIGINT, &old_actions[SIGINT], NULL);
53 kill(getpid(), SIGINT);
65 requested_shm_parameters_.buffer_count = buffer_count;
66 requested_shm_parameters_.buffer_size = buffer_size;
67 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
68 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
70 instances.push_back(
this);
73 static std::mutex sighandler_mutex;
74 std::unique_lock<std::mutex> lk(sighandler_mutex);
78 sighandler_init =
true;
79 std::vector<int> signals = { SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP };
80 for (
auto signal : signals)
82 struct sigaction old_action;
83 sigaction(signal, NULL, &old_action);
87 if (old_action.sa_handler != SIG_IGN)
89 struct sigaction action;
90 action.sa_handler = signal_handler;
91 sigemptyset(&action.sa_mask);
92 for (
auto sigblk : signals)
94 sigaddset(&action.sa_mask, sigblk);
99 sigaction(signal, &action, NULL);
100 old_actions[signal] = old_action;
109 static std::mutex destructor_mutex;
110 std::lock_guard<std::mutex> lk(destructor_mutex);
111 for (
auto it = instances.begin(); it != instances.end(); ++it) {
114 it = instances.erase(it);
119 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager called";
121 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager done";
128 if (manager_id_ == 0)
return true;
132 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
133 auto start_time = std::chrono::steady_clock::now();
135 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
139 if (requested_shm_parameters_.buffer_count > 0 && manager_id_ <= 0)
144 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
145 if (shm_segment_id_ == -1)
147 if (manager_id_ == 0)
149 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
150 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
152 if (shm_segment_id_ == -1)
154 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
161 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
165 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
167 if (shm_segment_id_ > -1)
170 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
171 <<
" and size " << shmSize
173 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
175 <<
"Attached to shared memory segment at address "
176 << std::hex << (
void*)shm_ptr_ << std::dec;
177 if (shm_ptr_ && shm_ptr_ != (
void *)-1)
179 if (manager_id_ == 0)
181 if (shm_ptr_->ready_magic == 0xCAFE1111)
183 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
184 <<
"Once the system is shut down, you can use one of the following commands "
185 <<
"to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
186 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
189 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
190 shm_ptr_->next_id = 1;
191 shm_ptr_->next_sequence_id = 0;
192 shm_ptr_->reader_pos = 0;
193 shm_ptr_->writer_pos = 0;
194 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
195 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
196 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
197 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
199 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
201 if (!getBufferInfo_(ii))
return false;
202 getBufferInfo_(ii)->writePos = 0;
203 getBufferInfo_(ii)->readPos = 0;
204 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
205 getBufferInfo_(ii)->sem_id = -1;
209 shm_ptr_->ready_magic = 0xCAFE1111;
213 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
214 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
215 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
217 shm_ptr_->lowest_seq_id_read = 0;
218 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
221 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
222 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
223 <<
"key: 0x" << std::hex << shm_key_
224 <<
", manager ID: " << std::dec << manager_id_
225 <<
", Buffer size: " << shm_ptr_->buffer_size
226 <<
", Buffer count: " << shm_ptr_->buffer_count;
231 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
238 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
239 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
241 <<
"if a stale shared memory segment needs to "
242 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
252 TLOG(13) <<
"GetBufferForReading BEGIN";
254 std::unique_lock<std::mutex> lk(search_mutex_);
256 auto rp = shm_ptr_->reader_pos.load();
258 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
260 for (
int retry = 0; retry < 5; retry++)
265 ShmBuffer* buffer_ptr =
nullptr;
268 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
270 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
272 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
275 auto buf = getBufferInfo_(buffer);
278 sem = buf->sem.load();
279 sem_id = buf->sem_id.load();
281 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
282 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
283 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
285 if (buf->sequence_id < seqID)
288 seqID = buf->sequence_id;
291 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
break;
298 sem = buffer_ptr->sem.load();
299 sem_id = buffer_ptr->sem_id.load();
302 if (!buffer_ptr || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
309 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
310 touchBuffer_(buffer_ptr);
311 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
312 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
continue;
313 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
315 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
318 buffer_ptr->readPos = 0;
319 touchBuffer_(buffer_ptr);
320 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
322 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
325 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
327 shm_ptr_->lowest_seq_id_read = seqID;
329 last_seen_id_ = seqID;
330 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
332 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
338 TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
344 TLOG(14) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
346 std::unique_lock<std::mutex> lk(search_mutex_);
348 auto wp = shm_ptr_->writer_pos.load();
350 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
353 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
355 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
359 auto buf = getBufferInfo_(buffer);
362 auto sem = buf->sem.load();
363 auto sem_id = buf->sem_id.load();
365 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
368 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
369 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
370 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
371 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
372 buf->sequence_id = ++shm_ptr_->next_sequence_id;
374 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
376 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
384 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
386 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
390 auto buf = getBufferInfo_(buffer);
393 auto sem = buf->sem.load();
394 auto sem_id = buf->sem_id.load();
396 if (sem == BufferSemaphoreFlags::Full)
399 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
400 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
401 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
402 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
403 buf->sequence_id = ++shm_ptr_->next_sequence_id;
405 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
407 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
413 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
415 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
419 auto buf = getBufferInfo_(buffer);
422 auto sem = buf->sem.load();
423 auto sem_id = buf->sem_id.load();
425 if (sem == BufferSemaphoreFlags::Reading)
428 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
429 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
430 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
431 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
432 buf->sequence_id = ++shm_ptr_->next_sequence_id;
434 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
436 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
441 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
447 if (!IsValid())
return 0;
448 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
449 std::unique_lock<std::mutex> lk(search_mutex_);
450 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
453 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
455 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
457 auto buf = getBufferInfo_(ii);
459 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 <<
" )";
460 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_))
462 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
472 if (!IsValid())
return 0;
473 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
474 std::unique_lock<std::mutex> lk(search_mutex_);
476 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
478 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
480 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
482 auto buf = getBufferInfo_(ii);
484 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
486 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
495 if (!IsValid())
return false;
496 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
497 std::unique_lock<std::mutex> lk(search_mutex_);
500 auto rp = shm_ptr_->reader_pos.load();
502 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
504 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
506 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
507 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
509 auto buf = getBufferInfo_(buffer);
511 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 <<
" )"
512 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
513 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_))
515 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
525 if (!IsValid())
return false;
526 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
528 std::unique_lock<std::mutex> lk(search_mutex_);
531 auto wp = shm_ptr_->writer_pos.load();
533 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
535 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
537 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
538 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
540 auto buf = getBufferInfo_(buffer);
542 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
544 TLOG(29) <<
"0x" << std::hex << shm_key_
546 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
555 std::deque<int> output;
556 if (!IsValid())
return output;
557 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
560 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
561 std::unique_lock<std::mutex> lk(search_mutex_);
562 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
564 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
566 auto buf = getBufferInfo_(ii);
568 if (buf->sem_id == manager_id_)
570 output.push_back(ii);
576 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
578 auto buf = getBufferInfo_(ii);
580 if (buf->sem_id == manager_id_)
582 output.push_back(ii);
587 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << shm_ptr_->buffer_count <<
" buffers.";
593 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
595 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
597 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
598 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
599 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
602 auto buf = getBufferInfo_(buffer);
606 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
607 return buf->writePos;
612 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
614 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
616 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
617 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
618 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
621 auto buf = getBufferInfo_(buffer);
622 if (!buf || buf->sem_id != manager_id_)
return;
626 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
631 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
633 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
635 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
636 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
637 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
640 auto buf = getBufferInfo_(buffer);
642 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
646 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
651 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
653 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
655 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
656 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
657 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
659 auto buf = getBufferInfo_(buffer);
660 if (!buf || buf->sem_id != manager_id_)
return;
662 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
663 buf->readPos = buf->readPos + read;
664 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
665 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) +
")");
670 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
672 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
674 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
675 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
676 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
678 auto buf = getBufferInfo_(buffer);
679 if (!buf)
return false;
680 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
682 if (buf->writePos + written > shm_ptr_->buffer_size)
684 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 <<
")";
687 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
688 buf->writePos += written;
689 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
690 if (written == 0) Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
697 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
699 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
701 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
702 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
703 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
705 auto buf = getBufferInfo_(buffer);
706 if (!buf)
return false;
707 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
708 return buf->readPos < buf->writePos;
713 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
715 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
716 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
717 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
719 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
724 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
726 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
727 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
728 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
731 auto shmBuf = getBufferInfo_(buffer);
733 touchBuffer_(shmBuf);
734 if (shmBuf->sem_id == manager_id_)
736 if (shmBuf->sem != BufferSemaphoreFlags::Full)
737 shmBuf->sem = BufferSemaphoreFlags::Full;
739 shmBuf->sem_id = destination;
745 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
746 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
747 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
749 auto shmBuf = getBufferInfo_(buffer);
753 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
true);
755 touchBuffer_(shmBuf);
758 shmBuf->sem = BufferSemaphoreFlags::Full;
760 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
762 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
763 shmBuf->writePos = 0;
764 shmBuf->sem = BufferSemaphoreFlags::Empty;
765 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
767 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
768 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
772 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
778 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
780 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
781 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
782 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
785 auto shmBuf = getBufferInfo_(buffer);
786 if (!shmBuf)
return false;
798 if (delta > 0xFFFFFFFF)
800 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
804 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
return false;
805 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;
807 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
812 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && (shmBuf->sequence_id < last_seen_id_ || manager_id_ == 0))
814 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
815 shmBuf->writePos = 0;
816 shmBuf->sem = BufferSemaphoreFlags::Empty;
818 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
822 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
826 if (delta <= shm_ptr_->buffer_timeout_us)
return false;
827 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " << (
void*)shmBuf
828 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
829 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
831 shmBuf->sem = BufferSemaphoreFlags::Full;
840 if (!IsValid())
return true;
842 struct shmid_ds info;
843 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
846 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
850 if (info.shm_perm.mode & SHM_DEST)
852 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
861 if (!IsValid())
return 0;
863 struct shmid_ds info;
864 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
867 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
871 return info.shm_nattch;
876 TLOG(19) <<
"Write BEGIN";
877 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
878 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
880 auto shmBuf = getBufferInfo_(buffer);
881 if (!shmBuf)
return -1;
882 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
883 touchBuffer_(shmBuf);
884 TLOG(19) <<
"Buffer Write Pos is " << shmBuf->writePos <<
", write size is " << size;
885 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
887 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
888 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
889 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
892 auto pos = GetWritePos(buffer);
893 memcpy(pos, data, size);
894 touchBuffer_(shmBuf);
895 shmBuf->writePos = shmBuf->writePos + size;
897 auto last_seen = last_seen_id_.load();
898 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
900 TLOG(19) <<
"Write END";
906 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
907 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
909 auto shmBuf = getBufferInfo_(buffer);
910 if (!shmBuf)
return false;
911 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
912 touchBuffer_(shmBuf);
913 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
915 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
916 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
917 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
920 auto pos = GetReadPos(buffer);
921 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
922 memcpy(data, pos, size);
923 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
924 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
927 shmBuf->readPos += size;
928 touchBuffer_(shmBuf);
936 std::ostringstream ostr;
937 ostr <<
"ShmStruct: " << std::endl
938 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
939 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
940 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
941 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
942 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
943 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
944 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
945 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
948 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
950 auto buf = getBufferInfo_(ii);
953 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
954 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
955 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
956 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
957 <<
"sem: " << FlagToString(buf->sem) << std::endl
958 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
959 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
968 auto buf = getBufferInfo_(buffer);
969 if (!buf)
return nullptr;
970 return bufferStart_(buffer) + buf->readPos;
974 auto buf = getBufferInfo_(buffer);
975 if (!buf)
return nullptr;
976 return bufferStart_(buffer) + buf->writePos;
981 return bufferStart_(buffer);
986 auto output = std::list<std::pair<int, BufferSemaphoreFlags>>();
987 for (
size_t ii = 0; ii < size(); ++ii)
989 auto buf = getBufferInfo_(ii);
990 output.emplace_back(std::make_pair(buf->sem_id.load(), buf->sem.load()));
995 uint8_t* artdaq::SharedMemoryManager::dataStart_()
const
997 if (shm_ptr_ ==
nullptr)
return nullptr;
998 return reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + shm_ptr_->buffer_count *
sizeof(ShmBuffer);
1001 uint8_t* artdaq::SharedMemoryManager::bufferStart_(
int buffer)
1003 if (shm_ptr_ ==
nullptr)
return nullptr;
1004 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1005 return dataStart_() + buffer * shm_ptr_->buffer_size;
1008 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(
int buffer)
1010 if (shm_ptr_ ==
nullptr)
return nullptr;
1011 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1012 return reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + buffer *
sizeof(ShmBuffer));
1015 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1021 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1025 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) <<
")";
1028 if (buffer->sem != flags) Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1029 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) +
")");
1031 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1035 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1036 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1037 <<
"ID -1 is okay if expected flag is \"Full\" or \"Empty\".";
1043 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1045 if (!buffer || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
return;
1046 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " << (
void*)buffer <<
" with sequence_id " << buffer->sequence_id;
1052 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1055 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1056 auto bufs = GetBuffersOwnedByManager(
false);
1057 for (
auto buf : bufs)
1059 auto shmBuf = getBufferInfo_(buf);
1060 if (!shmBuf)
continue;
1061 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1063 shmBuf->sem = BufferSemaphoreFlags::Empty;
1065 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1067 shmBuf->sem = BufferSemaphoreFlags::Full;
1069 shmBuf->sem_id = -1;
1075 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1080 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1082 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1083 shmctl(shm_segment_id_, IPC_RMID, NULL);
1084 shm_segment_id_ = -1;
1090 if (category.size() > 0 && message.size() > 0)
1092 TLOG(TLVL_ERROR) << category <<
": " << message;
1096 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.
bool Attach(size_t timeout_usec=0)
Reconnect to the shared memory segment.
std::deque< int > GetBuffersOwnedByManager(bool locked=true)
Get the list of all buffers currently owned by this manager instance.
void ResetReadPos(int buffer)
Set the read position of the given buffer to the beginning of the buffer.
bool MoreDataInBuffer(int buffer)
Determine if more data is available to be read, based on the read position and data size...
virtual std::string toString()
Write information about the SharedMemory to a string.
constexpr size_t GetElapsedTimeMicroseconds(std::chrono::steady_clock::time_point then, std::chrono::steady_clock::time_point now=std::chrono::steady_clock::now())
Gets the number of microseconds in the given time interval
std::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 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.