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