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