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