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