1 #define TRACE_NAME "SharedMemoryManager"
6 #include <unordered_map>
7 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!?
11 #include "artdaq-core/Core/SharedMemoryManager.hh"
12 #include "artdaq-core/Utilities/TraceLock.hh"
13 #include "cetlib_except/exception.h"
16 #define TLVL_DETACH 11
17 #define TLVL_BUFFER 40
18 #define TLVL_BUFLCK 41
20 static std::list<artdaq::SharedMemoryManager const*> instances = std::list<artdaq::SharedMemoryManager const*>();
22 static std::unordered_map<int, struct sigaction> old_actions = std::unordered_map<int, struct sigaction>();
23 static bool sighandler_init =
false;
24 static std::mutex sighandler_mutex;
26 static void signal_handler(
int signum)
29 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0) <<
"A signal of type " << signum <<
" was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!";
30 for (
auto ii : instances)
42 pthread_sigmask(SIG_UNBLOCK, NULL, &set);
43 pthread_sigmask(SIG_UNBLOCK, &set, NULL);
45 TRACE_STREAMER(TLVL_ERROR, &(
"SharedMemoryManager")[0], 0, 0, 0) <<
"Calling default signal handler";
46 if (signum != SIGUSR2)
48 sigaction(signum, &old_actions[signum], NULL);
49 kill(getpid(), signum);
54 sigaction(SIGINT, &old_actions[SIGINT], NULL);
55 kill(getpid(), SIGINT);
68 requested_shm_parameters_.buffer_count = buffer_count;
69 requested_shm_parameters_.buffer_size = buffer_size;
70 requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
71 requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
73 instances.push_back(
this);
76 std::lock_guard<std::mutex> lk(sighandler_mutex);
80 sighandler_init =
true;
81 std::vector<int> signals = {SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP};
82 for (
auto signal : signals)
84 struct sigaction old_action;
85 sigaction(signal, NULL, &old_action);
89 if (old_action.sa_handler != SIG_IGN)
91 struct sigaction action;
92 action.sa_handler = signal_handler;
93 sigemptyset(&action.sa_mask);
94 for (
auto sigblk : signals)
96 sigaddset(&action.sa_mask, sigblk);
101 sigaction(signal, &action, NULL);
103 old_actions[signal] = old_action;
110 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager called";
112 static std::mutex destructor_mutex;
113 std::lock_guard<std::mutex> lk(destructor_mutex);
114 for (
auto it = instances.begin(); it != instances.end(); ++it)
118 it = instances.erase(it);
125 std::lock_guard<std::mutex> lk(sighandler_mutex);
128 if (sighandler_init && instances.size() == 0)
130 sighandler_init =
false;
131 for (
auto signal : old_actions)
133 sigaction(signal.first, &signal.second, NULL);
138 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager done";
145 if (manager_id_ == 0)
return true;
149 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
150 auto start_time = std::chrono::steady_clock::now();
152 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
156 if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0)
161 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
162 if (shm_segment_id_ == -1)
164 if (manager_id_ == 0)
166 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
167 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
169 if (shm_segment_id_ == -1)
171 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
178 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
182 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
184 if (shm_segment_id_ > -1)
187 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
188 <<
" and size " << shmSize
190 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
192 <<
"Attached to shared memory segment at address "
193 << std::hex << (
void*)shm_ptr_ << std::dec;
194 if (shm_ptr_ && shm_ptr_ != (
void*)-1)
196 if (manager_id_ == 0)
198 if (shm_ptr_->ready_magic == 0xCAFE1111)
200 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
201 <<
"Once the system is shut down, you can use one of the following commands "
202 <<
"to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
203 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
206 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
207 shm_ptr_->next_id = 1;
208 shm_ptr_->next_sequence_id = 0;
209 shm_ptr_->reader_pos = 0;
210 shm_ptr_->writer_pos = 0;
211 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
212 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
213 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
214 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
216 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
217 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
219 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
220 reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
221 if (!getBufferInfo_(ii))
return false;
222 getBufferInfo_(ii)->writePos = 0;
223 getBufferInfo_(ii)->readPos = 0;
224 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
225 getBufferInfo_(ii)->sem_id = -1;
229 shm_ptr_->ready_magic = 0xCAFE1111;
233 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
234 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
235 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
237 shm_ptr_->lowest_seq_id_read = 0;
238 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
240 requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count;
241 buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
242 for (
int ii = 0; ii < shm_ptr_->buffer_count; ++ii)
244 buffer_ptrs_[ii] =
reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + ii *
sizeof(ShmBuffer));
249 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
251 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
252 <<
"key: 0x" << std::hex << shm_key_
253 <<
", manager ID: " << std::dec << manager_id_
254 <<
", Buffer size: " << shm_ptr_->buffer_size
255 <<
", Buffer count: " << shm_ptr_->buffer_count;
260 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
267 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
268 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
270 <<
"if a stale shared memory segment needs to "
271 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
281 TLOG(13) <<
"GetBufferForReading BEGIN";
283 std::lock_guard<std::mutex> lk(search_mutex_);
285 auto rp = shm_ptr_->reader_pos.load();
287 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
289 for (
int retry = 0; retry < 5; retry++)
294 ShmBuffer* buffer_ptr =
nullptr;
297 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
299 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
301 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
304 auto buf = getBufferInfo_(buffer);
307 sem = buf->sem.load();
308 sem_id = buf->sem_id.load();
310 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
311 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
312 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
314 if (buf->sequence_id < seqID)
317 seqID = buf->sequence_id;
320 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
break;
327 sem = buffer_ptr->sem.load();
328 sem_id = buffer_ptr->sem_id.load();
331 if (!buffer_ptr || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
338 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
339 touchBuffer_(buffer_ptr);
340 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
341 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
continue;
342 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
344 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
347 buffer_ptr->readPos = 0;
348 touchBuffer_(buffer_ptr);
349 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
351 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
354 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
356 shm_ptr_->lowest_seq_id_read = seqID;
358 last_seen_id_ = seqID;
359 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
361 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
367 TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
373 TLOG(14) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
375 std::lock_guard<std::mutex> lk(search_mutex_);
377 auto wp = shm_ptr_->writer_pos.load();
379 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
382 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
384 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
388 auto buf = getBufferInfo_(buffer);
391 auto sem = buf->sem.load();
392 auto sem_id = buf->sem_id.load();
394 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
397 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
398 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
399 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
400 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
401 buf->sequence_id = ++shm_ptr_->next_sequence_id;
403 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
405 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::Full)
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;
442 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
444 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
448 auto buf = getBufferInfo_(buffer);
451 auto sem = buf->sem.load();
452 auto sem_id = buf->sem_id.load();
454 if (sem == BufferSemaphoreFlags::Reading)
457 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
458 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
459 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
460 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
461 buf->sequence_id = ++shm_ptr_->next_sequence_id;
463 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
465 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
470 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
476 if (!IsValid())
return 0;
477 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
478 std::unique_lock<std::mutex> lk(search_mutex_);
479 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
482 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
485 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
488 auto buf = getBufferInfo_(ii);
492 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 <<
" )";
494 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_))
497 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
508 if (!IsValid())
return 0;
509 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
510 std::unique_lock<std::mutex> lk(search_mutex_);
512 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
514 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
518 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
521 auto buf = getBufferInfo_(ii);
523 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
526 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
536 if (!IsValid())
return false;
537 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
538 std::unique_lock<std::mutex> lk(search_mutex_);
541 auto rp = shm_ptr_->reader_pos.load();
543 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
545 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
547 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
550 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
553 auto buf = getBufferInfo_(buffer);
557 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 <<
" )"
558 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
561 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_))
563 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
573 if (!IsValid())
return false;
574 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
576 std::lock_guard<std::mutex> lk(search_mutex_);
579 auto wp = shm_ptr_->writer_pos.load();
581 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
583 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
585 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
586 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
588 auto buf = getBufferInfo_(buffer);
590 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
592 TLOG(29) <<
"0x" << std::hex << shm_key_
594 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
603 std::deque<int> output;
604 if (!IsValid())
return output;
605 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
608 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
609 std::lock_guard<std::mutex> lk(search_mutex_);
610 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
612 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
614 auto buf = getBufferInfo_(ii);
616 if (buf->sem_id == manager_id_)
618 output.push_back(ii);
624 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
626 auto buf = getBufferInfo_(ii);
628 if (buf->sem_id == manager_id_)
630 output.push_back(ii);
635 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << shm_ptr_->buffer_count <<
" buffers.";
641 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
643 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
645 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
646 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
647 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
650 auto buf = getBufferInfo_(buffer);
654 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
655 return buf->writePos;
660 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
662 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
664 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
665 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
666 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
669 auto buf = getBufferInfo_(buffer);
670 if (!buf || buf->sem_id != manager_id_)
return;
674 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
679 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
681 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
683 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
684 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
685 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
688 auto buf = getBufferInfo_(buffer);
690 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
694 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
699 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
701 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
703 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
704 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
705 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
707 auto buf = getBufferInfo_(buffer);
708 if (!buf || buf->sem_id != manager_id_)
return;
710 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
711 buf->readPos = buf->readPos + read;
712 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
713 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) +
")");
718 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
720 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
722 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
723 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
724 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
726 auto buf = getBufferInfo_(buffer);
727 if (!buf)
return false;
728 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
730 if (buf->writePos + written > shm_ptr_->buffer_size)
732 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 <<
")";
735 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
736 buf->writePos += written;
737 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
738 if (written == 0) Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
745 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
747 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
749 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
750 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
751 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
753 auto buf = getBufferInfo_(buffer);
754 if (!buf)
return false;
755 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
756 return buf->readPos < buf->writePos;
761 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
763 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
764 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
765 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
767 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
772 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
774 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
775 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
776 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
779 auto shmBuf = getBufferInfo_(buffer);
781 touchBuffer_(shmBuf);
782 if (shmBuf->sem_id == manager_id_)
784 if (shmBuf->sem != BufferSemaphoreFlags::Full)
785 shmBuf->sem = BufferSemaphoreFlags::Full;
787 shmBuf->sem_id = destination;
793 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
794 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
795 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
797 auto shmBuf = getBufferInfo_(buffer);
801 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
true);
803 touchBuffer_(shmBuf);
806 shmBuf->sem = BufferSemaphoreFlags::Full;
808 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
810 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
811 shmBuf->writePos = 0;
812 shmBuf->sem = BufferSemaphoreFlags::Empty;
813 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
815 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
816 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
820 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
826 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
830 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
834 auto shmBuf = getBufferInfo_(buffer);
835 if (!shmBuf)
return false;
847 if (delta > 0xFFFFFFFF)
849 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
853 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
return false;
854 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;
856 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
861 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && (shmBuf->sequence_id < last_seen_id_ || manager_id_ == 0))
863 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
864 shmBuf->writePos = 0;
865 shmBuf->sem = BufferSemaphoreFlags::Empty;
867 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
871 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
875 if (delta <= shm_ptr_->buffer_timeout_us)
return false;
876 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " << (
void*)shmBuf
877 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
878 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
880 shmBuf->sem = BufferSemaphoreFlags::Full;
889 if (!IsValid())
return true;
891 struct shmid_ds info;
892 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
895 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
899 if (info.shm_perm.mode & SHM_DEST)
901 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
910 if (!IsValid())
return 0;
912 struct shmid_ds info;
913 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
916 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
920 return info.shm_nattch;
925 TLOG(19) <<
"Write BEGIN";
926 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
927 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
929 auto shmBuf = getBufferInfo_(buffer);
930 if (!shmBuf)
return -1;
931 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
932 touchBuffer_(shmBuf);
933 TLOG(19) <<
"Buffer Write Pos is " << shmBuf->writePos <<
", write size is " << size;
934 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
936 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
937 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
938 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
941 auto pos = GetWritePos(buffer);
942 memcpy(pos, data, size);
943 touchBuffer_(shmBuf);
944 shmBuf->writePos = shmBuf->writePos + size;
946 auto last_seen = last_seen_id_.load();
947 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
949 TLOG(19) <<
"Write END";
955 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
956 std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
958 auto shmBuf = getBufferInfo_(buffer);
959 if (!shmBuf)
return false;
960 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
961 touchBuffer_(shmBuf);
962 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
964 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
965 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
966 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
969 auto pos = GetReadPos(buffer);
970 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
971 memcpy(data, pos, size);
972 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
973 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
976 shmBuf->readPos += size;
977 touchBuffer_(shmBuf);
985 std::ostringstream ostr;
986 ostr <<
"ShmStruct: " << std::endl
987 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
988 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
989 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
990 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
991 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
992 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
993 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
994 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
997 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
999 auto buf = getBufferInfo_(ii);
1002 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
1003 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1004 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
1005 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
1006 <<
"sem: " << FlagToString(buf->sem) << std::endl
1007 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1008 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1017 auto buf = getBufferInfo_(buffer);
1018 if (!buf)
return nullptr;
1019 return bufferStart_(buffer) + buf->readPos;
1023 auto buf = getBufferInfo_(buffer);
1024 if (!buf)
return nullptr;
1025 return bufferStart_(buffer) + buf->writePos;
1030 return bufferStart_(buffer);
1035 auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1036 for (
size_t ii = 0; ii < size(); ++ii)
1038 auto buf = getBufferInfo_(ii);
1039 output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1044 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1050 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1054 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) <<
")";
1057 if (buffer->sem != flags) Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1058 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) +
")");
1060 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1064 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1065 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1066 <<
"ID -1 is okay if expected flag is \"Full\" or \"Empty\".";
1072 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1074 if (!buffer || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
return;
1075 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " << (
void*)buffer <<
" with sequence_id " << buffer->sequence_id;
1081 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1084 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1085 auto bufs = GetBuffersOwnedByManager(
false);
1086 for (
auto buf : bufs)
1088 auto shmBuf = getBufferInfo_(buf);
1089 if (!shmBuf)
continue;
1090 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1092 shmBuf->sem = BufferSemaphoreFlags::Empty;
1094 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1096 shmBuf->sem = BufferSemaphoreFlags::Full;
1098 shmBuf->sem_id = -1;
1104 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1109 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1111 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1112 shmctl(shm_segment_id_, IPC_RMID, NULL);
1113 shm_segment_id_ = -1;
1119 if (category.size() > 0 && message.size() > 0)
1121 TLOG(TLVL_ERROR) << category <<
": " << message;
1125 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::vector< std::pair< int, BufferSemaphoreFlags > > GetBufferReport()
Get a report on the status of each buffer.
BufferSemaphoreFlags
The BufferSemaphoreFlags enumeration represents the different possible "states" of a given shared mem...
size_t BufferDataSize(int buffer)
Get the current size of the buffer's data.
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.