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