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)
115 it = instances.erase(it);
120 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager called";
122 TLOG(TLVL_DEBUG) <<
"~SharedMemoryManager done";
129 if (manager_id_ == 0)
return true;
133 size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
134 auto start_time = std::chrono::steady_clock::now();
136 size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size +
sizeof(ShmBuffer)) +
sizeof(ShmStruct);
140 if (requested_shm_parameters_.buffer_count > 0 && manager_id_ <= 0)
145 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
146 if (shm_segment_id_ == -1)
148 if (manager_id_ == 0)
150 TLOG(TLVL_DEBUG) <<
"Creating shared memory segment with key 0x" << std::hex << shm_key_ <<
" and size " << std::dec << shmSize;
151 shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
153 if (shm_segment_id_ == -1)
155 TLOG(TLVL_ERROR) <<
"Error creating shared memory segment with key 0x" << std::hex << shm_key_ <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")";
162 shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
166 TLOG(TLVL_DEBUG) <<
"shm_key == 0x" << std::hex << shm_key_ <<
", shm_segment_id == " << std::dec << shm_segment_id_;
168 if (shm_segment_id_ > -1)
171 <<
"Attached to shared memory segment with ID = " << shm_segment_id_
172 <<
" and size " << shmSize
174 shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
176 <<
"Attached to shared memory segment at address "
177 << std::hex << (
void*)shm_ptr_ << std::dec;
178 if (shm_ptr_ && shm_ptr_ != (
void*)-1)
180 if (manager_id_ == 0)
182 if (shm_ptr_->ready_magic == 0xCAFE1111)
184 TLOG(TLVL_WARNING) <<
"Owner encountered already-initialized Shared Memory! "
185 <<
"Once the system is shut down, you can use one of the following commands "
186 <<
"to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
187 <<
"' or 'ipcrm -m " << std::dec << shm_segment_id_ <<
"'.";
190 TLOG(TLVL_DEBUG) <<
"Owner initializing Shared Memory";
191 shm_ptr_->next_id = 1;
192 shm_ptr_->next_sequence_id = 0;
193 shm_ptr_->reader_pos = 0;
194 shm_ptr_->writer_pos = 0;
195 shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
196 shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
197 shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
198 shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
200 for (
int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
202 if (!getBufferInfo_(ii))
return false;
203 getBufferInfo_(ii)->writePos = 0;
204 getBufferInfo_(ii)->readPos = 0;
205 getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
206 getBufferInfo_(ii)->sem_id = -1;
210 shm_ptr_->ready_magic = 0xCAFE1111;
214 TLOG(TLVL_DEBUG) <<
"Waiting for owner to initalize Shared Memory";
215 while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
216 TLOG(TLVL_DEBUG) <<
"Getting ID from Shared Memory";
218 shm_ptr_->lowest_seq_id_read = 0;
219 TLOG(TLVL_DEBUG) <<
"Getting Shared Memory Size parameters";
222 buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
223 TLOG(TLVL_DEBUG) <<
"Initialization Complete: "
224 <<
"key: 0x" << std::hex << shm_key_
225 <<
", manager ID: " << std::dec << manager_id_
226 <<
", Buffer size: " << shm_ptr_->buffer_size
227 <<
", Buffer count: " << shm_ptr_->buffer_count;
232 TLOG(TLVL_ERROR) <<
"Failed to attach to shared memory segment "
239 TLOG(TLVL_ERROR) <<
"Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
240 <<
", errno=" << std::dec << errno <<
" (" << strerror(errno) <<
")"
242 <<
"if a stale shared memory segment needs to "
243 <<
"be cleaned up. (ipcs, ipcrm -m <segId>)";
253 TLOG(13) <<
"GetBufferForReading BEGIN";
255 std::unique_lock<std::mutex> lk(search_mutex_);
257 auto rp = shm_ptr_->reader_pos.load();
259 TLOG(13) <<
"GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
261 for (
int retry = 0; retry < 5; retry++)
266 ShmBuffer* buffer_ptr =
nullptr;
269 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
271 auto buffer = (ii + rp) % shm_ptr_->buffer_count;
273 TLOG(14) <<
"GetBufferForReading Checking if buffer " << buffer <<
" is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
276 auto buf = getBufferInfo_(buffer);
279 sem = buf->sem.load();
280 sem_id = buf->sem_id.load();
282 TLOG(14) <<
"GetBufferForReading: Buffer " << buffer <<
": sem=" << FlagToString(sem)
283 <<
" (expected " << FlagToString(BufferSemaphoreFlags::Full) <<
"), sem_id=" << sem_id <<
", seq_id=" << buf->sequence_id <<
" )";
284 if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
286 if (buf->sequence_id < seqID)
289 seqID = buf->sequence_id;
292 if (shm_ptr_->destructive_read_mode || seqID == last_seen_id_ + 1)
break;
299 sem = buffer_ptr->sem.load();
300 sem_id = buffer_ptr->sem_id.load();
303 if (!buffer_ptr || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
310 TLOG(13) <<
"GetBufferForReading Found buffer " << buffer_num;
311 touchBuffer_(buffer_ptr);
312 if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
313 if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
continue;
314 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
316 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was changing sem)";
319 buffer_ptr->readPos = 0;
320 touchBuffer_(buffer_ptr);
321 if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading,
false))
323 TLOG(13) <<
"GetBufferForReading: Failed to acquire buffer " << buffer_num <<
" (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
326 if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
328 shm_ptr_->lowest_seq_id_read = seqID;
330 last_seen_id_ = seqID;
331 if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
333 TLOG(13) <<
"GetBufferForReading returning " << buffer_num;
339 TLOG(13) <<
"GetBufferForReading returning -1 because no buffers are ready";
345 TLOG(14) <<
"GetBufferForWriting BEGIN, overwrite=" << (overwrite ?
"true" :
"false");
347 std::unique_lock<std::mutex> lk(search_mutex_);
349 auto wp = shm_ptr_->writer_pos.load();
351 TLOG(13) <<
"GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
354 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
356 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
360 auto buf = getBufferInfo_(buffer);
363 auto sem = buf->sem.load();
364 auto sem_id = buf->sem_id.load();
366 if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
369 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
370 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
371 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
372 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
373 buf->sequence_id = ++shm_ptr_->next_sequence_id;
375 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
377 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
385 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
387 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
391 auto buf = getBufferInfo_(buffer);
394 auto sem = buf->sem.load();
395 auto sem_id = buf->sem_id.load();
397 if (sem == BufferSemaphoreFlags::Full)
400 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
401 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
402 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
403 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
404 buf->sequence_id = ++shm_ptr_->next_sequence_id;
406 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
408 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
414 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
416 auto buffer = (ii + wp) % shm_ptr_->buffer_count;
420 auto buf = getBufferInfo_(buffer);
423 auto sem = buf->sem.load();
424 auto sem_id = buf->sem_id.load();
426 if (sem == BufferSemaphoreFlags::Reading)
429 if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
continue;
430 if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
continue;
431 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
432 shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
433 buf->sequence_id = ++shm_ptr_->next_sequence_id;
435 if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing,
false))
continue;
437 TLOG(14) <<
"GetBufferForWriting returning " << buffer;
442 TLOG(14) <<
"GetBufferForWriting Returning -1 because no buffers are ready";
448 if (!IsValid())
return 0;
449 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
450 std::unique_lock<std::mutex> lk(search_mutex_);
451 TLOG(23) <<
"ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
454 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
456 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Checking if buffer " << ii <<
" is stale.";
458 auto buf = getBufferInfo_(ii);
460 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 <<
" )";
461 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_))
463 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadReadyCount: Buffer " << ii <<
" is either unowned or owned by this manager, and is marked full.";
473 if (!IsValid())
return 0;
474 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadReadyCount BEGIN" << std::dec;
475 std::unique_lock<std::mutex> lk(search_mutex_);
477 TLOG(28) <<
"WriteReadyCount(" << overwrite <<
") lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
479 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
481 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Checking if buffer " << ii <<
" is stale.";
483 auto buf = getBufferInfo_(ii);
485 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
487 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or is available for overwrite.";
496 if (!IsValid())
return false;
497 TLOG(23) <<
"0x" << std::hex << shm_key_ <<
" ReadyForRead BEGIN" << std::dec;
498 std::unique_lock<std::mutex> lk(search_mutex_);
501 auto rp = shm_ptr_->reader_pos.load();
503 TLOG(23) <<
"ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
505 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
507 auto buffer = (rp + ii) % shm_ptr_->buffer_count;
508 TLOG(24) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Checking if buffer " << buffer <<
" is stale.";
510 auto buf = getBufferInfo_(buffer);
512 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 <<
" )"
513 <<
" seq_id=" << buf->sequence_id <<
" >? " << last_seen_id_;
514 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_))
516 TLOG(26) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForRead: Buffer " << buffer <<
" is either unowned or owned by this manager, and is marked full.";
526 if (!IsValid())
return false;
527 TLOG(28) <<
"0x" << std::hex << shm_key_ <<
" ReadyForWrite BEGIN" << std::dec;
529 std::unique_lock<std::mutex> lk(search_mutex_);
532 auto wp = shm_ptr_->writer_pos.load();
534 TLOG(28) <<
"ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count <<
" buffers";
536 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
538 auto buffer = (wp + ii) % shm_ptr_->buffer_count;
539 TLOG(29) <<
"0x" << std::hex << shm_key_ << std::dec <<
" ReadyForWrite: Checking if buffer " << buffer <<
" is stale.";
541 auto buf = getBufferInfo_(buffer);
543 if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
545 TLOG(29) <<
"0x" << std::hex << shm_key_
547 <<
" WriteReadyCount: Buffer " << ii <<
" is either empty or available for overwrite.";
556 std::deque<int> output;
557 if (!IsValid())
return output;
558 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager BEGIN. Locked? " << locked;
561 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtaining search_mutex";
562 std::unique_lock<std::mutex> lk(search_mutex_);
563 TLOG(TLVL_BUFLCK) <<
"GetBuffersOwnedByManager obtained search_mutex";
565 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
567 auto buf = getBufferInfo_(ii);
569 if (buf->sem_id == manager_id_)
571 output.push_back(ii);
577 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
579 auto buf = getBufferInfo_(ii);
581 if (buf->sem_id == manager_id_)
583 output.push_back(ii);
588 TLOG(TLVL_BUFFER) <<
"GetBuffersOwnedByManager: own " << output.size() <<
" / " << shm_ptr_->buffer_count <<
" buffers.";
594 TLOG(TLVL_BUFFER) <<
"BufferDataSize(" << buffer <<
") called.";
596 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
598 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtaining buffer_mutex for buffer " << buffer;
599 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
600 TLOG(TLVL_BUFLCK) <<
"BufferDataSize obtained buffer_mutex for buffer " << buffer;
603 auto buf = getBufferInfo_(buffer);
607 TLOG(TLVL_BUFFER) <<
"BufferDataSize: buffer " << buffer <<
", size=" << buf->writePos;
608 return buf->writePos;
613 TLOG(15) <<
"ResetReadPos(" << buffer <<
") called.";
615 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
617 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtaining buffer_mutex for buffer " << buffer;
618 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
619 TLOG(TLVL_BUFLCK) <<
"ResetReadPos obtained buffer_mutex for buffer " << buffer;
622 auto buf = getBufferInfo_(buffer);
623 if (!buf || buf->sem_id != manager_id_)
return;
627 TLOG(15) <<
"ResetReadPos(" << buffer <<
") ended.";
632 TLOG(16) <<
"ResetWritePos(" << buffer <<
") called.";
634 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
636 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtaining buffer_mutex for buffer " << buffer;
637 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
638 TLOG(TLVL_BUFLCK) <<
"ResetWritePos obtained buffer_mutex for buffer " << buffer;
641 auto buf = getBufferInfo_(buffer);
643 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
647 TLOG(16) <<
"ResetWritePos(" << buffer <<
") ended.";
652 TLOG(15) <<
"IncrementReadPos called: buffer= " << buffer <<
", bytes to read=" << read;
654 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
656 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
657 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
658 TLOG(TLVL_BUFLCK) <<
"IncrementReadPos obtained buffer_mutex for buffer " << buffer;
660 auto buf = getBufferInfo_(buffer);
661 if (!buf || buf->sem_id != manager_id_)
return;
663 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", readPos=" << buf->readPos <<
", bytes read=" << read;
664 buf->readPos = buf->readPos + read;
665 TLOG(15) <<
"IncrementReadPos: buffer= " << buffer <<
", New readPos is " << buf->readPos;
666 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) +
")");
671 TLOG(16) <<
"IncrementWritePos called: buffer= " << buffer <<
", bytes written=" << written;
673 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
675 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
676 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
677 TLOG(TLVL_BUFLCK) <<
"IncrementWritePos obtained buffer_mutex for buffer " << buffer;
679 auto buf = getBufferInfo_(buffer);
680 if (!buf)
return false;
681 checkBuffer_(buf, BufferSemaphoreFlags::Writing);
683 if (buf->writePos + written > shm_ptr_->buffer_size)
685 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 <<
")";
688 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", writePos=" << buf->writePos <<
", bytes written=" << written;
689 buf->writePos += written;
690 TLOG(16) <<
"IncrementWritePos: buffer= " << buffer <<
", New writePos is " << buf->writePos;
691 if (written == 0) Detach(
true,
"LogicError",
"Cannot increment Write pos by 0!");
698 TLOG(17) <<
"MoreDataInBuffer(" << buffer <<
") called.";
700 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
702 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
703 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
704 TLOG(TLVL_BUFLCK) <<
"MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
706 auto buf = getBufferInfo_(buffer);
707 if (!buf)
return false;
708 TLOG(17) <<
"MoreDataInBuffer: buffer= " << buffer <<
", readPos=" << std::to_string(buf->readPos) <<
", writePos=" << buf->writePos;
709 return buf->readPos < buf->writePos;
714 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
716 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtaining buffer_mutex for buffer " << buffer;
717 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
718 TLOG(TLVL_BUFLCK) <<
"CheckBuffer obtained buffer_mutex for buffer " << buffer;
720 return checkBuffer_(getBufferInfo_(buffer), flags,
false);
725 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
727 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
728 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
729 TLOG(TLVL_BUFLCK) <<
"MarkBufferFull obtained buffer_mutex for buffer " << buffer;
732 auto shmBuf = getBufferInfo_(buffer);
734 touchBuffer_(shmBuf);
735 if (shmBuf->sem_id == manager_id_)
737 if (shmBuf->sem != BufferSemaphoreFlags::Full)
738 shmBuf->sem = BufferSemaphoreFlags::Full;
740 shmBuf->sem_id = destination;
746 TLOG(18) <<
"MarkBufferEmpty BEGIN, buffer=" << buffer <<
", force=" << force <<
", manager_id_=" << manager_id_;
747 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
748 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
750 auto shmBuf = getBufferInfo_(buffer);
754 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
true);
756 touchBuffer_(shmBuf);
759 shmBuf->sem = BufferSemaphoreFlags::Full;
761 if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
763 TLOG(18) <<
"MarkBufferEmpty Resetting buffer " << buffer <<
" to Empty state";
764 shmBuf->writePos = 0;
765 shmBuf->sem = BufferSemaphoreFlags::Empty;
766 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
768 TLOG(18) <<
"MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos <<
" to " << (buffer + 1) % shm_ptr_->buffer_count;
769 shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
773 TLOG(18) <<
"MarkBufferEmpty END, buffer=" << buffer <<
", force=" << force;
779 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
781 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
782 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
783 TLOG(TLVL_BUFLCK) <<
"ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
786 auto shmBuf = getBufferInfo_(buffer);
787 if (!shmBuf)
return false;
799 if (delta > 0xFFFFFFFF)
801 TLOG(TLVL_TRACE) <<
"Buffer has touch time in the future, setting it to current time and ignoring...";
805 if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
return false;
806 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;
808 if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
813 if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && (shmBuf->sequence_id < last_seen_id_ || manager_id_ == 0))
815 TLOG(TLVL_DEBUG) <<
"Resetting old broadcast mode buffer " << buffer <<
" (seqid=" << shmBuf->sequence_id <<
"). State: Full-->Empty";
816 shmBuf->writePos = 0;
817 shmBuf->sem = BufferSemaphoreFlags::Empty;
819 if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer)) shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
823 if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
827 if (delta <= shm_ptr_->buffer_timeout_us)
return false;
828 TLOG(TLVL_WARNING) <<
"Stale Read buffer " << buffer <<
" at " << (
void*)shmBuf
829 <<
" ( " << delta <<
" / " << shm_ptr_->buffer_timeout_us <<
" us ) detected! (seqid="
830 << shmBuf->sequence_id <<
") Resetting... Reading-->Full";
832 shmBuf->sem = BufferSemaphoreFlags::Full;
841 if (!IsValid())
return true;
843 struct shmid_ds info;
844 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
847 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
851 if (info.shm_perm.mode & SHM_DEST)
853 TLOG(TLVL_INFO) <<
"Shared Memory marked for destruction. Probably an end-of-data condition!";
862 if (!IsValid())
return 0;
864 struct shmid_ds info;
865 auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
868 TLOG(TLVL_TRACE) <<
"Error accessing Shared Memory info: " << errno <<
" (" << strerror(errno) <<
").";
872 return info.shm_nattch;
877 TLOG(19) <<
"Write BEGIN";
878 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
879 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
881 auto shmBuf = getBufferInfo_(buffer);
882 if (!shmBuf)
return -1;
883 checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
884 touchBuffer_(shmBuf);
885 TLOG(19) <<
"Buffer Write Pos is " << shmBuf->writePos <<
", write size is " << size;
886 if (shmBuf->writePos + size > shm_ptr_->buffer_size)
888 TLOG(TLVL_ERROR) <<
"Attempted to write more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
889 <<
",writePos=" << shmBuf->writePos <<
",writeSize=" << size;
890 Detach(
true,
"SharedMemoryWrite",
"Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
893 auto pos = GetWritePos(buffer);
894 memcpy(pos, data, size);
895 touchBuffer_(shmBuf);
896 shmBuf->writePos = shmBuf->writePos + size;
898 auto last_seen = last_seen_id_.load();
899 while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
901 TLOG(19) <<
"Write END";
907 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
908 std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
910 auto shmBuf = getBufferInfo_(buffer);
911 if (!shmBuf)
return false;
912 checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
913 touchBuffer_(shmBuf);
914 if (shmBuf->readPos + size > shm_ptr_->buffer_size)
916 TLOG(TLVL_ERROR) <<
"Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
917 <<
",readPos=" << shmBuf->readPos <<
",readSize=" << size;
918 Detach(
true,
"SharedMemoryRead",
"Attempted to read more data than exists in Shared Memory!");
921 auto pos = GetReadPos(buffer);
922 TLOG(TLVL_TRACE) <<
"Before memcpy in Read(), size is " << size;
923 memcpy(data, pos, size);
924 TLOG(TLVL_TRACE) <<
"After memcpy in Read()";
925 auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading,
false);
928 shmBuf->readPos += size;
929 touchBuffer_(shmBuf);
937 std::ostringstream ostr;
938 ostr <<
"ShmStruct: " << std::endl
939 <<
"Reader Position: " << shm_ptr_->reader_pos << std::endl
940 <<
"Writer Position: " << shm_ptr_->writer_pos << std::endl
941 <<
"Next ID Number: " << shm_ptr_->next_id << std::endl
942 <<
"Buffer Count: " << shm_ptr_->buffer_count << std::endl
943 <<
"Buffer Size: " << std::to_string(shm_ptr_->buffer_size) <<
" bytes" << std::endl
944 <<
"Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
945 <<
"Rank of Writer: " << shm_ptr_->rank << std::endl
946 <<
"Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
949 for (
auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
951 auto buf = getBufferInfo_(ii);
954 ostr <<
"ShmBuffer " << std::dec << ii << std::endl
955 <<
"sequenceID: " << std::to_string(buf->sequence_id) << std::endl
956 <<
"writePos: " << std::to_string(buf->writePos) << std::endl
957 <<
"readPos: " << std::to_string(buf->readPos) << std::endl
958 <<
"sem: " << FlagToString(buf->sem) << std::endl
959 <<
"Owner: " << std::to_string(buf->sem_id.load()) << std::endl
960 <<
"Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
969 auto buf = getBufferInfo_(buffer);
970 if (!buf)
return nullptr;
971 return bufferStart_(buffer) + buf->readPos;
975 auto buf = getBufferInfo_(buffer);
976 if (!buf)
return nullptr;
977 return bufferStart_(buffer) + buf->writePos;
982 return bufferStart_(buffer);
987 auto output = std::list<std::pair<int, BufferSemaphoreFlags>>();
988 for (
size_t ii = 0; ii < size(); ++ii)
990 auto buf = getBufferInfo_(ii);
991 output.emplace_back(std::make_pair(buf->sem_id.load(), buf->sem.load()));
996 uint8_t* artdaq::SharedMemoryManager::dataStart_()
const
998 if (shm_ptr_ ==
nullptr)
return nullptr;
999 return reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + shm_ptr_->buffer_count *
sizeof(ShmBuffer);
1002 uint8_t* artdaq::SharedMemoryManager::bufferStart_(
int buffer)
1004 if (shm_ptr_ ==
nullptr)
return nullptr;
1005 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1006 return dataStart_() + buffer * shm_ptr_->buffer_size;
1009 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(
int buffer)
1011 if (shm_ptr_ ==
nullptr)
return nullptr;
1012 if (buffer >= shm_ptr_->buffer_count) Detach(
true,
"ArgumentOutOfRange",
"The specified buffer does not exist!");
1013 return reinterpret_cast<ShmBuffer*
>(
reinterpret_cast<uint8_t*
>(shm_ptr_ + 1) + buffer *
sizeof(ShmBuffer));
1016 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags,
bool exceptions)
1022 Detach(
true,
"BufferNotThereException",
"Request to check buffer that does not exist!");
1026 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) <<
")";
1029 if (buffer->sem != flags) Detach(
true,
"StateAccessViolation",
"Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) +
", actual " + FlagToString(buffer->sem) +
")");
1030 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) +
")");
1032 bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1036 TLOG(TLVL_WARNING) <<
"CheckBuffer detected issue with buffer " << buffer->sequence_id <<
"!"
1037 <<
" ID: " << buffer->sem_id <<
" (Expected " << manager_id_ <<
"), Flag: " << FlagToString(buffer->sem) <<
" (Expected " << FlagToString(flags) <<
"). "
1038 <<
"ID -1 is okay if expected flag is \"Full\" or \"Empty\".";
1044 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1046 if (!buffer || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
return;
1047 TLOG(TLVL_TRACE) <<
"touchBuffer_: Touching buffer at " << (
void*)buffer <<
" with sequence_id " << buffer->sequence_id;
1053 TLOG(TLVL_DETACH) <<
"Detach BEGIN: throwException: " << std::boolalpha << throwException <<
", force: " << force;
1056 TLOG(TLVL_DETACH) <<
"Detach: Resetting owned buffers";
1057 auto bufs = GetBuffersOwnedByManager(
false);
1058 for (
auto buf : bufs)
1060 auto shmBuf = getBufferInfo_(buf);
1061 if (!shmBuf)
continue;
1062 if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1064 shmBuf->sem = BufferSemaphoreFlags::Empty;
1066 else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1068 shmBuf->sem = BufferSemaphoreFlags::Full;
1070 shmBuf->sem_id = -1;
1076 TLOG(TLVL_DETACH) <<
"Detach: Detaching shared memory";
1081 if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1083 TLOG(TLVL_DETACH) <<
"Detach: Marking Shared memory for removal";
1084 shmctl(shm_segment_id_, IPC_RMID, NULL);
1085 shm_segment_id_ = -1;
1091 if (category.size() > 0 && message.size() > 0)
1093 TLOG(TLVL_ERROR) << category <<
": " << message;
1097 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.