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