artdaq_core  v3_06_09
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  size_t buffer_count = size();
687  if (!IsValid() || buffer_count == 0)
688  {
689  return output;
690  }
691  TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager BEGIN. Locked? " << locked;
692  if (locked)
693  {
694  TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtaining search_mutex";
695  std::lock_guard<std::mutex> lk(search_mutex_);
696  TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtained search_mutex";
697  //TraceLock lk(search_mutex_, 16, "GetOwnedSearch");
698  for (size_t ii = 0; ii < buffer_count; ++ii)
699  {
700  auto buf = getBufferInfo_(ii);
701  if (buf == nullptr)
702  {
703  continue;
704  }
705  if (buf->sem_id == manager_id_)
706  {
707  output.push_back(ii);
708  }
709  }
710  }
711  else
712  {
713  for (size_t ii = 0; ii < buffer_count; ++ii)
714  {
715  auto buf = getBufferInfo_(ii);
716  if (buf == nullptr)
717  {
718  continue;
719  }
720  if (buf->sem_id == manager_id_)
721  {
722  output.push_back(ii);
723  }
724  }
725  }
726 
727  TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager: own " << output.size() << " / " << buffer_count << " buffers.";
728  return output;
729 }
730 
732 {
733  TLOG(TLVL_BUFFER) << "BufferDataSize(" << buffer << ") called.";
734 
735  if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
736  {
737  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
738  }
739 
740  TLOG(TLVL_BUFLCK) << "BufferDataSize obtaining buffer_mutex for buffer " << buffer;
741  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
742  TLOG(TLVL_BUFLCK) << "BufferDataSize obtained buffer_mutex for buffer " << buffer;
743  //TraceLock lk(buffer_mutexes_[buffer], 17, "DataSizeBuffer" + std::to_string(buffer));
744 
745  auto buf = getBufferInfo_(buffer);
746  if (buf == nullptr)
747  {
748  return 0;
749  }
750  touchBuffer_(buf);
751 
752  TLOG(TLVL_BUFFER) << "BufferDataSize: buffer " << buffer << ", size=" << buf->writePos;
753  return buf->writePos;
754 }
755 
757 {
758  TLOG(15) << "ResetReadPos(" << buffer << ") called.";
759 
760  if (buffer >= shm_ptr_->buffer_count)
761  {
762  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
763  }
764 
765  TLOG(TLVL_BUFLCK) << "ResetReadPos obtaining buffer_mutex for buffer " << buffer;
766  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
767  TLOG(TLVL_BUFLCK) << "ResetReadPos obtained buffer_mutex for buffer " << buffer;
768 
769  //TraceLock lk(buffer_mutexes_[buffer], 18, "ResetReadPosBuffer" + std::to_string(buffer));
770  auto buf = getBufferInfo_(buffer);
771  if ((buf == nullptr) || buf->sem_id != manager_id_)
772  {
773  return;
774  }
775  touchBuffer_(buf);
776  buf->readPos = 0;
777 
778  TLOG(15) << "ResetReadPos(" << buffer << ") ended.";
779 }
780 
782 {
783  TLOG(16) << "ResetWritePos(" << buffer << ") called.";
784 
785  if (buffer >= shm_ptr_->buffer_count)
786  {
787  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
788  }
789 
790  TLOG(TLVL_BUFLCK) << "ResetWritePos obtaining buffer_mutex for buffer " << buffer;
791  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
792  TLOG(TLVL_BUFLCK) << "ResetWritePos obtained buffer_mutex for buffer " << buffer;
793 
794  //TraceLock lk(buffer_mutexes_[buffer], 18, "ResetWritePosBuffer" + std::to_string(buffer));
795  auto buf = getBufferInfo_(buffer);
796  if (buf == nullptr)
797  {
798  return;
799  }
800  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
801  touchBuffer_(buf);
802  buf->writePos = 0;
803 
804  TLOG(16) << "ResetWritePos(" << buffer << ") ended.";
805 }
806 
808 {
809  TLOG(15) << "IncrementReadPos called: buffer= " << buffer << ", bytes to read=" << read;
810 
811  if (buffer >= shm_ptr_->buffer_count)
812  {
813  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
814  }
815 
816  TLOG(TLVL_BUFLCK) << "IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
817  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
818  TLOG(TLVL_BUFLCK) << "IncrementReadPos obtained buffer_mutex for buffer " << buffer;
819  //TraceLock lk(buffer_mutexes_[buffer], 19, "IncReadPosBuffer" + std::to_string(buffer));
820  auto buf = getBufferInfo_(buffer);
821  if ((buf == nullptr) || buf->sem_id != manager_id_)
822  {
823  return;
824  }
825  touchBuffer_(buf);
826  TLOG(15) << "IncrementReadPos: buffer= " << buffer << ", readPos=" << buf->readPos << ", bytes read=" << read;
827  buf->readPos = buf->readPos + read;
828  TLOG(15) << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << buf->readPos;
829  if (read == 0)
830  {
831  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) + ")");
832  }
833 }
834 
835 bool artdaq::SharedMemoryManager::IncrementWritePos(int buffer, size_t written)
836 {
837  TLOG(16) << "IncrementWritePos called: buffer= " << buffer << ", bytes written=" << written;
838 
839  if (buffer >= shm_ptr_->buffer_count)
840  {
841  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
842  }
843 
844  TLOG(TLVL_BUFLCK) << "IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
845  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
846  TLOG(TLVL_BUFLCK) << "IncrementWritePos obtained buffer_mutex for buffer " << buffer;
847  //TraceLock lk(buffer_mutexes_[buffer], 20, "IncWritePosBuffer" + std::to_string(buffer));
848  auto buf = getBufferInfo_(buffer);
849  if (buf == nullptr)
850  {
851  return false;
852  }
853  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
854  touchBuffer_(buf);
855  if (buf->writePos + written > shm_ptr_->buffer_size)
856  {
857  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 << ")";
858  return false;
859  }
860  TLOG(16) << "IncrementWritePos: buffer= " << buffer << ", writePos=" << buf->writePos << ", bytes written=" << written;
861  buf->writePos += written;
862  TLOG(16) << "IncrementWritePos: buffer= " << buffer << ", New writePos is " << buf->writePos;
863  if (written == 0)
864  {
865  Detach(true, "LogicError", "Cannot increment Write pos by 0!");
866  }
867 
868  return true;
869 }
870 
872 {
873  TLOG(17) << "MoreDataInBuffer(" << buffer << ") called.";
874 
875  if (buffer >= shm_ptr_->buffer_count)
876  {
877  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
878  }
879 
880  TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
881  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
882  TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
883  //TraceLock lk(buffer_mutexes_[buffer], 21, "MoreDataInBuffer" + std::to_string(buffer));
884  auto buf = getBufferInfo_(buffer);
885  if (buf == nullptr)
886  {
887  return false;
888  }
889  TLOG(17) << "MoreDataInBuffer: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", writePos=" << buf->writePos;
890  return buf->readPos < buf->writePos;
891 }
892 
894 {
895  if (buffer >= shm_ptr_->buffer_count)
896  {
897  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
898  }
899 
900  TLOG(TLVL_BUFLCK) << "CheckBuffer obtaining buffer_mutex for buffer " << buffer;
901  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
902  TLOG(TLVL_BUFLCK) << "CheckBuffer obtained buffer_mutex for buffer " << buffer;
903  //TraceLock lk(buffer_mutexes_[buffer], 22, "CheckBuffer" + std::to_string(buffer));
904  return checkBuffer_(getBufferInfo_(buffer), flags, false);
905 }
906 
907 void artdaq::SharedMemoryManager::MarkBufferFull(int buffer, int destination)
908 {
909  if (buffer >= shm_ptr_->buffer_count)
910  {
911  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
912  }
913 
914  TLOG(TLVL_BUFLCK) << "MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
915  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
916  TLOG(TLVL_BUFLCK) << "MarkBufferFull obtained buffer_mutex for buffer " << buffer;
917 
918  //TraceLock lk(buffer_mutexes_[buffer], 23, "FillBuffer" + std::to_string(buffer));
919  auto shmBuf = getBufferInfo_(buffer);
920  if (shmBuf == nullptr)
921  {
922  return;
923  }
924  touchBuffer_(shmBuf);
925  if (shmBuf->sem_id == manager_id_)
926  {
927  if (shmBuf->sem != BufferSemaphoreFlags::Full)
928  {
929  shmBuf->sem = BufferSemaphoreFlags::Full;
930  }
931 
932  shmBuf->sem_id = destination;
933  }
934 }
935 
936 void artdaq::SharedMemoryManager::MarkBufferEmpty(int buffer, bool force, bool detachOnException)
937 {
938  TLOG(18) << "MarkBufferEmpty BEGIN, buffer=" << buffer << ", force=" << force << ", manager_id_=" << manager_id_;
939  if (buffer >= shm_ptr_->buffer_count)
940  {
941  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
942  }
943  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
944  //TraceLock lk(buffer_mutexes_[buffer], 24, "EmptyBuffer" + std::to_string(buffer));
945  auto shmBuf = getBufferInfo_(buffer);
946  if (shmBuf == nullptr)
947  {
948  return;
949  }
950  if (!force)
951  {
952  auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
953  if (!ret) return;
954  }
955  touchBuffer_(shmBuf);
956 
957  shmBuf->readPos = 0;
958  shmBuf->sem = BufferSemaphoreFlags::Full;
959 
960  if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
961  {
962  TLOG(18) << "MarkBufferEmpty Resetting buffer " << buffer << " to Empty state";
963  shmBuf->writePos = 0;
964  shmBuf->sem = BufferSemaphoreFlags::Empty;
965  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
966  {
967  TLOG(18) << "MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos << " to " << (buffer + 1) % shm_ptr_->buffer_count;
968  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
969  }
970  }
971  shmBuf->sem_id = -1;
972  TLOG(18) << "MarkBufferEmpty END, buffer=" << buffer << ", force=" << force;
973  ;
974 }
975 
977 {
978  if (buffer >= shm_ptr_->buffer_count)
979  {
980  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
981  }
982 
983  // ELF, 3/19/2019: These TRACE calls are a major performance hit with many buffers.
984  //TLOG(TLVL_BUFLCK) << "ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
985  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
986  //TLOG(TLVL_BUFLCK) << "ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
987 
988  //TraceLock lk(buffer_mutexes_[buffer], 25, "ResetBuffer" + std::to_string(buffer));
989  auto shmBuf = getBufferInfo_(buffer);
990  if (shmBuf == nullptr)
991  {
992  return false;
993  }
994  /*
995  if (shmBuf->sequence_id < shm_ptr_->lowest_seq_id_read - size() && shmBuf->sem == BufferSemaphoreFlags::Full)
996  {
997  TLOG(TLVL_DEBUG) << "Buffer " << buffer << " has been passed by all readers, marking Empty" ;
998  shmBuf->writePos = 0;
999  shmBuf->sem = BufferSemaphoreFlags::Empty;
1000  shmBuf->sem_id = -1;
1001  return true;
1002  }*/
1003 
1004  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1005  if (delta > 0xFFFFFFFF)
1006  {
1007  TLOG(TLVL_TRACE) << "Buffer has touch time in the future, setting it to current time and ignoring...";
1008  shmBuf->last_touch_time = TimeUtils::gettimeofday_us();
1009  return false;
1010  }
1011  if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1012  {
1013  return false;
1014  }
1015  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;
1016 
1017  if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1018  {
1019  return true;
1020  }
1021 
1022  if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1023  {
1024  TLOG(TLVL_DEBUG) << "Resetting old broadcast mode buffer " << buffer << " (seqid=" << shmBuf->sequence_id << "). State: Full-->Empty";
1025  shmBuf->writePos = 0;
1026  shmBuf->sem = BufferSemaphoreFlags::Empty;
1027  shmBuf->sem_id = -1;
1028  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1029  {
1030  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1031  }
1032  return true;
1033  }
1034 
1035  if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1036  {
1037  // Ron wants to re-check for potential interleave of buffer state updates
1038  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1039  if (delta <= shm_ptr_->buffer_timeout_us)
1040  {
1041  return false;
1042  }
1043  TLOG(TLVL_WARNING) << "Stale Read buffer " << buffer << " at " << static_cast<void*>(shmBuf)
1044  << " ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! (seqid="
1045  << shmBuf->sequence_id << ") Resetting... Reading-->Full";
1046  shmBuf->readPos = 0;
1047  shmBuf->sem = BufferSemaphoreFlags::Full;
1048  shmBuf->sem_id = -1;
1049  return true;
1050  }
1051  return false;
1052 }
1053 
1055 {
1056  if (!IsValid())
1057  {
1058  return true;
1059  }
1060 
1061  struct shmid_ds info;
1062  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1063  if (sts < 0)
1064  {
1065  TLOG(TLVL_TRACE) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1066  return true;
1067  }
1068 
1069  if ((info.shm_perm.mode & SHM_DEST) != 0)
1070  {
1071  TLOG(TLVL_INFO) << "Shared Memory marked for destruction. Probably an end-of-data condition!";
1072  return true;
1073  }
1074 
1075  return false;
1076 }
1077 
1079 {
1080  if (!IsValid())
1081  {
1082  return 0;
1083  }
1084 
1085  struct shmid_ds info;
1086  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1087  if (sts < 0)
1088  {
1089  TLOG(TLVL_TRACE) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1090  return 0;
1091  }
1092 
1093  return info.shm_nattch;
1094 }
1095 
1096 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
1097 {
1098  TLOG(19) << "Write BEGIN";
1099  if (buffer >= shm_ptr_->buffer_count)
1100  {
1101  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1102  }
1103  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1104  //TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer));
1105  auto shmBuf = getBufferInfo_(buffer);
1106  if (shmBuf == nullptr)
1107  {
1108  return -1;
1109  }
1110  checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1111  touchBuffer_(shmBuf);
1112  TLOG(19) << "Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos << ", write size is " << size;
1113  if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1114  {
1115  TLOG(TLVL_ERROR) << "Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1116  << ",writePos=" << shmBuf->writePos << ",writeSize=" << size;
1117  Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1118  }
1119 
1120  auto pos = GetWritePos(buffer);
1121  memcpy(pos, data, size);
1122  touchBuffer_(shmBuf);
1123  shmBuf->writePos = shmBuf->writePos + size;
1124 
1125  auto last_seen = last_seen_id_.load();
1126  while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1127 
1128  TLOG(19) << "Write END";
1129  return size;
1130 }
1131 
1132 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
1133 {
1134  if (buffer >= shm_ptr_->buffer_count)
1135  {
1136  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1137  }
1138  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1139  //TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer));
1140  auto shmBuf = getBufferInfo_(buffer);
1141  if (shmBuf == nullptr)
1142  {
1143  return false;
1144  }
1145  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1146  touchBuffer_(shmBuf);
1147  if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1148  {
1149  TLOG(TLVL_ERROR) << "Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1150  << ",readPos=" << shmBuf->readPos << ",readSize=" << size;
1151  Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
1152  }
1153 
1154  auto pos = GetReadPos(buffer);
1155  TLOG(TLVL_TRACE) << "Before memcpy in Read(), size is " << size;
1156  memcpy(data, pos, size);
1157  TLOG(TLVL_TRACE) << "After memcpy in Read()";
1158  auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
1159  if (sts)
1160  {
1161  shmBuf->readPos += size;
1162  touchBuffer_(shmBuf);
1163  return true;
1164  }
1165  return false;
1166 }
1167 
1169 {
1170  std::ostringstream ostr;
1171  ostr << "ShmStruct: " << std::endl
1172  << "Reader Position: " << shm_ptr_->reader_pos << std::endl
1173  << "Writer Position: " << shm_ptr_->writer_pos << std::endl
1174  << "Next ID Number: " << shm_ptr_->next_id << std::endl
1175  << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
1176  << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
1177  << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1178  << "Rank of Writer: " << shm_ptr_->rank << std::endl
1179  << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1180  << std::endl;
1181 
1182  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1183  {
1184  auto buf = getBufferInfo_(ii);
1185  if (buf == nullptr)
1186  {
1187  continue;
1188  }
1189 
1190  ostr << "ShmBuffer " << std::dec << ii << std::endl
1191  << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1192  << "writePos: " << std::to_string(buf->writePos) << std::endl
1193  << "readPos: " << std::to_string(buf->readPos) << std::endl
1194  << "sem: " << FlagToString(buf->sem) << std::endl
1195  << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1196  << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1197  << std::endl;
1198  }
1199 
1200  return ostr.str();
1201 }
1202 
1204 {
1205  auto buf = getBufferInfo_(buffer);
1206  if (buf == nullptr)
1207  {
1208  return nullptr;
1209  }
1210  return bufferStart_(buffer) + buf->readPos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1211 }
1213 {
1214  auto buf = getBufferInfo_(buffer);
1215  if (buf == nullptr)
1216  {
1217  return nullptr;
1218  }
1219  return bufferStart_(buffer) + buf->writePos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1220 }
1221 
1223 {
1224  return bufferStart_(buffer);
1225 }
1226 
1227 std::vector<std::pair<int, artdaq::SharedMemoryManager::BufferSemaphoreFlags>> artdaq::SharedMemoryManager::GetBufferReport()
1228 {
1229  auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1230  for (size_t ii = 0; ii < size(); ++ii)
1231  {
1232  auto buf = getBufferInfo_(ii);
1233  output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1234  }
1235  return output;
1236 }
1237 
1238 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
1239 {
1240  if (buffer == nullptr)
1241  {
1242  if (exceptions)
1243  {
1244  Detach(true, "BufferNotThereException", "Request to check buffer that does not exist!");
1245  }
1246  return false;
1247  }
1248  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) << ")";
1249  if (exceptions)
1250  {
1251  if (buffer->sem != flags)
1252  {
1253  Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
1254  }
1255  if (buffer->sem_id != manager_id_)
1256  {
1257  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) + ")");
1258  }
1259  }
1260  bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1261 
1262  if (!ret)
1263  {
1264  TLOG(TLVL_WARNING) << "CheckBuffer detected issue with buffer " << buffer->sequence_id << "!"
1265  << " ID: " << buffer->sem_id << " (Expected " << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (Expected " << FlagToString(flags) << "). "
1266  << R"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1267  }
1268 
1269  return ret;
1270 }
1271 
1272 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1273 {
1274  if ((buffer == nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1275  {
1276  return;
1277  }
1278  TLOG(TLVL_TRACE) << "touchBuffer_: Touching buffer at " << static_cast<void*>(buffer) << " with sequence_id " << buffer->sequence_id;
1279  buffer->last_touch_time = TimeUtils::gettimeofday_us();
1280 }
1281 
1282 void artdaq::SharedMemoryManager::Detach(bool throwException, const std::string& category, const std::string& message, bool force)
1283 {
1284  TLOG(TLVL_DETACH) << "Detach BEGIN: throwException: " << std::boolalpha << throwException << ", force: " << force;
1285  if (IsValid())
1286  {
1287  TLOG(TLVL_DETACH) << "Detach: Resetting owned buffers";
1288  auto bufs = GetBuffersOwnedByManager(false);
1289  for (auto buf : bufs)
1290  {
1291  auto shmBuf = getBufferInfo_(buf);
1292  if (shmBuf == nullptr)
1293  {
1294  continue;
1295  }
1296  if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1297  {
1298  shmBuf->sem = BufferSemaphoreFlags::Empty;
1299  }
1300  else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1301  {
1302  shmBuf->sem = BufferSemaphoreFlags::Full;
1303  }
1304  shmBuf->sem_id = -1;
1305  }
1306  }
1307 
1308  if (shm_ptr_ != nullptr)
1309  {
1310  TLOG(TLVL_DETACH) << "Detach: Detaching shared memory";
1311  shmdt(shm_ptr_);
1312  shm_ptr_ = nullptr;
1313  }
1314 
1315  if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1316  {
1317  TLOG(TLVL_DETACH) << "Detach: Marking Shared memory for removal";
1318  shmctl(shm_segment_id_, IPC_RMID, nullptr);
1319  shm_segment_id_ = -1;
1320  }
1321 
1322  // Reset manager_id_
1323  manager_id_ = -1;
1324 
1325  if (!category.empty() && !message.empty())
1326  {
1327  TLOG(TLVL_ERROR) << category << ": " << message;
1328 
1329  if (throwException)
1330  {
1331  throw cet::exception(category) << message; // NOLINT(cert-err60-cpp)
1332  }
1333  }
1334 }
1335 
1336 // Local Variables:
1337 // mode: c++
1338 // 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, 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.