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