artdaq_core  v3_09_01
SharedMemoryManager.cc
1 #define TRACE_NAME "SharedMemoryManager"
2 #include <sys/ipc.h>
3 #include <sys/shm.h>
4 #include <cstring>
5 #include <list>
6 #include <unordered_map>
7 #ifndef SHM_DEST // Lynn reports that this is missing on Mac OS X?!?
8 #define SHM_DEST 01000
9 #endif
10 #include <csignal>
11 #include "artdaq-core/Core/SharedMemoryManager.hh"
12 #include "artdaq-core/Utilities/TraceLock.hh"
13 #include "cetlib_except/exception.h"
14 #include "TRACE/tracemf.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 }
986 
988 {
989  if (buffer >= shm_ptr_->buffer_count)
990  {
991  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
992  }
993 
994  // ELF, 3/19/2019: These TRACE calls are a major performance hit with many buffers.
995  //TLOG(TLVL_BUFLCK) << "ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
996  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
997  //TLOG(TLVL_BUFLCK) << "ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
998 
999  //TraceLock lk(buffer_mutexes_[buffer], 25, "ResetBuffer" + std::to_string(buffer));
1000  auto shmBuf = getBufferInfo_(buffer);
1001  if (shmBuf == nullptr)
1002  {
1003  return false;
1004  }
1005  /*
1006  if (shmBuf->sequence_id < shm_ptr_->lowest_seq_id_read - size() && shmBuf->sem == BufferSemaphoreFlags::Full)
1007  {
1008  TLOG(TLVL_RESET) << "Buffer " << buffer << " has been passed by all readers, marking Empty" ;
1009  shmBuf->writePos = 0;
1010  shmBuf->sem = BufferSemaphoreFlags::Empty;
1011  shmBuf->sem_id = -1;
1012  return true;
1013  }*/
1014 
1015  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1016  if (delta > 0xFFFFFFFF)
1017  {
1018  TLOG(TLVL_RESET) << "Buffer has touch time in the future, setting it to current time and ignoring...";
1019  shmBuf->last_touch_time = TimeUtils::gettimeofday_us();
1020  return false;
1021  }
1022  if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1023  {
1024  return false;
1025  }
1026  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;
1027 
1028  if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1029  {
1030  return true;
1031  }
1032 
1033  if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1034  {
1035  TLOG(TLVL_RESET) << "Resetting old broadcast mode buffer " << buffer << " (seqid=" << shmBuf->sequence_id << "). State: Full-->Empty";
1036  shmBuf->writePos = 0;
1037  shmBuf->sem = BufferSemaphoreFlags::Empty;
1038  shmBuf->sem_id = -1;
1039  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1040  {
1041  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1042  }
1043  return true;
1044  }
1045 
1046  if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1047  {
1048  // Ron wants to re-check for potential interleave of buffer state updates
1049  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1050  if (delta <= shm_ptr_->buffer_timeout_us)
1051  {
1052  return false;
1053  }
1054  TLOG(TLVL_WARNING) << "Stale Read buffer " << buffer << " at " << static_cast<void*>(shmBuf)
1055  << " ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! (seqid="
1056  << shmBuf->sequence_id << ") Resetting... Reading-->Full";
1057  shmBuf->readPos = 0;
1058  shmBuf->sem = BufferSemaphoreFlags::Full;
1059  shmBuf->sem_id = -1;
1060  return true;
1061  }
1062  return false;
1063 }
1064 
1066 {
1067  if (!IsValid())
1068  {
1069  return true;
1070  }
1071 
1072  struct shmid_ds info;
1073  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1074  if (sts < 0)
1075  {
1076  TLOG(TLVL_BUFINFO) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1077  return true;
1078  }
1079 
1080  if ((info.shm_perm.mode & SHM_DEST) != 0)
1081  {
1082  TLOG(TLVL_INFO) << "Shared Memory marked for destruction. Probably an end-of-data condition!";
1083  return true;
1084  }
1085 
1086  return false;
1087 }
1088 
1090 {
1091  if (!IsValid())
1092  {
1093  return 0;
1094  }
1095 
1096  struct shmid_ds info;
1097  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1098  if (sts < 0)
1099  {
1100  TLOG(TLVL_BUFINFO) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1101  return 0;
1102  }
1103 
1104  return info.shm_nattch;
1105 }
1106 
1107 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
1108 {
1109  TLOG(TLVL_WRITE) << "Write BEGIN";
1110  if (buffer >= shm_ptr_->buffer_count)
1111  {
1112  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1113  }
1114  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1115  //TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer));
1116  auto shmBuf = getBufferInfo_(buffer);
1117  if (shmBuf == nullptr)
1118  {
1119  return -1;
1120  }
1121  checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1122  touchBuffer_(shmBuf);
1123  TLOG(TLVL_WRITE) << "Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos << ", write size is " << size;
1124  if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1125  {
1126  TLOG(TLVL_ERROR) << "Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1127  << ",writePos=" << shmBuf->writePos << ",writeSize=" << size;
1128  Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1129  }
1130 
1131  auto pos = GetWritePos(buffer);
1132  memcpy(pos, data, size);
1133  touchBuffer_(shmBuf);
1134  shmBuf->writePos = shmBuf->writePos + size;
1135 
1136  auto last_seen = last_seen_id_.load();
1137  while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1138 
1139  TLOG(TLVL_WRITE) << "Write END";
1140  return size;
1141 }
1142 
1143 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
1144 {
1145  if (buffer >= shm_ptr_->buffer_count)
1146  {
1147  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1148  }
1149  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1150  //TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer));
1151  auto shmBuf = getBufferInfo_(buffer);
1152  if (shmBuf == nullptr)
1153  {
1154  return false;
1155  }
1156  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1157  touchBuffer_(shmBuf);
1158  if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1159  {
1160  TLOG(TLVL_ERROR) << "Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1161  << ",readPos=" << shmBuf->readPos << ",readSize=" << size;
1162  Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
1163  }
1164 
1165  auto pos = GetReadPos(buffer);
1166  TLOG(TLVL_READ) << "Before memcpy in Read(), size is " << size;
1167  memcpy(data, pos, size);
1168  TLOG(TLVL_READ) << "After memcpy in Read()";
1169  auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
1170  if (sts)
1171  {
1172  shmBuf->readPos += size;
1173  touchBuffer_(shmBuf);
1174  return true;
1175  }
1176  return false;
1177 }
1178 
1180 {
1181  std::ostringstream ostr;
1182  ostr << "ShmStruct: " << std::endl
1183  << "Reader Position: " << shm_ptr_->reader_pos << std::endl
1184  << "Writer Position: " << shm_ptr_->writer_pos << std::endl
1185  << "Next ID Number: " << shm_ptr_->next_id << std::endl
1186  << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
1187  << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
1188  << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1189  << "Rank of Writer: " << shm_ptr_->rank << std::endl
1190  << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1191  << std::endl;
1192 
1193  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1194  {
1195  auto buf = getBufferInfo_(ii);
1196  if (buf == nullptr)
1197  {
1198  continue;
1199  }
1200 
1201  ostr << "ShmBuffer " << std::dec << ii << std::endl
1202  << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1203  << "writePos: " << std::to_string(buf->writePos) << std::endl
1204  << "readPos: " << std::to_string(buf->readPos) << std::endl
1205  << "sem: " << FlagToString(buf->sem) << std::endl
1206  << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1207  << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1208  << std::endl;
1209  }
1210 
1211  return ostr.str();
1212 }
1213 
1215 {
1216  auto buf = getBufferInfo_(buffer);
1217  if (buf == nullptr)
1218  {
1219  return nullptr;
1220  }
1221  return bufferStart_(buffer) + buf->readPos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1222 }
1224 {
1225  auto buf = getBufferInfo_(buffer);
1226  if (buf == nullptr)
1227  {
1228  return nullptr;
1229  }
1230  return bufferStart_(buffer) + buf->writePos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1231 }
1232 
1234 {
1235  return bufferStart_(buffer);
1236 }
1237 
1238 std::vector<std::pair<int, artdaq::SharedMemoryManager::BufferSemaphoreFlags>> artdaq::SharedMemoryManager::GetBufferReport()
1239 {
1240  auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1241  for (size_t ii = 0; ii < size(); ++ii)
1242  {
1243  auto buf = getBufferInfo_(ii);
1244  output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1245  }
1246  return output;
1247 }
1248 
1249 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
1250 {
1251  if (buffer == nullptr)
1252  {
1253  if (exceptions)
1254  {
1255  Detach(true, "BufferNotThereException", "Request to check buffer that does not exist!");
1256  }
1257  return false;
1258  }
1259  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) << ")";
1260  if (exceptions)
1261  {
1262  if (buffer->sem != flags)
1263  {
1264  Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
1265  }
1266  if (buffer->sem_id != manager_id_)
1267  {
1268  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) + ")");
1269  }
1270  }
1271  bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1272 
1273  if (!ret)
1274  {
1275  TLOG(TLVL_WARNING) << "CheckBuffer detected issue with buffer " << buffer->sequence_id << "!"
1276  << " ID: " << buffer->sem_id << " (Expected " << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (Expected " << FlagToString(flags) << "). "
1277  << R"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1278  }
1279 
1280  return ret;
1281 }
1282 
1283 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1284 {
1285  if ((buffer == nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1286  {
1287  return;
1288  }
1289  TLOG(TLVL_CHKBUFFER + 1) << "touchBuffer_: Touching buffer at " << static_cast<void*>(buffer) << " with sequence_id " << buffer->sequence_id;
1290  buffer->last_touch_time = TimeUtils::gettimeofday_us();
1291 }
1292 
1293 void artdaq::SharedMemoryManager::Detach(bool throwException, const std::string& category, const std::string& message, bool force)
1294 {
1295  TLOG(TLVL_DETACH) << "Detach BEGIN: throwException: " << std::boolalpha << throwException << ", force: " << force;
1296  if (IsValid())
1297  {
1298  TLOG(TLVL_DETACH) << "Detach: Resetting owned buffers";
1299  auto bufs = GetBuffersOwnedByManager(false);
1300  for (auto buf : bufs)
1301  {
1302  auto shmBuf = getBufferInfo_(buf);
1303  if (shmBuf == nullptr)
1304  {
1305  continue;
1306  }
1307  if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1308  {
1309  shmBuf->sem = BufferSemaphoreFlags::Empty;
1310  }
1311  else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1312  {
1313  shmBuf->sem = BufferSemaphoreFlags::Full;
1314  }
1315  shmBuf->sem_id = -1;
1316  }
1317  }
1318 
1319  if (shm_ptr_ != nullptr)
1320  {
1321  TLOG(TLVL_DETACH) << "Detach: Detaching shared memory";
1322  shmdt(shm_ptr_);
1323  shm_ptr_ = nullptr;
1324  }
1325 
1326  if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1327  {
1328  TLOG(TLVL_DETACH) << "Detach: Marking Shared memory for removal";
1329  shmctl(shm_segment_id_, IPC_RMID, nullptr);
1330  shm_segment_id_ = -1;
1331  }
1332 
1333  // Reset manager_id_
1334  manager_id_ = -1;
1335 
1336  if (!category.empty() && !message.empty())
1337  {
1338  TLOG(TLVL_ERROR) << category << ": " << message;
1339 
1340  if (throwException)
1341  {
1342  throw cet::exception(category) << message; // NOLINT(cert-err60-cpp)
1343  }
1344  }
1345 }
1346 
1347 // Local Variables:
1348 // mode: c++
1349 // 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.