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