artdaq_core  3.09.13
SharedMemoryManager.cc
1 #define TRACE_NAME "SharedMemoryManager"
2 #include <sys/ipc.h>
3 #include <sys/shm.h>
4 #include <cstring>
5 #include <list>
6 #include <unordered_map>
7 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!?
8 #define SHM_DEST 01000
9 #endif
10 #include <csignal>
11 #include "TRACE/tracemf.h"
12 #include "artdaq-core/Core/SharedMemoryManager.hh"
13 #include "artdaq-core/Utilities/TraceLock.hh"
14 #include "cetlib_except/exception.h"
15 
16 #define TLVL_DETACH 34
17 #define TLVL_DESTRUCTOR 35
18 #define TLVL_ATTACH 36
19 #define TLVL_GETBUFFER 37
20 #define TLVL_BUFFER 40
21 #define TLVL_BUFLCK 41
22 #define TLVL_READREADY 42
23 #define TLVL_WRITEREADY 46
24 #define TLVL_POS 48
25 #define TLVL_RESET 51
26 #define TLVL_BUFINFO 52
27 #define TLVL_WRITE 53
28 #define TLVL_READ 54
29 #define TLVL_CHKBUFFER 55
30 
31 static std::list<artdaq::SharedMemoryManager const*> instances = std::list<artdaq::SharedMemoryManager const*>();
32 
33 static std::unordered_map<int, struct sigaction> old_actions = std::unordered_map<int, struct sigaction>();
34 static bool sighandler_init = false;
35 static std::mutex sighandler_mutex;
36 
37 static void signal_handler(int signum)
38 {
39  // Messagefacility may already be gone at this point, TRACE ONLY!
40 #if TRACE_REVNUM < 1459
41  TRACE_STREAMER(TLVL_ERROR, &("SharedMemoryManager")[0], 0, 0, 0)
42 #else
43  TRACE_STREAMER(TLVL_ERROR, TLOG2("SharedMemoryManager", 0), 0)
44 #endif
45  << "A signal of type " << signum << " was caught by SharedMemoryManager. Detaching all Shared Memory segments, then proceeding with default handlers!";
46  for (auto ii : instances)
47  {
48  if (ii != nullptr)
49  {
50  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
51  const_cast<artdaq::SharedMemoryManager*>(ii)->Detach(false, "", "", false /* don't force destruct segment, allows reconnection (applicable for
52  restart and/or multiple art processes (i.e. dispatcher)) */
53  );
54  }
55  ii = nullptr;
56  }
57 
58  sigset_t set;
59  pthread_sigmask(SIG_UNBLOCK, nullptr, &set);
60  pthread_sigmask(SIG_UNBLOCK, &set, nullptr);
61 
62 #if TRACE_REVNUM < 1459
63  TRACE_STREAMER(TLVL_ERROR, &("SharedMemoryManager")[0], 0, 0, 0)
64 #else
65  TRACE_STREAMER(TLVL_ERROR, TLOG2("SharedMemoryManager", 0), 0)
66 #endif
67  << "Restoring default signal handler";
68  sigaction(signum, &old_actions[signum], nullptr);
69 }
70 
71 artdaq::SharedMemoryManager::SharedMemoryManager(uint32_t shm_key, size_t buffer_count, size_t buffer_size, uint64_t buffer_timeout_us, bool destructive_read_mode)
72  : shm_segment_id_(-1)
73  , shm_ptr_(nullptr)
74  , shm_key_(shm_key)
75  , manager_id_(-1)
76  , last_seen_id_(0)
77 {
78  requested_shm_parameters_.buffer_count = buffer_count;
79  requested_shm_parameters_.buffer_size = buffer_size;
80  requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
81  requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
82 
83  instances.push_back(this);
84  Attach();
85 
86  std::lock_guard<std::mutex> lk(sighandler_mutex);
87 
88  if (!sighandler_init) //&& manager_id_ == 0) // ELF 3/22/18: Taking out manager_id_==0 requirement as I think kill(getpid()) is enough protection
89  {
90  sighandler_init = true;
91  std::vector<int> signals = {SIGINT, SIGILL, SIGABRT, SIGFPE, SIGSEGV, SIGPIPE, SIGALRM, SIGTERM, SIGUSR2, SIGHUP}; // SIGQUIT is used by art in normal operation
92  for (auto signal : signals)
93  {
94  struct sigaction old_action;
95  sigaction(signal, nullptr, &old_action);
96 
97  // If the old handler wasn't SIG_IGN (it's a handler that just
98  // "ignore" the signal)
99  if (old_action.sa_handler != SIG_IGN) // NOLINT(cppcoreguidelines-pro-type-cstyle-cast)
100  {
101  struct sigaction action;
102  action.sa_handler = signal_handler;
103  sigemptyset(&action.sa_mask);
104  for (auto sigblk : signals)
105  {
106  sigaddset(&action.sa_mask, sigblk);
107  }
108  action.sa_flags = 0;
109 
110  // Replace the signal handler of SIGINT with the one described by new_action
111  sigaction(signal, &action, nullptr);
112  }
113  old_actions[signal] = old_action;
114  }
115  }
116 }
117 
118 // The clang-tidy warning comes from Detach, which can throw an exception if called with first parameter = true (defaults to false)
119 artdaq::SharedMemoryManager::~SharedMemoryManager() noexcept // NOLINT(bugprone-exception-escape)
120 {
121  TLOG(TLVL_DESTRUCTOR) << "~SharedMemoryManager called";
122  {
123  static std::mutex destructor_mutex;
124  std::lock_guard<std::mutex> lk(destructor_mutex);
125  for (auto it = instances.begin(); it != instances.end(); ++it)
126  {
127  if (*it == this)
128  {
129  it = instances.erase(it);
130  break;
131  }
132  }
133  }
134  Detach();
135  {
136  std::lock_guard<std::mutex> lk(sighandler_mutex);
137 
138  // Restore signal handlers
139  if (sighandler_init && instances.empty())
140  {
141  sighandler_init = false;
142  for (auto signal : old_actions)
143  {
144  sigaction(signal.first, &signal.second, nullptr);
145  }
146  old_actions.clear();
147  }
148  }
149  TLOG(TLVL_DESTRUCTOR) << "~SharedMemoryManager done";
150 }
151 
152 bool artdaq::SharedMemoryManager::Attach(size_t timeout_usec)
153 {
154  if (IsValid())
155  {
156  if (manager_id_ == 0)
157  {
158  return true;
159  }
160  Detach();
161  }
162 
163  size_t timeout_us = timeout_usec > 0 ? timeout_usec : 1000000;
164  auto start_time = std::chrono::steady_clock::now();
165  last_seen_id_ = 0;
166  size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size + sizeof(ShmBuffer)) + sizeof(ShmStruct);
167 
168  // 19-Feb-2019, KAB: separating out the determination of whether a given process owns the shared
169  // memory (indicated by manager_id_ == 0) and whether or not the shared memory already exists.
170  if (requested_shm_parameters_.buffer_count > 0 && requested_shm_parameters_.buffer_size > 0 && manager_id_ <= 0)
171  {
172  manager_id_ = 0;
173  }
174 
175  shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
176  if (shm_segment_id_ == -1)
177  {
178  if (manager_id_ == 0)
179  {
180  TLOG(TLVL_ATTACH) << "Creating shared memory segment with key 0x" << std::hex << shm_key_ << " and size " << std::dec << shmSize;
181  shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
182 
183  if (shm_segment_id_ == -1)
184  {
185  TLOG(TLVL_ERROR) << "Error creating shared memory segment with key 0x" << std::hex << shm_key_ << ", errno=" << std::dec << errno << " (" << strerror(errno) << ")";
186  }
187  }
188  else
189  {
190  while (shm_segment_id_ == -1 && TimeUtils::GetElapsedTimeMicroseconds(start_time) < timeout_us)
191  {
192  shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
193  }
194  }
195  }
196  TLOG(TLVL_ATTACH) << "shm_key == 0x" << std::hex << shm_key_ << ", shm_segment_id == " << std::dec << shm_segment_id_;
197 
198  if (shm_segment_id_ > -1)
199  {
200  TLOG(TLVL_ATTACH)
201  << "Attached to shared memory segment with ID = " << shm_segment_id_
202  << " and size " << shmSize
203  << " bytes";
204  shm_ptr_ = static_cast<ShmStruct*>(shmat(shm_segment_id_, nullptr, 0));
205  TLOG(TLVL_ATTACH)
206  << "Attached to shared memory segment at address "
207  << std::hex << static_cast<void*>(shm_ptr_) << std::dec;
208  if ((shm_ptr_ != nullptr) && shm_ptr_ != reinterpret_cast<void*>(-1)) // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast)
209  {
210  if (manager_id_ == 0)
211  {
212  if (shm_ptr_->ready_magic == 0xCAFE1111)
213  {
214  TLOG(TLVL_WARNING) << "Owner encountered already-initialized Shared Memory! "
215  << "Once the system is shut down, you can use one of the following commands "
216  << "to clean up this shared memory: 'ipcrm -M 0x" << std::hex << shm_key_
217  << "' or 'ipcrm -m " << std::dec << shm_segment_id_ << "'.";
218  // exit(-2);
219  }
220  TLOG(TLVL_ATTACH) << "Owner initializing Shared Memory";
221  shm_ptr_->next_id = 1;
222  shm_ptr_->next_sequence_id = 0;
223  shm_ptr_->reader_pos = 0;
224  shm_ptr_->writer_pos = 0;
225  shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
226  shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
227  shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
228  shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
229 
230  buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
231  for (int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
232  {
233  buffer_ptrs_[ii] = reinterpret_cast<ShmBuffer*>(reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + ii * sizeof(ShmBuffer)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
234  if (getBufferInfo_(ii) == nullptr)
235  {
236  return false;
237  }
238  getBufferInfo_(ii)->writePos = 0;
239  getBufferInfo_(ii)->readPos = 0;
240  getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
241  getBufferInfo_(ii)->sem_id = -1;
242  getBufferInfo_(ii)->last_touch_time = TimeUtils::gettimeofday_us();
243  }
244 
245  shm_ptr_->ready_magic = 0xCAFE1111;
246  }
247  else
248  {
249  TLOG(TLVL_ATTACH) << "Waiting for owner to initalize Shared Memory";
250  while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
251  TLOG(TLVL_ATTACH) << "Getting ID from Shared Memory";
252  GetNewId();
253  shm_ptr_->lowest_seq_id_read = 0;
254  TLOG(TLVL_ATTACH) << "Getting Shared Memory Size parameters";
255 
256  requested_shm_parameters_.buffer_count = shm_ptr_->buffer_count;
257  buffer_ptrs_ = std::vector<ShmBuffer*>(shm_ptr_->buffer_count);
258  for (int ii = 0; ii < shm_ptr_->buffer_count; ++ii)
259  {
260  buffer_ptrs_[ii] = reinterpret_cast<ShmBuffer*>(reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + ii * sizeof(ShmBuffer)); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
261  }
262  }
263 
264  // last_seen_id_ = shm_ptr_->next_sequence_id;
265  buffer_mutexes_ = std::vector<std::mutex>(shm_ptr_->buffer_count);
266 
267  TLOG(TLVL_ATTACH) << "Initialization Complete: "
268  << "key: 0x" << std::hex << shm_key_
269  << ", manager ID: " << std::dec << manager_id_
270  << ", Buffer size: " << shm_ptr_->buffer_size
271  << ", Buffer count: " << shm_ptr_->buffer_count;
272  return true;
273  }
274 
275  TLOG(TLVL_ERROR) << "Failed to attach to shared memory segment "
276  << shm_segment_id_;
277  return false;
278  }
279 
280  TLOG(TLVL_ERROR) << "Failed to connect to shared memory segment with key 0x" << std::hex << shm_key_
281  << ", errno=" << std::dec << errno << " (" << strerror(errno) << ")"
282  << ". Please check "
283  << "if a stale shared memory segment needs to "
284  << "be cleaned up. (ipcs, ipcrm -m <segId>)";
285  return false;
286 }
287 
289 {
290  TLOG(TLVL_GETBUFFER) << "GetBufferForReading BEGIN";
291 
292  if (!registered_reader_)
293  {
294  shm_ptr_->reader_count++;
295  registered_reader_ = true;
296  }
297 
298  std::lock_guard<std::mutex> lk(search_mutex_);
299  // TraceLock lk(search_mutex_, 11, "GetBufferForReadingSearch");
300  auto rp = shm_ptr_->reader_pos.load();
301 
302  TLOG(TLVL_GETBUFFER) << "GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
303 
304  for (int retry = 0; retry < 5; retry++)
305  {
307  int16_t sem_id = -2;
308  int buffer_num = -1;
309  ShmBuffer* buffer_ptr = nullptr;
310  uint64_t seqID = -1;
311 
312  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
313  {
314  auto buffer = (ii + rp) % shm_ptr_->buffer_count;
315 
316  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForReading Checking if buffer " << buffer << " is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
317  ResetBuffer(buffer);
318 
319  auto buf = getBufferInfo_(buffer);
320  if (buf == nullptr)
321  {
322  continue;
323  }
324 
325  sem = buf->sem.load();
326  sem_id = buf->sem_id.load();
327 
328  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForReading: Buffer " << buffer << ": sem=" << FlagToString(sem)
329  << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << sem_id << ", seq_id=" << buf->sequence_id << " )";
330  if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
331  {
332  if (buf->sequence_id < seqID)
333  {
334  buffer_ptr = buf;
335  seqID = buf->sequence_id;
336  buffer_num = buffer;
337  touchBuffer_(buf);
338  if (seqID == last_seen_id_ + shm_ptr_->reader_count)
339  {
340  break;
341  }
342  }
343  }
344  }
345 
346  if (buffer_ptr != nullptr)
347  {
348  sem = buffer_ptr->sem.load();
349  sem_id = buffer_ptr->sem_id.load();
350  seqID = buffer_ptr->sequence_id.load();
351  }
352 
353  TLOG(TLVL_GETBUFFER + 2) << "GetBufferForReading: Mode: " << std::boolalpha << shm_ptr_->destructive_read_mode << ", seqID: " << seqID << ", last_seen_id_: " << last_seen_id_ << ", reader_count: " << shm_ptr_->reader_count;
354 
355  if (shm_ptr_->destructive_read_mode && last_seen_id_ > 0 // Round-robin enabled
356  && shm_ptr_->reader_count > 1 // Don't skip buffers if there is only one reader
357  && seqID != last_seen_id_ + shm_ptr_->reader_count // SeqID is not "next" SeqID
358  && seqID > last_seen_id_ - shm_ptr_->reader_count // SeqID is not "left behind" (from at least previous RR)
359  && seqID < last_seen_id_ + 2 * shm_ptr_->reader_count) // Reader is not "left behind" (SeqID from next RR)
360  {
361  TLOG(TLVL_GETBUFFER + 2) << "GetBufferForReading: Skipping due to seqID check";
362  continue;
363  }
364  TLOG(TLVL_GETBUFFER + 2) << "GetBufferForReading: After seqID check";
365 
366  if ((buffer_ptr == nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
367  {
368  continue;
369  }
370 
371  if (buffer_num >= 0)
372  {
373  TLOG(TLVL_GETBUFFER) << "GetBufferForReading Found buffer " << buffer_num;
374  touchBuffer_(buffer_ptr);
375  if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
376  {
377  continue;
378  }
379  if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
380  {
381  continue;
382  }
383  if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading, false))
384  {
385  TLOG(TLVL_GETBUFFER) << "GetBufferForReading: Failed to acquire buffer " << buffer_num << " (someone else changed manager ID while I was changing sem)";
386  continue;
387  }
388  buffer_ptr->readPos = 0;
389  touchBuffer_(buffer_ptr);
390  if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading, false))
391  {
392  TLOG(TLVL_GETBUFFER) << "GetBufferForReading: Failed to acquire buffer " << buffer_num << " (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
393  continue;
394  }
395  if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
396  {
397  shm_ptr_->lowest_seq_id_read = seqID;
398  }
399  last_seen_id_ = seqID;
400  if (shm_ptr_->destructive_read_mode)
401  {
402  shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
403  }
404 
405  TLOG(TLVL_GETBUFFER) << "GetBufferForReading returning " << buffer_num;
406  return buffer_num;
407  }
408  retry = 5;
409  }
410 
411  TLOG(TLVL_GETBUFFER) << "GetBufferForReading returning -1 because no buffers are ready";
412  return -1;
413 }
414 
416 {
417  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting BEGIN, overwrite=" << (overwrite ? "true" : "false");
418 
419  if (!registered_writer_)
420  {
421  shm_ptr_->writer_count++;
422  registered_writer_ = true;
423  }
424 
425  std::lock_guard<std::mutex> lk(search_mutex_);
426  // TraceLock lk(search_mutex_, 12, "GetBufferForWritingSearch");
427  auto wp = shm_ptr_->writer_pos.load();
428 
429  TLOG(TLVL_GETBUFFER) << "GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
430 
431  // First, only look for "Empty" buffers
432  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
433  {
434  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
435 
436  ResetBuffer(buffer);
437 
438  auto buf = getBufferInfo_(buffer);
439  if (buf == nullptr)
440  {
441  continue;
442  }
443 
444  auto sem = buf->sem.load();
445  auto sem_id = buf->sem_id.load();
446 
447  if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
448  {
449  touchBuffer_(buf);
450  if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
451  {
452  continue;
453  }
454  if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
455  {
456  continue;
457  }
458  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
459  {
460  continue;
461  }
462  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
463  buf->sequence_id = ++shm_ptr_->next_sequence_id;
464  buf->writePos = 0;
465  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
466  {
467  continue;
468  }
469  touchBuffer_(buf);
470  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting returning empty buffer " << buffer;
471  return buffer;
472  }
473  }
474 
475  if (overwrite)
476  {
477  // Then, look for "Full" buffers
478  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
479  {
480  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
481 
482  ResetBuffer(buffer);
483 
484  auto buf = getBufferInfo_(buffer);
485  if (buf == nullptr)
486  {
487  continue;
488  }
489 
490  auto sem = buf->sem.load();
491  auto sem_id = buf->sem_id.load();
492 
493  if (sem == BufferSemaphoreFlags::Full)
494  {
495  touchBuffer_(buf);
496  if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
497  {
498  continue;
499  }
500  if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
501  {
502  continue;
503  }
504  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
505  {
506  continue;
507  }
508  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
509  buf->sequence_id = ++shm_ptr_->next_sequence_id;
510  buf->writePos = 0;
511  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
512  {
513  continue;
514  }
515  touchBuffer_(buf);
516  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting returning full buffer (overwrite mode) " << buffer;
517  return buffer;
518  }
519  }
520 
521  // Finally, if we still haven't found a buffer, we have to clobber a reader...
522  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
523  {
524  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
525 
526  ResetBuffer(buffer);
527 
528  auto buf = getBufferInfo_(buffer);
529  if (buf == nullptr)
530  {
531  continue;
532  }
533 
534  auto sem = buf->sem.load();
535  auto sem_id = buf->sem_id.load();
536 
537  if (sem == BufferSemaphoreFlags::Reading)
538  {
539  touchBuffer_(buf);
540  if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
541  {
542  continue;
543  }
544  if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
545  {
546  continue;
547  }
548  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
549  {
550  continue;
551  }
552  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
553  buf->sequence_id = ++shm_ptr_->next_sequence_id;
554  buf->writePos = 0;
555  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
556  {
557  continue;
558  }
559  touchBuffer_(buf);
560  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting clobbering reader on buffer " << buffer << " (overwrite mode)";
561  return buffer;
562  }
563  }
564  }
565  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting Returning -1 because no buffers are ready";
566  return -1;
567 }
568 
570 {
571  if (!IsValid())
572  {
573  return 0;
574  }
575  TLOG(TLVL_READREADY) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN" << std::dec;
576  std::unique_lock<std::mutex> lk(search_mutex_);
577  TLOG(TLVL_READREADY) << "ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
578  // TraceLock lk(search_mutex_, 14, "ReadReadyCountSearch");
579  size_t count = 0;
580  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
581  {
582 #ifndef __OPTIMIZE__
583  TLOG(TLVL_READREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Checking if buffer " << ii << " is stale.";
584 #endif
585  ResetBuffer(ii);
586  auto buf = getBufferInfo_(ii);
587  if (buf == nullptr)
588  {
589  continue;
590  }
591 
592 #ifndef __OPTIMIZE__
593  TLOG(TLVL_READREADY + 2) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Buffer " << ii << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " )";
594 #endif
595  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_))
596  {
597 #ifndef __OPTIMIZE__
598  TLOG(TLVL_READREADY + 3) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Buffer " << ii << " is either unowned or owned by this manager, and is marked full.";
599 #endif
600  touchBuffer_(buf);
601  ++count;
602  }
603  }
604  return count;
605 }
606 
608 {
609  if (!IsValid())
610  {
611  return 0;
612  }
613  TLOG(TLVL_WRITEREADY) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN" << std::dec;
614  std::unique_lock<std::mutex> lk(search_mutex_);
615  // TraceLock lk(search_mutex_, 15, "WriteReadyCountSearch");
616  TLOG(TLVL_WRITEREADY) << "WriteReadyCount(" << overwrite << ") lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
617  size_t count = 0;
618  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
619  {
620  // ELF, 3/19/2019: This TRACE call is a major performance hit with many buffers
621 #ifndef __OPTIMIZE__
622  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " WriteReadyCount: Checking if buffer " << ii << " is stale.";
623 #endif
624  ResetBuffer(ii);
625  auto buf = getBufferInfo_(ii);
626  if (buf == nullptr)
627  {
628  continue;
629  }
630  if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
631  {
632 #ifndef __OPTIMIZE__
633  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " WriteReadyCount: Buffer " << ii << " is either empty or is available for overwrite.";
634 #endif
635  ++count;
636  }
637  }
638  return count;
639 }
640 
642 {
643  if (!IsValid())
644  {
645  return false;
646  }
647  TLOG(TLVL_READREADY) << "0x" << std::hex << shm_key_ << " ReadyForRead BEGIN" << std::dec;
648  std::unique_lock<std::mutex> lk(search_mutex_);
649  // TraceLock lk(search_mutex_, 14, "ReadyForReadSearch");
650 
651  auto rp = shm_ptr_->reader_pos.load();
652 
653  TLOG(TLVL_READREADY) << "ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
654 
655  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
656  {
657  auto buffer = (rp + ii) % shm_ptr_->buffer_count;
658 
659 #ifndef __OPTIMIZE__
660  TLOG(TLVL_READREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Checking if buffer " << buffer << " is stale.";
661 #endif
662  ResetBuffer(buffer);
663  auto buf = getBufferInfo_(buffer);
664  if (buf == nullptr)
665  {
666  continue;
667  }
668 
669 #ifndef __OPTIMIZE__
670  TLOG(TLVL_READREADY + 2) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Buffer " << buffer << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " )"
671  << " seq_id=" << buf->sequence_id << " >? " << last_seen_id_;
672 #endif
673 
674  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_))
675  {
676  TLOG(TLVL_READREADY + 3) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Buffer " << buffer << " is either unowned or owned by this manager, and is marked full.";
677  touchBuffer_(buf);
678  return true;
679  }
680  }
681  return false;
682 }
683 
685 {
686  if (!IsValid())
687  {
688  return false;
689  }
690  TLOG(TLVL_WRITEREADY) << "0x" << std::hex << shm_key_ << " ReadyForWrite BEGIN" << std::dec;
691 
692  std::lock_guard<std::mutex> lk(search_mutex_);
693  // TraceLock lk(search_mutex_, 15, "ReadyForWriteSearch");
694 
695  auto wp = shm_ptr_->writer_pos.load();
696 
697  TLOG(TLVL_WRITEREADY) << "ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
698 
699  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
700  {
701  auto buffer = (wp + ii) % shm_ptr_->buffer_count;
702  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForWrite: Checking if buffer " << buffer << " is stale.";
703  ResetBuffer(buffer);
704  auto buf = getBufferInfo_(buffer);
705  if (buf == nullptr)
706  {
707  continue;
708  }
709  if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
710  {
711  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_
712  << std::dec
713  << " WriteReadyCount: Buffer " << ii << " is either empty or available for overwrite.";
714  return true;
715  }
716  }
717  return false;
718 }
719 
721 {
722  std::deque<int> output;
723  size_t buffer_count = size();
724  if (!IsValid() || buffer_count == 0)
725  {
726  return output;
727  }
728  TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager BEGIN. Locked? " << locked;
729  if (locked)
730  {
731  TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtaining search_mutex";
732  std::lock_guard<std::mutex> lk(search_mutex_);
733  TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtained search_mutex";
734  // TraceLock lk(search_mutex_, 16, "GetOwnedSearch");
735  for (size_t ii = 0; ii < buffer_count; ++ii)
736  {
737  auto buf = getBufferInfo_(ii);
738  if (buf == nullptr)
739  {
740  continue;
741  }
742  if (buf->sem_id == manager_id_)
743  {
744  output.push_back(ii);
745  }
746  }
747  }
748  else
749  {
750  for (size_t ii = 0; ii < buffer_count; ++ii)
751  {
752  auto buf = getBufferInfo_(ii);
753  if (buf == nullptr)
754  {
755  continue;
756  }
757  if (buf->sem_id == manager_id_)
758  {
759  output.push_back(ii);
760  }
761  }
762  }
763 
764  TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager: own " << output.size() << " / " << buffer_count << " buffers.";
765  return output;
766 }
767 
769 {
770  TLOG(TLVL_BUFFER) << "BufferDataSize(" << buffer << ") called.";
771 
772  if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
773  {
774  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
775  }
776 
777  TLOG(TLVL_BUFLCK) << "BufferDataSize obtaining buffer_mutex for buffer " << buffer;
778  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
779  TLOG(TLVL_BUFLCK) << "BufferDataSize obtained buffer_mutex for buffer " << buffer;
780  // TraceLock lk(buffer_mutexes_[buffer], 17, "DataSizeBuffer" + std::to_string(buffer));
781 
782  auto buf = getBufferInfo_(buffer);
783  if (buf == nullptr)
784  {
785  return 0;
786  }
787  touchBuffer_(buf);
788 
789  TLOG(TLVL_BUFFER) << "BufferDataSize: buffer " << buffer << ", size=" << buf->writePos;
790  return buf->writePos;
791 }
792 
794 {
795  TLOG(TLVL_POS) << "ResetReadPos(" << buffer << ") called.";
796 
797  if (buffer >= shm_ptr_->buffer_count)
798  {
799  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
800  }
801 
802  TLOG(TLVL_BUFLCK) << "ResetReadPos obtaining buffer_mutex for buffer " << buffer;
803  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
804  TLOG(TLVL_BUFLCK) << "ResetReadPos obtained buffer_mutex for buffer " << buffer;
805 
806  // TraceLock lk(buffer_mutexes_[buffer], 18, "ResetReadPosBuffer" + std::to_string(buffer));
807  auto buf = getBufferInfo_(buffer);
808  if ((buf == nullptr) || buf->sem_id != manager_id_)
809  {
810  return;
811  }
812  touchBuffer_(buf);
813  buf->readPos = 0;
814 
815  TLOG(TLVL_POS) << "ResetReadPos(" << buffer << ") ended.";
816 }
817 
819 {
820  TLOG(TLVL_POS + 1) << "ResetWritePos(" << buffer << ") called.";
821 
822  if (buffer >= shm_ptr_->buffer_count)
823  {
824  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
825  }
826 
827  TLOG(TLVL_BUFLCK) << "ResetWritePos obtaining buffer_mutex for buffer " << buffer;
828  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
829  TLOG(TLVL_BUFLCK) << "ResetWritePos obtained buffer_mutex for buffer " << buffer;
830 
831  // TraceLock lk(buffer_mutexes_[buffer], 18, "ResetWritePosBuffer" + std::to_string(buffer));
832  auto buf = getBufferInfo_(buffer);
833  if (buf == nullptr)
834  {
835  return;
836  }
837  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
838  touchBuffer_(buf);
839  buf->writePos = 0;
840 
841  TLOG(TLVL_POS + 1) << "ResetWritePos(" << buffer << ") ended.";
842 }
843 
845 {
846  TLOG(TLVL_POS) << "IncrementReadPos called: buffer= " << buffer << ", bytes to read=" << read;
847 
848  if (buffer >= shm_ptr_->buffer_count)
849  {
850  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
851  }
852 
853  TLOG(TLVL_BUFLCK) << "IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
854  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
855  TLOG(TLVL_BUFLCK) << "IncrementReadPos obtained buffer_mutex for buffer " << buffer;
856  // TraceLock lk(buffer_mutexes_[buffer], 19, "IncReadPosBuffer" + std::to_string(buffer));
857  auto buf = getBufferInfo_(buffer);
858  if ((buf == nullptr) || buf->sem_id != manager_id_)
859  {
860  return;
861  }
862  touchBuffer_(buf);
863  TLOG(TLVL_POS) << "IncrementReadPos: buffer= " << buffer << ", readPos=" << buf->readPos << ", bytes read=" << read;
864  buf->readPos = buf->readPos + read;
865  TLOG(TLVL_POS) << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << buf->readPos;
866  if (read == 0)
867  {
868  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) + ")");
869  }
870 }
871 
872 bool artdaq::SharedMemoryManager::IncrementWritePos(int buffer, size_t written)
873 {
874  TLOG(TLVL_POS + 1) << "IncrementWritePos called: buffer= " << buffer << ", bytes written=" << written;
875 
876  if (buffer >= shm_ptr_->buffer_count)
877  {
878  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
879  }
880 
881  TLOG(TLVL_BUFLCK) << "IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
882  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
883  TLOG(TLVL_BUFLCK) << "IncrementWritePos obtained buffer_mutex for buffer " << buffer;
884  // TraceLock lk(buffer_mutexes_[buffer], 20, "IncWritePosBuffer" + std::to_string(buffer));
885  auto buf = getBufferInfo_(buffer);
886  if (buf == nullptr)
887  {
888  return false;
889  }
890  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
891  touchBuffer_(buf);
892  if (buf->writePos + written > shm_ptr_->buffer_size)
893  {
894  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 << ")";
895  return false;
896  }
897  TLOG(TLVL_POS + 1) << "IncrementWritePos: buffer= " << buffer << ", writePos=" << buf->writePos << ", bytes written=" << written;
898  buf->writePos += written;
899  TLOG(TLVL_POS + 1) << "IncrementWritePos: buffer= " << buffer << ", New writePos is " << buf->writePos;
900  if (written == 0)
901  {
902  Detach(true, "LogicError", "Cannot increment Write pos by 0!");
903  }
904 
905  return true;
906 }
907 
909 {
910  TLOG(TLVL_POS + 2) << "MoreDataInBuffer(" << buffer << ") called.";
911 
912  if (buffer >= shm_ptr_->buffer_count)
913  {
914  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
915  }
916 
917  TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
918  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
919  TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
920  // TraceLock lk(buffer_mutexes_[buffer], 21, "MoreDataInBuffer" + std::to_string(buffer));
921  auto buf = getBufferInfo_(buffer);
922  if (buf == nullptr)
923  {
924  return false;
925  }
926  TLOG(TLVL_POS + 2) << "MoreDataInBuffer: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", writePos=" << buf->writePos;
927  return buf->readPos < buf->writePos;
928 }
929 
931 {
932  if (buffer >= shm_ptr_->buffer_count)
933  {
934  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
935  }
936 
937  TLOG(TLVL_BUFLCK) << "CheckBuffer obtaining buffer_mutex for buffer " << buffer;
938  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
939  TLOG(TLVL_BUFLCK) << "CheckBuffer obtained buffer_mutex for buffer " << buffer;
940  // TraceLock lk(buffer_mutexes_[buffer], 22, "CheckBuffer" + std::to_string(buffer));
941  return checkBuffer_(getBufferInfo_(buffer), flags, false);
942 }
943 
944 void artdaq::SharedMemoryManager::MarkBufferFull(int buffer, int destination)
945 {
946  if (buffer >= shm_ptr_->buffer_count)
947  {
948  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
949  }
950 
951  TLOG(TLVL_BUFLCK) << "MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
952  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
953  TLOG(TLVL_BUFLCK) << "MarkBufferFull obtained buffer_mutex for buffer " << buffer;
954 
955  // TraceLock lk(buffer_mutexes_[buffer], 23, "FillBuffer" + std::to_string(buffer));
956  auto shmBuf = getBufferInfo_(buffer);
957  if (shmBuf == nullptr)
958  {
959  return;
960  }
961  touchBuffer_(shmBuf);
962  if (shmBuf->sem_id == manager_id_)
963  {
964  if (shmBuf->sem != BufferSemaphoreFlags::Full)
965  {
966  shmBuf->sem = BufferSemaphoreFlags::Full;
967  }
968 
969  shmBuf->sem_id = destination;
970  }
971 }
972 
973 void artdaq::SharedMemoryManager::MarkBufferEmpty(int buffer, bool force, bool detachOnException)
974 {
975  TLOG(TLVL_POS + 3) << "MarkBufferEmpty BEGIN, buffer=" << buffer << ", force=" << force << ", manager_id_=" << manager_id_;
976  if (buffer >= shm_ptr_->buffer_count)
977  {
978  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
979  }
980  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
981  // TraceLock lk(buffer_mutexes_[buffer], 24, "EmptyBuffer" + std::to_string(buffer));
982  auto shmBuf = getBufferInfo_(buffer);
983  if (shmBuf == nullptr)
984  {
985  return;
986  }
987  if (!force)
988  {
989  auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
990  if (!ret) return;
991  }
992  touchBuffer_(shmBuf);
993 
994  shmBuf->readPos = 0;
995  shmBuf->sem = BufferSemaphoreFlags::Full;
996 
997  if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
998  {
999  TLOG(TLVL_POS + 3) << "MarkBufferEmpty Resetting buffer " << buffer << " to Empty state";
1000  shmBuf->writePos = 0;
1001  shmBuf->sem = BufferSemaphoreFlags::Empty;
1002  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
1003  {
1004  TLOG(TLVL_POS + 3) << "MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos << " to " << (buffer + 1) % shm_ptr_->buffer_count;
1005  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1006  }
1007  }
1008  shmBuf->sem_id = -1;
1009  TLOG(TLVL_POS + 3) << "MarkBufferEmpty END, buffer=" << buffer << ", force=" << force;
1010 }
1011 
1013 {
1014  if (buffer >= shm_ptr_->buffer_count)
1015  {
1016  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1017  }
1018 
1019  // ELF, 3/19/2019: These TRACE calls are a major performance hit with many buffers.
1020  // TLOG(TLVL_BUFLCK) << "ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
1021  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1022  // TLOG(TLVL_BUFLCK) << "ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
1023 
1024  // TraceLock lk(buffer_mutexes_[buffer], 25, "ResetBuffer" + std::to_string(buffer));
1025  auto shmBuf = getBufferInfo_(buffer);
1026  if (shmBuf == nullptr)
1027  {
1028  return false;
1029  }
1030  /*
1031  if (shmBuf->sequence_id < shm_ptr_->lowest_seq_id_read - size() && shmBuf->sem == BufferSemaphoreFlags::Full)
1032  {
1033  TLOG(TLVL_RESET) << "Buffer " << buffer << " has been passed by all readers, marking Empty" ;
1034  shmBuf->writePos = 0;
1035  shmBuf->sem = BufferSemaphoreFlags::Empty;
1036  shmBuf->sem_id = -1;
1037  return true;
1038  }*/
1039 
1040  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1041  if (delta > 0xFFFFFFFF)
1042  {
1043  TLOG(TLVL_RESET) << "Buffer has touch time in the future, setting it to current time and ignoring...";
1044  shmBuf->last_touch_time = TimeUtils::gettimeofday_us();
1045  return false;
1046  }
1047  if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1048  {
1049  return false;
1050  }
1051  TLOG(TLVL_RESET) << "Buffer " << buffer << " at " << static_cast<void*>(shmBuf) << " is stale, time=" << TimeUtils::gettimeofday_us() << ", last touch=" << shmBuf->last_touch_time << ", d=" << delta << ", timeout=" << shm_ptr_->buffer_timeout_us;
1052 
1053  if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1054  {
1055  return true;
1056  }
1057 
1058  if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1059  {
1060  TLOG(TLVL_RESET) << "Resetting old broadcast mode buffer " << buffer << " (seqid=" << shmBuf->sequence_id << "). State: Full-->Empty";
1061  shmBuf->writePos = 0;
1062  shmBuf->sem = BufferSemaphoreFlags::Empty;
1063  shmBuf->sem_id = -1;
1064  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1065  {
1066  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1067  }
1068  return true;
1069  }
1070 
1071  if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1072  {
1073  // Ron wants to re-check for potential interleave of buffer state updates
1074  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1075  if (delta <= shm_ptr_->buffer_timeout_us)
1076  {
1077  return false;
1078  }
1079  TLOG(TLVL_WARNING) << "Stale Read buffer " << buffer << " at " << static_cast<void*>(shmBuf)
1080  << " ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! (seqid="
1081  << shmBuf->sequence_id << ") Resetting... Reading-->Full";
1082  shmBuf->readPos = 0;
1083  shmBuf->sem = BufferSemaphoreFlags::Full;
1084  shmBuf->sem_id = -1;
1085  return true;
1086  }
1087  return false;
1088 }
1089 
1091 {
1092  if (!IsValid())
1093  {
1094  return true;
1095  }
1096 
1097  struct shmid_ds info;
1098  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1099  if (sts < 0)
1100  {
1101  TLOG(TLVL_BUFINFO) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1102  return true;
1103  }
1104 
1105  if ((info.shm_perm.mode & SHM_DEST) != 0)
1106  {
1107  TLOG(TLVL_INFO) << "Shared Memory marked for destruction. Probably an end-of-data condition!";
1108  return true;
1109  }
1110 
1111  return false;
1112 }
1113 
1115 {
1116  if (!IsValid())
1117  {
1118  return 0;
1119  }
1120 
1121  struct shmid_ds info;
1122  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1123  if (sts < 0)
1124  {
1125  TLOG(TLVL_BUFINFO) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1126  return 0;
1127  }
1128 
1129  return info.shm_nattch;
1130 }
1131 
1132 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
1133 {
1134  TLOG(TLVL_WRITE) << "Write BEGIN";
1135  if (buffer >= shm_ptr_->buffer_count)
1136  {
1137  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1138  }
1139  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1140  // TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer));
1141  auto shmBuf = getBufferInfo_(buffer);
1142  if (shmBuf == nullptr)
1143  {
1144  return -1;
1145  }
1146  checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1147  touchBuffer_(shmBuf);
1148  TLOG(TLVL_WRITE) << "Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos << ", write size is " << size;
1149  if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1150  {
1151  TLOG(TLVL_ERROR) << "Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1152  << ",writePos=" << shmBuf->writePos << ",writeSize=" << size;
1153  Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1154  }
1155 
1156  auto pos = GetWritePos(buffer);
1157  memcpy(pos, data, size);
1158  touchBuffer_(shmBuf);
1159  shmBuf->writePos = shmBuf->writePos + size;
1160 
1161  auto last_seen = last_seen_id_.load();
1162  while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1163 
1164  TLOG(TLVL_WRITE) << "Write END";
1165  return size;
1166 }
1167 
1168 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
1169 {
1170  if (buffer >= shm_ptr_->buffer_count)
1171  {
1172  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1173  }
1174  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1175  // TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer));
1176  auto shmBuf = getBufferInfo_(buffer);
1177  if (shmBuf == nullptr)
1178  {
1179  return false;
1180  }
1181  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1182  touchBuffer_(shmBuf);
1183  if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1184  {
1185  TLOG(TLVL_ERROR) << "Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1186  << ",readPos=" << shmBuf->readPos << ",readSize=" << size;
1187  Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
1188  }
1189 
1190  auto pos = GetReadPos(buffer);
1191  TLOG(TLVL_READ) << "Before memcpy in Read(), size is " << size;
1192  memcpy(data, pos, size);
1193  TLOG(TLVL_READ) << "After memcpy in Read()";
1194  auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
1195  if (sts)
1196  {
1197  shmBuf->readPos += size;
1198  touchBuffer_(shmBuf);
1199  return true;
1200  }
1201  return false;
1202 }
1203 
1205 {
1206  if (shm_ptr_ == nullptr)
1207  {
1208  return "Not connected to shared memory";
1209  }
1210  std::ostringstream ostr;
1211  ostr << "ShmStruct: " << std::endl
1212  << "Reader Position: " << shm_ptr_->reader_pos << std::endl
1213  << "Writer Position: " << shm_ptr_->writer_pos << std::endl
1214  << "Next ID Number: " << shm_ptr_->next_id << std::endl
1215  << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
1216  << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
1217  << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1218  << "Rank of Writer: " << shm_ptr_->rank << std::endl
1219  << "Number of Writers: " << shm_ptr_->writer_count << std::endl
1220  << "Number of Readers: " << shm_ptr_->reader_count << std::endl
1221  << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1222  << std::endl;
1223 
1224  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1225  {
1226  auto buf = getBufferInfo_(ii);
1227  if (buf == nullptr)
1228  {
1229  continue;
1230  }
1231 
1232  ostr << "ShmBuffer " << std::dec << ii << std::endl
1233  << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1234  << "writePos: " << std::to_string(buf->writePos) << std::endl
1235  << "readPos: " << std::to_string(buf->readPos) << std::endl
1236  << "sem: " << FlagToString(buf->sem) << std::endl
1237  << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1238  << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1239  << std::endl;
1240  }
1241 
1242  return ostr.str();
1243 }
1244 
1246 {
1247  auto buf = getBufferInfo_(buffer);
1248  if (buf == nullptr)
1249  {
1250  return nullptr;
1251  }
1252  return bufferStart_(buffer) + buf->readPos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1253 }
1255 {
1256  auto buf = getBufferInfo_(buffer);
1257  if (buf == nullptr)
1258  {
1259  return nullptr;
1260  }
1261  return bufferStart_(buffer) + buf->writePos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1262 }
1263 
1265 {
1266  return bufferStart_(buffer);
1267 }
1268 
1269 std::vector<std::pair<int, artdaq::SharedMemoryManager::BufferSemaphoreFlags>> artdaq::SharedMemoryManager::GetBufferReport()
1270 {
1271  auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1272  for (size_t ii = 0; ii < size(); ++ii)
1273  {
1274  auto buf = getBufferInfo_(ii);
1275  output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1276  }
1277  return output;
1278 }
1279 
1280 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
1281 {
1282  if (buffer == nullptr)
1283  {
1284  if (exceptions)
1285  {
1286  Detach(true, "BufferNotThereException", "Request to check buffer that does not exist!");
1287  }
1288  return false;
1289  }
1290  TLOG(TLVL_CHKBUFFER) << "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) << ")";
1291  if (exceptions)
1292  {
1293  if (buffer->sem != flags)
1294  {
1295  Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
1296  }
1297  if (buffer->sem_id != manager_id_)
1298  {
1299  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) + ")");
1300  }
1301  }
1302  bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1303 
1304  if (!ret)
1305  {
1306  TLOG(TLVL_WARNING) << "CheckBuffer detected issue with buffer " << buffer->sequence_id << "!"
1307  << " ID: " << buffer->sem_id << " (Expected " << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (Expected " << FlagToString(flags) << "). "
1308  << R"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1309  }
1310 
1311  return ret;
1312 }
1313 
1314 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1315 {
1316  if ((buffer == nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1317  {
1318  return;
1319  }
1320  TLOG(TLVL_CHKBUFFER + 1) << "touchBuffer_: Touching buffer at " << static_cast<void*>(buffer) << " with sequence_id " << buffer->sequence_id;
1321  buffer->last_touch_time = TimeUtils::gettimeofday_us();
1322 }
1323 
1324 void artdaq::SharedMemoryManager::Detach(bool throwException, const std::string& category, const std::string& message, bool force)
1325 {
1326  TLOG(TLVL_DETACH) << "Detach BEGIN: throwException: " << std::boolalpha << throwException << ", force: " << force;
1327  if (IsValid())
1328  {
1329  TLOG(TLVL_DETACH) << "Detach: Resetting owned buffers";
1330  auto bufs = GetBuffersOwnedByManager(false);
1331  for (auto buf : bufs)
1332  {
1333  auto shmBuf = getBufferInfo_(buf);
1334  if (shmBuf == nullptr)
1335  {
1336  continue;
1337  }
1338  if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1339  {
1340  shmBuf->sem = BufferSemaphoreFlags::Empty;
1341  }
1342  else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1343  {
1344  shmBuf->sem = BufferSemaphoreFlags::Full;
1345  }
1346  shmBuf->sem_id = -1;
1347  }
1348  if (registered_reader_)
1349  {
1350  shm_ptr_->reader_count--;
1351  registered_reader_ = false;
1352  }
1353  if (registered_writer_)
1354  {
1355  shm_ptr_->writer_count--;
1356  registered_writer_ = false;
1357  }
1358  }
1359 
1360  if (shm_ptr_ != nullptr)
1361  {
1362  TLOG(TLVL_DETACH) << "Detach: Detaching shared memory";
1363  shmdt(shm_ptr_);
1364  shm_ptr_ = nullptr;
1365  }
1366 
1367  if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1368  {
1369  TLOG(TLVL_DETACH) << "Detach: Marking Shared memory for removal";
1370  shmctl(shm_segment_id_, IPC_RMID, nullptr);
1371  shm_segment_id_ = -1;
1372  }
1373 
1374  // Reset manager_id_
1375  manager_id_ = -1;
1376 
1377  if (!category.empty() && !message.empty())
1378  {
1379  TLOG(TLVL_ERROR) << category << ": " << message;
1380 
1381  if (throwException)
1382  {
1383  throw cet::exception(category) << message; // NOLINT(cert-err60-cpp)
1384  }
1385  }
1386 }
1387 
1388 // Local Variables:
1389 // mode: c++
1390 // End:
void Detach(bool throwException=false, const std::string &category="", const std::string &message="", bool force=false)
Detach from the Shared Memory segment, optionally throwing a cet::exception with the specified proper...
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.
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
Definition: TimeUtils.hh:41
std::vector< std::pair< int, BufferSemaphoreFlags > > GetBufferReport()
Get a report on the status of each buffer.
BufferSemaphoreFlags
The BufferSemaphoreFlags enumeration represents the different possible &quot;states&quot; of a given shared mem...
size_t BufferDataSize(int buffer)
Get the current size of the buffer&#39;s data.
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, bool detachOnException=true)
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)
Definition: TimeUtils.cc:51
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.