artdaq_core  3.09.12
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  if (!registered_reader_)
293  {
294  shm_ptr_->reader_count++;
295  registered_reader_ = true;
296  }
297 
298  std::lock_guard<std::mutex> lk(search_mutex_);
299  // TraceLock lk(search_mutex_, 11, "GetBufferForReadingSearch");
300  auto rp = shm_ptr_->reader_pos.load();
301 
302  TLOG(TLVL_GETBUFFER) << "GetBufferForReading lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
303 
304  for (int retry = 0; retry < 5; retry++)
305  {
307  int16_t sem_id = -2;
308  int buffer_num = -1;
309  ShmBuffer* buffer_ptr = nullptr;
310  uint64_t seqID = -1;
311 
312  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
313  {
314  auto buffer = (ii + rp) % shm_ptr_->buffer_count;
315 
316  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForReading Checking if buffer " << buffer << " is stale. Shm destructive_read_mode=" << shm_ptr_->destructive_read_mode;
317  ResetBuffer(buffer);
318 
319  auto buf = getBufferInfo_(buffer);
320  if (buf == nullptr)
321  {
322  continue;
323  }
324 
325  sem = buf->sem.load();
326  sem_id = buf->sem_id.load();
327 
328  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForReading: Buffer " << buffer << ": sem=" << FlagToString(sem)
329  << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << sem_id << ", seq_id=" << buf->sequence_id << " )";
330  if (sem == BufferSemaphoreFlags::Full && (sem_id == -1 || sem_id == manager_id_) && (shm_ptr_->destructive_read_mode || buf->sequence_id > last_seen_id_))
331  {
332  if (buf->sequence_id < seqID)
333  {
334  buffer_ptr = buf;
335  seqID = buf->sequence_id;
336  buffer_num = buffer;
337  touchBuffer_(buf);
338  if (seqID == last_seen_id_ + shm_ptr_->reader_count)
339  {
340  break;
341  }
342  }
343  }
344  }
345 
346  if (buffer_ptr != nullptr)
347  {
348  sem = buffer_ptr->sem.load();
349  sem_id = buffer_ptr->sem_id.load();
350  seqID = buffer_ptr->sequence_id.load();
351  }
352 
353  TLOG(TLVL_GETBUFFER + 2) << "GetBufferForReading: Mode: " << std::boolalpha << shm_ptr_->destructive_read_mode << ", seqID: " << seqID << ", last_seen_id_: " << last_seen_id_ << ", reader_count: " << shm_ptr_->reader_count;
354  // Round-robin to readers, but check for left-behind buffers
355  if(shm_ptr_->destructive_read_mode && last_seen_id_ > 0 && seqID != last_seen_id_ + shm_ptr_->reader_count && seqID > last_seen_id_ - shm_ptr_->reader_count ){
356  TLOG(TLVL_GETBUFFER + 2) << "GetBufferForReading: Skipping due to seqID check";
357  continue;
358  }
359  TLOG(TLVL_GETBUFFER + 2) << "GetBufferForReading: After seqID check";
360 
361  if ((buffer_ptr == nullptr) || (sem_id != -1 && sem_id != manager_id_) || sem != BufferSemaphoreFlags::Full)
362  {
363  continue;
364  }
365 
366  if (buffer_num >= 0)
367  {
368  TLOG(TLVL_GETBUFFER) << "GetBufferForReading Found buffer " << buffer_num;
369  touchBuffer_(buffer_ptr);
370  if (!buffer_ptr->sem_id.compare_exchange_strong(sem_id, manager_id_))
371  {
372  continue;
373  }
374  if (!buffer_ptr->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Reading))
375  {
376  continue;
377  }
378  if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading, false))
379  {
380  TLOG(TLVL_GETBUFFER) << "GetBufferForReading: Failed to acquire buffer " << buffer_num << " (someone else changed manager ID while I was changing sem)";
381  continue;
382  }
383  buffer_ptr->readPos = 0;
384  touchBuffer_(buffer_ptr);
385  if (!checkBuffer_(buffer_ptr, BufferSemaphoreFlags::Reading, false))
386  {
387  TLOG(TLVL_GETBUFFER) << "GetBufferForReading: Failed to acquire buffer " << buffer_num << " (someone else changed manager ID while I was touching buffer SHOULD NOT HAPPEN!)";
388  continue;
389  }
390  if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
391  {
392  shm_ptr_->lowest_seq_id_read = seqID;
393  }
394  last_seen_id_ = seqID;
395  if (shm_ptr_->destructive_read_mode)
396  {
397  shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
398  }
399 
400  TLOG(TLVL_GETBUFFER) << "GetBufferForReading returning " << buffer_num;
401  return buffer_num;
402  }
403  retry = 5;
404  }
405 
406  TLOG(TLVL_GETBUFFER) << "GetBufferForReading returning -1 because no buffers are ready";
407  return -1;
408 }
409 
411 {
412  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting BEGIN, overwrite=" << (overwrite ? "true" : "false");
413 
414  if (!registered_writer_)
415  {
416  shm_ptr_->writer_count++;
417  registered_writer_ = true;
418  }
419 
420  std::lock_guard<std::mutex> lk(search_mutex_);
421  // TraceLock lk(search_mutex_, 12, "GetBufferForWritingSearch");
422  auto wp = shm_ptr_->writer_pos.load();
423 
424  TLOG(TLVL_GETBUFFER) << "GetBufferForWriting lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
425 
426  // First, only look for "Empty" buffers
427  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
428  {
429  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
430 
431  ResetBuffer(buffer);
432 
433  auto buf = getBufferInfo_(buffer);
434  if (buf == nullptr)
435  {
436  continue;
437  }
438 
439  auto sem = buf->sem.load();
440  auto sem_id = buf->sem_id.load();
441 
442  if (sem == BufferSemaphoreFlags::Empty && sem_id == -1)
443  {
444  touchBuffer_(buf);
445  if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
446  {
447  continue;
448  }
449  if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
450  {
451  continue;
452  }
453  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
454  {
455  continue;
456  }
457  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
458  buf->sequence_id = ++shm_ptr_->next_sequence_id;
459  buf->writePos = 0;
460  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
461  {
462  continue;
463  }
464  touchBuffer_(buf);
465  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting returning empty buffer " << buffer;
466  return buffer;
467  }
468  }
469 
470  if (overwrite)
471  {
472  // Then, look for "Full" buffers
473  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
474  {
475  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
476 
477  ResetBuffer(buffer);
478 
479  auto buf = getBufferInfo_(buffer);
480  if (buf == nullptr)
481  {
482  continue;
483  }
484 
485  auto sem = buf->sem.load();
486  auto sem_id = buf->sem_id.load();
487 
488  if (sem == BufferSemaphoreFlags::Full)
489  {
490  touchBuffer_(buf);
491  if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
492  {
493  continue;
494  }
495  if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
496  {
497  continue;
498  }
499  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
500  {
501  continue;
502  }
503  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
504  buf->sequence_id = ++shm_ptr_->next_sequence_id;
505  buf->writePos = 0;
506  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
507  {
508  continue;
509  }
510  touchBuffer_(buf);
511  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting returning full buffer (overwrite mode) " << buffer;
512  return buffer;
513  }
514  }
515 
516  // Finally, if we still haven't found a buffer, we have to clobber a reader...
517  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
518  {
519  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
520 
521  ResetBuffer(buffer);
522 
523  auto buf = getBufferInfo_(buffer);
524  if (buf == nullptr)
525  {
526  continue;
527  }
528 
529  auto sem = buf->sem.load();
530  auto sem_id = buf->sem_id.load();
531 
532  if (sem == BufferSemaphoreFlags::Reading)
533  {
534  touchBuffer_(buf);
535  if (!buf->sem_id.compare_exchange_strong(sem_id, manager_id_))
536  {
537  continue;
538  }
539  if (!buf->sem.compare_exchange_strong(sem, BufferSemaphoreFlags::Writing))
540  {
541  continue;
542  }
543  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
544  {
545  continue;
546  }
547  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
548  buf->sequence_id = ++shm_ptr_->next_sequence_id;
549  buf->writePos = 0;
550  if (!checkBuffer_(buf, BufferSemaphoreFlags::Writing, false))
551  {
552  continue;
553  }
554  touchBuffer_(buf);
555  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting clobbering reader on buffer " << buffer << " (overwrite mode)";
556  return buffer;
557  }
558  }
559  }
560  TLOG(TLVL_GETBUFFER + 1) << "GetBufferForWriting Returning -1 because no buffers are ready";
561  return -1;
562 }
563 
565 {
566  if (!IsValid())
567  {
568  return 0;
569  }
570  TLOG(TLVL_READREADY) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN" << std::dec;
571  std::unique_lock<std::mutex> lk(search_mutex_);
572  TLOG(TLVL_READREADY) << "ReadReadyCount lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
573  // TraceLock lk(search_mutex_, 14, "ReadReadyCountSearch");
574  size_t count = 0;
575  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
576  {
577 #ifndef __OPTIMIZE__
578  TLOG(TLVL_READREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " ReadReadyCount: Checking if buffer " << ii << " is stale.";
579 #endif
580  ResetBuffer(ii);
581  auto buf = getBufferInfo_(ii);
582  if (buf == nullptr)
583  {
584  continue;
585  }
586 
587 #ifndef __OPTIMIZE__
588  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 << " )";
589 #endif
590  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_))
591  {
592 #ifndef __OPTIMIZE__
593  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.";
594 #endif
595  touchBuffer_(buf);
596  ++count;
597  }
598  }
599  return count;
600 }
601 
603 {
604  if (!IsValid())
605  {
606  return 0;
607  }
608  TLOG(TLVL_WRITEREADY) << "0x" << std::hex << shm_key_ << " ReadReadyCount BEGIN" << std::dec;
609  std::unique_lock<std::mutex> lk(search_mutex_);
610  // TraceLock lk(search_mutex_, 15, "WriteReadyCountSearch");
611  TLOG(TLVL_WRITEREADY) << "WriteReadyCount(" << overwrite << ") lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
612  size_t count = 0;
613  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
614  {
615  // ELF, 3/19/2019: This TRACE call is a major performance hit with many buffers
616 #ifndef __OPTIMIZE__
617  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " WriteReadyCount: Checking if buffer " << ii << " is stale.";
618 #endif
619  ResetBuffer(ii);
620  auto buf = getBufferInfo_(ii);
621  if (buf == nullptr)
622  {
623  continue;
624  }
625  if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
626  {
627 #ifndef __OPTIMIZE__
628  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " WriteReadyCount: Buffer " << ii << " is either empty or is available for overwrite.";
629 #endif
630  ++count;
631  }
632  }
633  return count;
634 }
635 
637 {
638  if (!IsValid())
639  {
640  return false;
641  }
642  TLOG(TLVL_READREADY) << "0x" << std::hex << shm_key_ << " ReadyForRead BEGIN" << std::dec;
643  std::unique_lock<std::mutex> lk(search_mutex_);
644  // TraceLock lk(search_mutex_, 14, "ReadyForReadSearch");
645 
646  auto rp = shm_ptr_->reader_pos.load();
647 
648  TLOG(TLVL_READREADY) << "ReadyForRead lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
649 
650  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
651  {
652  auto buffer = (rp + ii) % shm_ptr_->buffer_count;
653 
654 #ifndef __OPTIMIZE__
655  TLOG(TLVL_READREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForRead: Checking if buffer " << buffer << " is stale.";
656 #endif
657  ResetBuffer(buffer);
658  auto buf = getBufferInfo_(buffer);
659  if (buf == nullptr)
660  {
661  continue;
662  }
663 
664 #ifndef __OPTIMIZE__
665  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 << " )"
666  << " seq_id=" << buf->sequence_id << " >? " << last_seen_id_;
667 #endif
668 
669  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_))
670  {
671  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.";
672  touchBuffer_(buf);
673  return true;
674  }
675  }
676  return false;
677 }
678 
680 {
681  if (!IsValid())
682  {
683  return false;
684  }
685  TLOG(TLVL_WRITEREADY) << "0x" << std::hex << shm_key_ << " ReadyForWrite BEGIN" << std::dec;
686 
687  std::lock_guard<std::mutex> lk(search_mutex_);
688  // TraceLock lk(search_mutex_, 15, "ReadyForWriteSearch");
689 
690  auto wp = shm_ptr_->writer_pos.load();
691 
692  TLOG(TLVL_WRITEREADY) << "ReadyForWrite lock acquired, scanning " << shm_ptr_->buffer_count << " buffers";
693 
694  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
695  {
696  auto buffer = (wp + ii) % shm_ptr_->buffer_count;
697  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_ << std::dec << " ReadyForWrite: Checking if buffer " << buffer << " is stale.";
698  ResetBuffer(buffer);
699  auto buf = getBufferInfo_(buffer);
700  if (buf == nullptr)
701  {
702  continue;
703  }
704  if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1) || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
705  {
706  TLOG(TLVL_WRITEREADY + 1) << "0x" << std::hex << shm_key_
707  << std::dec
708  << " WriteReadyCount: Buffer " << ii << " is either empty or available for overwrite.";
709  return true;
710  }
711  }
712  return false;
713 }
714 
716 {
717  std::deque<int> output;
718  size_t buffer_count = size();
719  if (!IsValid() || buffer_count == 0)
720  {
721  return output;
722  }
723  TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager BEGIN. Locked? " << locked;
724  if (locked)
725  {
726  TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtaining search_mutex";
727  std::lock_guard<std::mutex> lk(search_mutex_);
728  TLOG(TLVL_BUFLCK) << "GetBuffersOwnedByManager obtained search_mutex";
729  // TraceLock lk(search_mutex_, 16, "GetOwnedSearch");
730  for (size_t ii = 0; ii < buffer_count; ++ii)
731  {
732  auto buf = getBufferInfo_(ii);
733  if (buf == nullptr)
734  {
735  continue;
736  }
737  if (buf->sem_id == manager_id_)
738  {
739  output.push_back(ii);
740  }
741  }
742  }
743  else
744  {
745  for (size_t ii = 0; ii < buffer_count; ++ii)
746  {
747  auto buf = getBufferInfo_(ii);
748  if (buf == nullptr)
749  {
750  continue;
751  }
752  if (buf->sem_id == manager_id_)
753  {
754  output.push_back(ii);
755  }
756  }
757  }
758 
759  TLOG(TLVL_BUFFER) << "GetBuffersOwnedByManager: own " << output.size() << " / " << buffer_count << " buffers.";
760  return output;
761 }
762 
764 {
765  TLOG(TLVL_BUFFER) << "BufferDataSize(" << buffer << ") called.";
766 
767  if (!shm_ptr_ || buffer >= shm_ptr_->buffer_count)
768  {
769  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
770  }
771 
772  TLOG(TLVL_BUFLCK) << "BufferDataSize obtaining buffer_mutex for buffer " << buffer;
773  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
774  TLOG(TLVL_BUFLCK) << "BufferDataSize obtained buffer_mutex for buffer " << buffer;
775  // TraceLock lk(buffer_mutexes_[buffer], 17, "DataSizeBuffer" + std::to_string(buffer));
776 
777  auto buf = getBufferInfo_(buffer);
778  if (buf == nullptr)
779  {
780  return 0;
781  }
782  touchBuffer_(buf);
783 
784  TLOG(TLVL_BUFFER) << "BufferDataSize: buffer " << buffer << ", size=" << buf->writePos;
785  return buf->writePos;
786 }
787 
789 {
790  TLOG(TLVL_POS) << "ResetReadPos(" << buffer << ") called.";
791 
792  if (buffer >= shm_ptr_->buffer_count)
793  {
794  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
795  }
796 
797  TLOG(TLVL_BUFLCK) << "ResetReadPos obtaining buffer_mutex for buffer " << buffer;
798  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
799  TLOG(TLVL_BUFLCK) << "ResetReadPos obtained buffer_mutex for buffer " << buffer;
800 
801  // TraceLock lk(buffer_mutexes_[buffer], 18, "ResetReadPosBuffer" + std::to_string(buffer));
802  auto buf = getBufferInfo_(buffer);
803  if ((buf == nullptr) || buf->sem_id != manager_id_)
804  {
805  return;
806  }
807  touchBuffer_(buf);
808  buf->readPos = 0;
809 
810  TLOG(TLVL_POS) << "ResetReadPos(" << buffer << ") ended.";
811 }
812 
814 {
815  TLOG(TLVL_POS + 1) << "ResetWritePos(" << buffer << ") called.";
816 
817  if (buffer >= shm_ptr_->buffer_count)
818  {
819  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
820  }
821 
822  TLOG(TLVL_BUFLCK) << "ResetWritePos obtaining buffer_mutex for buffer " << buffer;
823  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
824  TLOG(TLVL_BUFLCK) << "ResetWritePos obtained buffer_mutex for buffer " << buffer;
825 
826  // TraceLock lk(buffer_mutexes_[buffer], 18, "ResetWritePosBuffer" + std::to_string(buffer));
827  auto buf = getBufferInfo_(buffer);
828  if (buf == nullptr)
829  {
830  return;
831  }
832  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
833  touchBuffer_(buf);
834  buf->writePos = 0;
835 
836  TLOG(TLVL_POS + 1) << "ResetWritePos(" << buffer << ") ended.";
837 }
838 
840 {
841  TLOG(TLVL_POS) << "IncrementReadPos called: buffer= " << buffer << ", bytes to read=" << read;
842 
843  if (buffer >= shm_ptr_->buffer_count)
844  {
845  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
846  }
847 
848  TLOG(TLVL_BUFLCK) << "IncrementReadPos obtaining buffer_mutex for buffer " << buffer;
849  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
850  TLOG(TLVL_BUFLCK) << "IncrementReadPos obtained buffer_mutex for buffer " << buffer;
851  // TraceLock lk(buffer_mutexes_[buffer], 19, "IncReadPosBuffer" + std::to_string(buffer));
852  auto buf = getBufferInfo_(buffer);
853  if ((buf == nullptr) || buf->sem_id != manager_id_)
854  {
855  return;
856  }
857  touchBuffer_(buf);
858  TLOG(TLVL_POS) << "IncrementReadPos: buffer= " << buffer << ", readPos=" << buf->readPos << ", bytes read=" << read;
859  buf->readPos = buf->readPos + read;
860  TLOG(TLVL_POS) << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << buf->readPos;
861  if (read == 0)
862  {
863  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) + ")");
864  }
865 }
866 
867 bool artdaq::SharedMemoryManager::IncrementWritePos(int buffer, size_t written)
868 {
869  TLOG(TLVL_POS + 1) << "IncrementWritePos called: buffer= " << buffer << ", bytes written=" << written;
870 
871  if (buffer >= shm_ptr_->buffer_count)
872  {
873  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
874  }
875 
876  TLOG(TLVL_BUFLCK) << "IncrementWritePos obtaining buffer_mutex for buffer " << buffer;
877  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
878  TLOG(TLVL_BUFLCK) << "IncrementWritePos obtained buffer_mutex for buffer " << buffer;
879  // TraceLock lk(buffer_mutexes_[buffer], 20, "IncWritePosBuffer" + std::to_string(buffer));
880  auto buf = getBufferInfo_(buffer);
881  if (buf == nullptr)
882  {
883  return false;
884  }
885  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
886  touchBuffer_(buf);
887  if (buf->writePos + written > shm_ptr_->buffer_size)
888  {
889  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 << ")";
890  return false;
891  }
892  TLOG(TLVL_POS + 1) << "IncrementWritePos: buffer= " << buffer << ", writePos=" << buf->writePos << ", bytes written=" << written;
893  buf->writePos += written;
894  TLOG(TLVL_POS + 1) << "IncrementWritePos: buffer= " << buffer << ", New writePos is " << buf->writePos;
895  if (written == 0)
896  {
897  Detach(true, "LogicError", "Cannot increment Write pos by 0!");
898  }
899 
900  return true;
901 }
902 
904 {
905  TLOG(TLVL_POS + 2) << "MoreDataInBuffer(" << buffer << ") called.";
906 
907  if (buffer >= shm_ptr_->buffer_count)
908  {
909  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
910  }
911 
912  TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtaining buffer_mutex for buffer " << buffer;
913  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
914  TLOG(TLVL_BUFLCK) << "MoreDataInBuffer obtained buffer_mutex for buffer " << buffer;
915  // TraceLock lk(buffer_mutexes_[buffer], 21, "MoreDataInBuffer" + std::to_string(buffer));
916  auto buf = getBufferInfo_(buffer);
917  if (buf == nullptr)
918  {
919  return false;
920  }
921  TLOG(TLVL_POS + 2) << "MoreDataInBuffer: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", writePos=" << buf->writePos;
922  return buf->readPos < buf->writePos;
923 }
924 
926 {
927  if (buffer >= shm_ptr_->buffer_count)
928  {
929  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
930  }
931 
932  TLOG(TLVL_BUFLCK) << "CheckBuffer obtaining buffer_mutex for buffer " << buffer;
933  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
934  TLOG(TLVL_BUFLCK) << "CheckBuffer obtained buffer_mutex for buffer " << buffer;
935  // TraceLock lk(buffer_mutexes_[buffer], 22, "CheckBuffer" + std::to_string(buffer));
936  return checkBuffer_(getBufferInfo_(buffer), flags, false);
937 }
938 
939 void artdaq::SharedMemoryManager::MarkBufferFull(int buffer, int destination)
940 {
941  if (buffer >= shm_ptr_->buffer_count)
942  {
943  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
944  }
945 
946  TLOG(TLVL_BUFLCK) << "MarkBufferFull obtaining buffer_mutex for buffer " << buffer;
947  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
948  TLOG(TLVL_BUFLCK) << "MarkBufferFull obtained buffer_mutex for buffer " << buffer;
949 
950  // TraceLock lk(buffer_mutexes_[buffer], 23, "FillBuffer" + std::to_string(buffer));
951  auto shmBuf = getBufferInfo_(buffer);
952  if (shmBuf == nullptr)
953  {
954  return;
955  }
956  touchBuffer_(shmBuf);
957  if (shmBuf->sem_id == manager_id_)
958  {
959  if (shmBuf->sem != BufferSemaphoreFlags::Full)
960  {
961  shmBuf->sem = BufferSemaphoreFlags::Full;
962  }
963 
964  shmBuf->sem_id = destination;
965  }
966 }
967 
968 void artdaq::SharedMemoryManager::MarkBufferEmpty(int buffer, bool force, bool detachOnException)
969 {
970  TLOG(TLVL_POS + 3) << "MarkBufferEmpty BEGIN, buffer=" << buffer << ", force=" << force << ", manager_id_=" << manager_id_;
971  if (buffer >= shm_ptr_->buffer_count)
972  {
973  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
974  }
975  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
976  // TraceLock lk(buffer_mutexes_[buffer], 24, "EmptyBuffer" + std::to_string(buffer));
977  auto shmBuf = getBufferInfo_(buffer);
978  if (shmBuf == nullptr)
979  {
980  return;
981  }
982  if (!force)
983  {
984  auto ret = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, detachOnException);
985  if (!ret) return;
986  }
987  touchBuffer_(shmBuf);
988 
989  shmBuf->readPos = 0;
990  shmBuf->sem = BufferSemaphoreFlags::Full;
991 
992  if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
993  {
994  TLOG(TLVL_POS + 3) << "MarkBufferEmpty Resetting buffer " << buffer << " to Empty state";
995  shmBuf->writePos = 0;
996  shmBuf->sem = BufferSemaphoreFlags::Empty;
997  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
998  {
999  TLOG(TLVL_POS + 3) << "MarkBufferEmpty Broadcast mode; incrementing reader_pos from " << shm_ptr_->reader_pos << " to " << (buffer + 1) % shm_ptr_->buffer_count;
1000  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1001  }
1002  }
1003  shmBuf->sem_id = -1;
1004  TLOG(TLVL_POS + 3) << "MarkBufferEmpty END, buffer=" << buffer << ", force=" << force;
1005 }
1006 
1008 {
1009  if (buffer >= shm_ptr_->buffer_count)
1010  {
1011  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1012  }
1013 
1014  // ELF, 3/19/2019: These TRACE calls are a major performance hit with many buffers.
1015  // TLOG(TLVL_BUFLCK) << "ResetBuffer: obtaining buffer_mutex lock for buffer " << buffer;
1016  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1017  // TLOG(TLVL_BUFLCK) << "ResetBuffer: obtained buffer_mutex lock for buffer " << buffer;
1018 
1019  // TraceLock lk(buffer_mutexes_[buffer], 25, "ResetBuffer" + std::to_string(buffer));
1020  auto shmBuf = getBufferInfo_(buffer);
1021  if (shmBuf == nullptr)
1022  {
1023  return false;
1024  }
1025  /*
1026  if (shmBuf->sequence_id < shm_ptr_->lowest_seq_id_read - size() && shmBuf->sem == BufferSemaphoreFlags::Full)
1027  {
1028  TLOG(TLVL_RESET) << "Buffer " << buffer << " has been passed by all readers, marking Empty" ;
1029  shmBuf->writePos = 0;
1030  shmBuf->sem = BufferSemaphoreFlags::Empty;
1031  shmBuf->sem_id = -1;
1032  return true;
1033  }*/
1034 
1035  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1036  if (delta > 0xFFFFFFFF)
1037  {
1038  TLOG(TLVL_RESET) << "Buffer has touch time in the future, setting it to current time and ignoring...";
1039  shmBuf->last_touch_time = TimeUtils::gettimeofday_us();
1040  return false;
1041  }
1042  if (shm_ptr_->buffer_timeout_us == 0 || delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty)
1043  {
1044  return false;
1045  }
1046  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;
1047 
1048  if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
1049  {
1050  return true;
1051  }
1052 
1053  if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full && manager_id_ == 0)
1054  {
1055  TLOG(TLVL_RESET) << "Resetting old broadcast mode buffer " << buffer << " (seqid=" << shmBuf->sequence_id << "). State: Full-->Empty";
1056  shmBuf->writePos = 0;
1057  shmBuf->sem = BufferSemaphoreFlags::Empty;
1058  shmBuf->sem_id = -1;
1059  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer))
1060  {
1061  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
1062  }
1063  return true;
1064  }
1065 
1066  if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
1067  {
1068  // Ron wants to re-check for potential interleave of buffer state updates
1069  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
1070  if (delta <= shm_ptr_->buffer_timeout_us)
1071  {
1072  return false;
1073  }
1074  TLOG(TLVL_WARNING) << "Stale Read buffer " << buffer << " at " << static_cast<void*>(shmBuf)
1075  << " ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! (seqid="
1076  << shmBuf->sequence_id << ") Resetting... Reading-->Full";
1077  shmBuf->readPos = 0;
1078  shmBuf->sem = BufferSemaphoreFlags::Full;
1079  shmBuf->sem_id = -1;
1080  return true;
1081  }
1082  return false;
1083 }
1084 
1086 {
1087  if (!IsValid())
1088  {
1089  return true;
1090  }
1091 
1092  struct shmid_ds info;
1093  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1094  if (sts < 0)
1095  {
1096  TLOG(TLVL_BUFINFO) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1097  return true;
1098  }
1099 
1100  if ((info.shm_perm.mode & SHM_DEST) != 0)
1101  {
1102  TLOG(TLVL_INFO) << "Shared Memory marked for destruction. Probably an end-of-data condition!";
1103  return true;
1104  }
1105 
1106  return false;
1107 }
1108 
1110 {
1111  if (!IsValid())
1112  {
1113  return 0;
1114  }
1115 
1116  struct shmid_ds info;
1117  auto sts = shmctl(shm_segment_id_, IPC_STAT, &info);
1118  if (sts < 0)
1119  {
1120  TLOG(TLVL_BUFINFO) << "Error accessing Shared Memory info: " << errno << " (" << strerror(errno) << ").";
1121  return 0;
1122  }
1123 
1124  return info.shm_nattch;
1125 }
1126 
1127 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
1128 {
1129  TLOG(TLVL_WRITE) << "Write BEGIN";
1130  if (buffer >= shm_ptr_->buffer_count)
1131  {
1132  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1133  }
1134  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1135  // TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer));
1136  auto shmBuf = getBufferInfo_(buffer);
1137  if (shmBuf == nullptr)
1138  {
1139  return -1;
1140  }
1141  checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
1142  touchBuffer_(shmBuf);
1143  TLOG(TLVL_WRITE) << "Buffer Write Pos is " << std::hex << std::showbase << shmBuf->writePos << ", write size is " << size;
1144  if (shmBuf->writePos + size > shm_ptr_->buffer_size)
1145  {
1146  TLOG(TLVL_ERROR) << "Attempted to write more data than fits into Shared Memory, bufferSize=" << std::hex << std::showbase << shm_ptr_->buffer_size
1147  << ",writePos=" << shmBuf->writePos << ",writeSize=" << size;
1148  Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
1149  }
1150 
1151  auto pos = GetWritePos(buffer);
1152  memcpy(pos, data, size);
1153  touchBuffer_(shmBuf);
1154  shmBuf->writePos = shmBuf->writePos + size;
1155 
1156  auto last_seen = last_seen_id_.load();
1157  while (last_seen < shmBuf->sequence_id && !last_seen_id_.compare_exchange_weak(last_seen, shmBuf->sequence_id)) {}
1158 
1159  TLOG(TLVL_WRITE) << "Write END";
1160  return size;
1161 }
1162 
1163 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
1164 {
1165  if (buffer >= shm_ptr_->buffer_count)
1166  {
1167  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
1168  }
1169  std::lock_guard<std::mutex> lk(buffer_mutexes_[buffer]);
1170  // TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer));
1171  auto shmBuf = getBufferInfo_(buffer);
1172  if (shmBuf == nullptr)
1173  {
1174  return false;
1175  }
1176  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
1177  touchBuffer_(shmBuf);
1178  if (shmBuf->readPos + size > shm_ptr_->buffer_size)
1179  {
1180  TLOG(TLVL_ERROR) << "Attempted to read more data than fits into Shared Memory, bufferSize=" << shm_ptr_->buffer_size
1181  << ",readPos=" << shmBuf->readPos << ",readSize=" << size;
1182  Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
1183  }
1184 
1185  auto pos = GetReadPos(buffer);
1186  TLOG(TLVL_READ) << "Before memcpy in Read(), size is " << size;
1187  memcpy(data, pos, size);
1188  TLOG(TLVL_READ) << "After memcpy in Read()";
1189  auto sts = checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
1190  if (sts)
1191  {
1192  shmBuf->readPos += size;
1193  touchBuffer_(shmBuf);
1194  return true;
1195  }
1196  return false;
1197 }
1198 
1200 {
1201  if (shm_ptr_ == nullptr)
1202  {
1203  return "Not connected to shared memory";
1204  }
1205  std::ostringstream ostr;
1206  ostr << "ShmStruct: " << std::endl
1207  << "Reader Position: " << shm_ptr_->reader_pos << std::endl
1208  << "Writer Position: " << shm_ptr_->writer_pos << std::endl
1209  << "Next ID Number: " << shm_ptr_->next_id << std::endl
1210  << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
1211  << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
1212  << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
1213  << "Rank of Writer: " << shm_ptr_->rank << std::endl
1214  << "Number of Writers: " << shm_ptr_->writer_count << std::endl
1215  << "Number of Readers: " << shm_ptr_->reader_count << std::endl
1216  << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::dec << std::endl
1217  << std::endl;
1218 
1219  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
1220  {
1221  auto buf = getBufferInfo_(ii);
1222  if (buf == nullptr)
1223  {
1224  continue;
1225  }
1226 
1227  ostr << "ShmBuffer " << std::dec << ii << std::endl
1228  << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
1229  << "writePos: " << std::to_string(buf->writePos) << std::endl
1230  << "readPos: " << std::to_string(buf->readPos) << std::endl
1231  << "sem: " << FlagToString(buf->sem) << std::endl
1232  << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
1233  << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl
1234  << std::endl;
1235  }
1236 
1237  return ostr.str();
1238 }
1239 
1241 {
1242  auto buf = getBufferInfo_(buffer);
1243  if (buf == nullptr)
1244  {
1245  return nullptr;
1246  }
1247  return bufferStart_(buffer) + buf->readPos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1248 }
1250 {
1251  auto buf = getBufferInfo_(buffer);
1252  if (buf == nullptr)
1253  {
1254  return nullptr;
1255  }
1256  return bufferStart_(buffer) + buf->writePos; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1257 }
1258 
1260 {
1261  return bufferStart_(buffer);
1262 }
1263 
1264 std::vector<std::pair<int, artdaq::SharedMemoryManager::BufferSemaphoreFlags>> artdaq::SharedMemoryManager::GetBufferReport()
1265 {
1266  auto output = std::vector<std::pair<int, BufferSemaphoreFlags>>(size());
1267  for (size_t ii = 0; ii < size(); ++ii)
1268  {
1269  auto buf = getBufferInfo_(ii);
1270  output[ii] = std::make_pair(buf->sem_id.load(), buf->sem.load());
1271  }
1272  return output;
1273 }
1274 
1275 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
1276 {
1277  if (buffer == nullptr)
1278  {
1279  if (exceptions)
1280  {
1281  Detach(true, "BufferNotThereException", "Request to check buffer that does not exist!");
1282  }
1283  return false;
1284  }
1285  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) << ")";
1286  if (exceptions)
1287  {
1288  if (buffer->sem != flags)
1289  {
1290  Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
1291  }
1292  if (buffer->sem_id != manager_id_)
1293  {
1294  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) + ")");
1295  }
1296  }
1297  bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
1298 
1299  if (!ret)
1300  {
1301  TLOG(TLVL_WARNING) << "CheckBuffer detected issue with buffer " << buffer->sequence_id << "!"
1302  << " ID: " << buffer->sem_id << " (Expected " << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (Expected " << FlagToString(flags) << "). "
1303  << R"(ID -1 is okay if expected flag is "Full" or "Empty".)";
1304  }
1305 
1306  return ret;
1307 }
1308 
1309 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
1310 {
1311  if ((buffer == nullptr) || (buffer->sem_id != -1 && buffer->sem_id != manager_id_))
1312  {
1313  return;
1314  }
1315  TLOG(TLVL_CHKBUFFER + 1) << "touchBuffer_: Touching buffer at " << static_cast<void*>(buffer) << " with sequence_id " << buffer->sequence_id;
1316  buffer->last_touch_time = TimeUtils::gettimeofday_us();
1317 }
1318 
1319 void artdaq::SharedMemoryManager::Detach(bool throwException, const std::string& category, const std::string& message, bool force)
1320 {
1321  TLOG(TLVL_DETACH) << "Detach BEGIN: throwException: " << std::boolalpha << throwException << ", force: " << force;
1322  if (IsValid())
1323  {
1324  TLOG(TLVL_DETACH) << "Detach: Resetting owned buffers";
1325  auto bufs = GetBuffersOwnedByManager(false);
1326  for (auto buf : bufs)
1327  {
1328  auto shmBuf = getBufferInfo_(buf);
1329  if (shmBuf == nullptr)
1330  {
1331  continue;
1332  }
1333  if (shmBuf->sem == BufferSemaphoreFlags::Writing)
1334  {
1335  shmBuf->sem = BufferSemaphoreFlags::Empty;
1336  }
1337  else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
1338  {
1339  shmBuf->sem = BufferSemaphoreFlags::Full;
1340  }
1341  shmBuf->sem_id = -1;
1342  }
1343  if(registered_reader_) {
1344  shm_ptr_->reader_count--;
1345  registered_reader_ = false;
1346  }
1347  if(registered_writer_){
1348  shm_ptr_->writer_count--;
1349  registered_writer_ = false;
1350  }
1351  }
1352 
1353  if (shm_ptr_ != nullptr)
1354  {
1355  TLOG(TLVL_DETACH) << "Detach: Detaching shared memory";
1356  shmdt(shm_ptr_);
1357  shm_ptr_ = nullptr;
1358  }
1359 
1360  if ((force || manager_id_ == 0) && shm_segment_id_ > -1)
1361  {
1362  TLOG(TLVL_DETACH) << "Detach: Marking Shared memory for removal";
1363  shmctl(shm_segment_id_, IPC_RMID, nullptr);
1364  shm_segment_id_ = -1;
1365  }
1366 
1367  // Reset manager_id_
1368  manager_id_ = -1;
1369 
1370  if (!category.empty() && !message.empty())
1371  {
1372  TLOG(TLVL_ERROR) << category << ": " << message;
1373 
1374  if (throwException)
1375  {
1376  throw cet::exception(category) << message; // NOLINT(cert-err60-cpp)
1377  }
1378  }
1379 }
1380 
1381 // Local Variables:
1382 // mode: c++
1383 // 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.