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