artdaq_core  v3_01_02
SharedMemoryManager.cc
1 #include <sys/shm.h>
2 #include "tracemf.h"
3 #include "cetlib_except/exception.h"
4 #include "artdaq-core/Core/SharedMemoryManager.hh"
5 #include "artdaq-core/Utilities/TraceLock.hh"
6 
7 artdaq::SharedMemoryManager::SharedMemoryManager(uint32_t shm_key, size_t buffer_count, size_t buffer_size, uint64_t buffer_timeout_us, bool destructive_read_mode)
8  : shm_segment_id_(-1)
9  , shm_ptr_(NULL)
10  , shm_key_(shm_key)
11  , manager_id_(-1)
12  , buffer_mutexes_()
13  , last_seen_id_(0)
14 {
15  requested_shm_parameters_.buffer_count = buffer_count;
16  requested_shm_parameters_.buffer_size = buffer_size;
17  requested_shm_parameters_.buffer_timeout_us = buffer_timeout_us;
18  requested_shm_parameters_.destructive_read_mode = destructive_read_mode;
19 
20  Attach();
21 }
22 
24 {
25  TLOG_ARB(5, "SharedMemoryManager") << "~SharedMemoryManager called" << TLOG_ENDL;
26  Detach();
27  TLOG_ARB(5, "SharedMemoryManager") << "~SharedMemoryManager done" << TLOG_ENDL;
28 }
29 
31 {
32  if (IsValid())
33  {
34  if (manager_id_ == 0) return;
35  Detach();
36  }
37  auto start_time = std::chrono::steady_clock::now();
38  last_seen_id_ = 0;
39  size_t shmSize = requested_shm_parameters_.buffer_count * (requested_shm_parameters_.buffer_size + sizeof(ShmBuffer)) + sizeof(ShmStruct);
40 
41  shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
42  if (shm_segment_id_ == -1 && requested_shm_parameters_.buffer_count > 0 && manager_id_ <= 0)
43  {
44  TLOG_DEBUG("SharedMemoryManager") << "Creating shared memory segment with key 0x" << std::hex << shm_key_ << TLOG_ENDL;
45  shm_segment_id_ = shmget(shm_key_, shmSize, IPC_CREAT | 0666);
46  manager_id_ = 0;
47  }
48 
49  while (shm_segment_id_ == -1 && TimeUtils::GetElapsedTimeMilliseconds(start_time) < 1000)
50  {
51  shm_segment_id_ = shmget(shm_key_, shmSize, 0666);
52 
53  }
54  TLOG_DEBUG("SharedMemoryManager") << "shm_key == 0x" << std::hex << shm_key_ << ", shm_segment_id == " << shm_segment_id_ << TLOG_ENDL;
55 
56  if (shm_segment_id_ > -1)
57  {
58  TLOG_DEBUG("SharedMemoryManager")
59  << "Attached to shared memory segment with ID = " << shm_segment_id_
60  << " and size " << shmSize
61  << " bytes" << TLOG_ENDL;
62  shm_ptr_ = (ShmStruct*)shmat(shm_segment_id_, 0, 0);
63  TLOG_DEBUG("SharedMemoryManager")
64  << "Attached to shared memory segment at address "
65  << std::hex << (void*)shm_ptr_ << std::dec << TLOG_ENDL;
66  if (shm_ptr_ && shm_ptr_ != (void *)-1)
67  {
68  if (manager_id_ == 0)
69  {
70  if (shm_ptr_->ready_magic == 0xCAFE1111)
71  {
72  TLOG_ERROR("SharedMemoryManager") << "Owner encountered already-initialized Shared Memory!" << TLOG_ENDL;
73  exit(-2);
74  }
75  TLOG_DEBUG("SharedMemoryManager") << "Owner initializing Shared Memory" << TLOG_ENDL;
76  shm_ptr_->next_id = 1;
77  shm_ptr_->next_sequence_id = 0;
78  shm_ptr_->reader_pos = 0;
79  shm_ptr_->writer_pos = 0;
80  shm_ptr_->buffer_size = requested_shm_parameters_.buffer_size;
81  shm_ptr_->buffer_count = requested_shm_parameters_.buffer_count;
82  shm_ptr_->buffer_timeout_us = requested_shm_parameters_.buffer_timeout_us;
83  shm_ptr_->destructive_read_mode = requested_shm_parameters_.destructive_read_mode;
84 
85  for (int ii = 0; ii < static_cast<int>(requested_shm_parameters_.buffer_count); ++ii)
86  {
87  getBufferInfo_(ii)->writePos = 0;
88  getBufferInfo_(ii)->readPos = 0;
89  getBufferInfo_(ii)->sem = BufferSemaphoreFlags::Empty;
90  getBufferInfo_(ii)->sem_id = -1;
91  getBufferInfo_(ii)->last_touch_time = TimeUtils::gettimeofday_us();
92  }
93 
94  shm_ptr_->ready_magic = 0xCAFE1111;
95  }
96  else
97  {
98  TLOG_DEBUG("SharedMemoryManager") << "Waiting for owner to initalize Shared Memory" << TLOG_ENDL;
99  while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
100  TLOG_DEBUG("SharedMemoryManager") << "Getting ID from Shared Memory" << TLOG_ENDL;
101  GetNewId();
102  shm_ptr_->lowest_seq_id_read = 0;
103  TLOG_DEBUG("SharedMemoryManager") << "Getting Shared Memory Size parameters" << TLOG_ENDL;
104  }
105  //last_seen_id_ = shm_ptr_->next_sequence_id;
106  TLOG_DEBUG("SharedMemoryManager") << "Initialization Complete: "
107  << "key: 0x" << std::hex << shm_key_
108  << ", manager ID: " << manager_id_
109  << ", Buffer size: " << std::to_string(shm_ptr_->buffer_size)
110  << ", Buffer count: " << std::to_string(shm_ptr_->buffer_count) << TLOG_ENDL;
111  return;
112  }
113  else
114  {
115  TLOG_ERROR("SharedMemoryManager") << "Failed to attach to shared memory segment "
116  << shm_segment_id_ << TLOG_ENDL;
117  }
118  }
119  else
120  {
121  TLOG_ERROR("SharedMemoryManager") << "Failed to connect to shared memory segment"
122  << ", errno = " << errno << ". Please check "
123  << "if a stale shared memory segment needs to "
124  << "be cleaned up. (ipcs, ipcrm -m <segId>)" << TLOG_ENDL;
125  }
126  return;
127 }
128 
130 {
131  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading BEGIN" << TLOG_ENDL;
132 
133  //std::unique_lock<std::mutex> lk(search_mutex_);
134  TraceLock lk(search_mutex_, 11, "GetBufferForReadingSearch");
135  auto rp = shm_ptr_->reader_pos.load();
136 
137  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading lock acquired, scanning buffers" << TLOG_ENDL;
138  bool retry = true;
139  int buffer_num = -1;
140  while (retry)
141  {
142  ShmBuffer* buffer_ptr = nullptr;
143  uint64_t seqID = -1;
144  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
145  {
146  auto buffer = (ii + rp) % shm_ptr_->buffer_count;
147 
148 
149  TLOG_ARB(14, "SharedMemoryManager") << "GetBufferForReading Checking if buffer " << buffer << " is stale" << TLOG_ENDL;
150  ResetBuffer(buffer);
151 
152  auto buf = getBufferInfo_(buffer);
153  TLOG_ARB(14, "SharedMemoryManager") << "GetBufferForReading: Buffer " << buffer << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " (" << manager_id_ << " or -1), seqid=" << buf->sequence_id << " (> " << last_seen_id_ << ")" << TLOG_ENDL;
154  if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
155  {
156  if (buf->sequence_id < seqID)
157  {
158  buffer_ptr = buf;
159  seqID = buf->sequence_id;
160  buffer_num = buffer;
161  }
162  }
163  }
164 
165  if (!buffer_ptr || (buffer_ptr && buffer_ptr->sem_id != -1 && buffer_ptr->sem_id != manager_id_))
166  {
167  continue;
168  }
169 
170  if (buffer_num >= 0)
171  {
172  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading Found buffer " << buffer_num << TLOG_ENDL;
173  buffer_ptr->sem_id = manager_id_;
174  buffer_ptr->sem = BufferSemaphoreFlags::Reading;
175  if (buffer_ptr->sem_id != manager_id_) { continue; } // Failed to acquire buffer
176  buffer_ptr->readPos = 0;
177  touchBuffer_(buffer_ptr);
178  if (buffer_ptr->sem_id != manager_id_) { continue; } // Failed to acquire buffer
179  if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
180  {
181  shm_ptr_->lowest_seq_id_read = seqID;
182  }
183  last_seen_id_ = seqID;
184  if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
185  }
186  retry = false;
187  }
188 
189  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading returning -1 because no buffers are ready" << TLOG_ENDL;
190  return buffer_num;
191 }
192 
194 {
195  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting BEGIN" << TLOG_ENDL;
196  //std::unique_lock<std::mutex> lk(search_mutex_);
197  TraceLock lk(search_mutex_, 12, "GetBufferForWritingSearch");
198  auto wp = shm_ptr_->writer_pos.load();
199 
200  // First, only look for "Empty" buffers
201  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
202  {
203  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
204 
205  ResetBuffer(buffer);
206 
207  auto buf = getBufferInfo_(buffer);
208  if (buf->sem == BufferSemaphoreFlags::Empty)
209  {
210  buf->sem_id = manager_id_;
211  buf->sem = BufferSemaphoreFlags::Writing;
212  if (buf->sem_id != manager_id_) continue;
213  buf->sequence_id = ++shm_ptr_->next_sequence_id;
214  buf->writePos = 0;
215  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
216  touchBuffer_(buf);
217  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting returning " << buffer << TLOG_ENDL;
218  return buffer;
219  }
220  }
221 
222  if (overwrite)
223  {
224  // Then, look for "Full" buffers
225  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
226  {
227  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
228 
229  ResetBuffer(buffer);
230 
231  auto buf = getBufferInfo_(buffer);
232  if (buf->sem == BufferSemaphoreFlags::Full)
233  {
234  buf->sem_id = manager_id_;
235  buf->sem = BufferSemaphoreFlags::Writing;
236  if (buf->sem_id != manager_id_) continue;
237  buf->sequence_id = ++shm_ptr_->next_sequence_id;
238  buf->writePos = 0;
239  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
240  touchBuffer_(buf);
241  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting returning " << buffer << TLOG_ENDL;
242  return buffer;
243  }
244  }
245 
246  // Finally, if we still haven't found a buffer, we have to clobber a reader...
247  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
248  {
249  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
250 
251  ResetBuffer(buffer);
252 
253  auto buf = getBufferInfo_(buffer);
254  if (buf->sem == BufferSemaphoreFlags::Reading)
255  {
256  buf->sem_id = manager_id_;
257  buf->sem = BufferSemaphoreFlags::Writing;
258  if (buf->sem_id != manager_id_) continue;
259  buf->sequence_id = ++shm_ptr_->next_sequence_id;
260  buf->writePos = 0;
261  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
262  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting returning " << buffer << TLOG_ENDL;
263  touchBuffer_(buf);
264  return buffer;
265  }
266  }
267  }
268 
269  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting Returning -1 because no buffers are ready" << TLOG_ENDL;
270  return -1;
271 }
272 
274 {
275  if (!IsValid()) return 0;
276  TLOG_ARB(23, "SharedMemoryManager") << "ReadReadyCount BEGIN" << TLOG_ENDL;
277  //std::unique_lock<std::mutex> lk(search_mutex_);
278  TraceLock lk(search_mutex_, 14, "ReadReadyCountSearch");
279  size_t count = 0;
280  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
281  {
282  TLOG_ARB(24, "SharedMemoryManager") << "ReadReadyCount: Checking if buffer " << ii << " is stale." << TLOG_ENDL;
283  ResetBuffer(ii);
284  auto buf = getBufferInfo_(ii);
285  TLOG_ARB(24, "SharedMemoryManager") << "ReadReadyCount: Buffer " << ii << ": sem=" << FlagToString(buf->sem) << " (expected " << FlagToString(BufferSemaphoreFlags::Full) << "), sem_id=" << buf->sem_id << " (" << manager_id_ << " or -1), seqid=" << buf->sequence_id << " (> " << last_seen_id_ << ")" << TLOG_ENDL;
286  if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
287  {
288  TLOG_ARB(24, "SharedMemoryManager") << "ReadReadyCount: Buffer " << ii << " is either unowned or owned by this manager, and is marked full." << TLOG_ENDL;
289  ++count;
290  }
291  }
292 
293  TLOG_ARB(23, "SharedMemoryManager") << "ReadReadyCount returning " << std::to_string(count) << TLOG_ENDL;
294  return count;
295 }
296 
298 {
299  if (!IsValid()) return 0;
300  //std::unique_lock<std::mutex> lk(search_mutex_);
301  TraceLock lk(search_mutex_, 15, "WriteReadyCountSearch");
302  size_t count = 0;
303  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
304  {
305  ResetBuffer(ii);
306  auto buf = getBufferInfo_(ii);
307  if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
308  || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
309  {
310  ++count;
311  }
312  }
313  return count;
314 }
315 
317 {
318  //std::unique_lock<std::mutex> lk(search_mutex_);
319  std::deque<int> output;
320  if (!IsValid()) return output;
321  TraceLock lk(search_mutex_, 16, "GetOwnedSearch");
322  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
323  {
324  auto buf = getBufferInfo_(ii);
325  if (buf->sem_id == manager_id_)
326  {
327  output.push_back(ii);
328  }
329  }
330 
331  return output;
332 }
333 
335 {
336  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
337  TraceLock lk(buffer_mutexes_[buffer], 17, "DataSizeBuffer" + std::to_string(buffer));
338  auto buf = getBufferInfo_(buffer);
339  touchBuffer_(buf);
340  return buf->writePos;
341 }
342 
343 
345 {
346  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
347  TraceLock lk(buffer_mutexes_[buffer], 18, "ResetReadPosBuffer" + std::to_string(buffer));
348  auto buf = getBufferInfo_(buffer);
349  touchBuffer_(buf);
350  buf->readPos = 0;
351 }
352 
354 {
355  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
356  TraceLock lk(buffer_mutexes_[buffer], 18, "ResetWritePosBuffer" + std::to_string(buffer));
357  auto buf = getBufferInfo_(buffer);
358  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
359  touchBuffer_(buf);
360  buf->writePos = 0;
361 }
362 
364 {
365  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
366  TraceLock lk(buffer_mutexes_[buffer], 19, "IncReadPosBuffer" + std::to_string(buffer));
367  auto buf = getBufferInfo_(buffer);
368  touchBuffer_(buf);
369  TLOG_ARB(13, "SharedMemoryManager") << "IncrementReadPos: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", bytes read=" << std::to_string(read) << TLOG_ENDL;
370  buf->readPos = buf->readPos + read;
371  TLOG_ARB(13, "SharedMemoryManager") << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << std::to_string(buf->readPos) << TLOG_ENDL;
372  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) + ")");
373 }
374 
375 void artdaq::SharedMemoryManager::IncrementWritePos(int buffer, size_t written)
376 {
377  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
378  TraceLock lk(buffer_mutexes_[buffer], 20, "IncWritePosBuffer" + std::to_string(buffer));
379  auto buf = getBufferInfo_(buffer);
380  touchBuffer_(buf);
381  TLOG_ARB(13, "SharedMemoryManager") << "IncrementWritePos: buffer= " << buffer << ", writePos=" << std::to_string(buf->writePos) << ", bytes written=" << std::to_string(written) << TLOG_ENDL;
382  buf->writePos += written;
383  TLOG_ARB(13, "SharedMemoryManager") << "IncrementWritePos: buffer= " << buffer << ", New writePos is " << std::to_string(buf->writePos) << TLOG_ENDL;
384  if (written == 0) Detach(true, "LogicError", "Cannot increment Write pos by 0!");
385 }
386 
388 {
389  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
390  TraceLock lk(buffer_mutexes_[buffer], 21, "MoreDataInBuffer" + std::to_string(buffer));
391  auto buf = getBufferInfo_(buffer);
392  TLOG_ARB(13, "SharedMemoryManager") << "MoreDataInBuffer: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", writePos=" << std::to_string(buf->writePos) << TLOG_ENDL;
393  return buf->readPos < buf->writePos;
394 }
395 
397 {
398  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
399  TraceLock lk(buffer_mutexes_[buffer], 22, "CheckBuffer" + std::to_string(buffer));
400  return checkBuffer_(getBufferInfo_(buffer), flags, false);
401 }
402 
403 void artdaq::SharedMemoryManager::MarkBufferFull(int buffer, int destination)
404 {
405  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
406  TraceLock lk(buffer_mutexes_[buffer], 23, "FillBuffer" + std::to_string(buffer));
407  auto shmBuf = getBufferInfo_(buffer);
408  touchBuffer_(shmBuf);
409  if (shmBuf->sem_id == manager_id_)
410  {
411  if (shmBuf->sem != BufferSemaphoreFlags::Full)
412  shmBuf->sem = BufferSemaphoreFlags::Full;
413 
414  shmBuf->sem_id = destination;
415  }
416 }
417 
419 {
420  TLOG_ARB(13, "SharedMemoryManager") << "MarkBufferEmpty BEGIN" << TLOG_ENDL;
421  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
422  TraceLock lk(buffer_mutexes_[buffer], 24, "EmptyBuffer" + std::to_string(buffer));
423  auto shmBuf = getBufferInfo_(buffer);
424  if (!force)
425  {
426  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, true);
427  }
428  touchBuffer_(shmBuf);
429 
430  shmBuf->readPos = 0;
431  shmBuf->sem = BufferSemaphoreFlags::Full;
432 
433  if ((force && (manager_id_ == 0 || manager_id_ == shmBuf->sem_id)) || (!force && shm_ptr_->destructive_read_mode))
434  {
435  TLOG_ARB(13, "SharedMemoryManager") << "MarkBufferEmpty Resetting buffer to Empty state" << TLOG_ENDL;
436  shmBuf->writePos = 0;
437  shmBuf->sem = BufferSemaphoreFlags::Empty;
438  if (shm_ptr_->reader_pos == static_cast<unsigned>(buffer) && !shm_ptr_->destructive_read_mode)
439  {
440  TLOG_ARB(13, "SharedMemoryManager") << "MarkBufferEmpty Broadcast mode; incrementing reader_pos" << TLOG_ENDL;
441  shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
442  }
443  }
444  shmBuf->sem_id = -1;
445  TLOG_ARB(13, "SharedMemoryManager") << "MarkBufferEmpty END" << TLOG_ENDL;
446 }
447 
449 {
450  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
451  TraceLock lk(buffer_mutexes_[buffer], 25, "ResetBuffer" + std::to_string(buffer));
452  auto shmBuf = getBufferInfo_(buffer);
453  /*
454  if (shmBuf->sequence_id < shm_ptr_->lowest_seq_id_read - size() && shmBuf->sem == BufferSemaphoreFlags::Full)
455  {
456  TLOG_DEBUG("SharedMemoryManager") << "Buffer " << buffer << " has been passed by all readers, marking Empty" << TLOG_ENDL;
457  shmBuf->writePos = 0;
458  shmBuf->sem = BufferSemaphoreFlags::Empty;
459  shmBuf->sem_id = -1;
460  return true;
461  }*/
462 
463  size_t delta = TimeUtils::gettimeofday_us() - shmBuf->last_touch_time;
464  if (delta > 0xFFFFFFFF) {
465  TLOG_TRACE("SharedMemoryManager") << "Buffer has touch time in the future, setting it to current time and ignoring..." << TLOG_ENDL;
466  shmBuf->last_touch_time = TimeUtils::gettimeofday_us();
467  return false;
468  }
469  if (delta <= shm_ptr_->buffer_timeout_us || shmBuf->sem == BufferSemaphoreFlags::Empty) return false;
470  TLOG_TRACE("SharedMemoryManager") << "Buffer " << buffer << " is stale, time=" << TimeUtils::gettimeofday_us() << ", last touch=" << shmBuf->last_touch_time << ", d=" << delta << ", timeout=" << shm_ptr_->buffer_timeout_us << TLOG_ENDL;
471 
472  if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
473  {
474  return true;
475  }
476  if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full)
477  {
478  TLOG_DEBUG("SharedMemoryManager") << "Resetting old broadcast mode buffer" << TLOG_ENDL;
479  shmBuf->writePos = 0;
480  shmBuf->sem = BufferSemaphoreFlags::Empty;
481  shmBuf->sem_id = -1;
482  if (shm_ptr_->reader_pos = buffer)shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
483  return true;
484  }
485 
486  if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
487  {
488  TLOG_WARNING("SharedMemoryManager") << "Stale Read buffer ( " << delta << " / " << shm_ptr_->buffer_timeout_us << " us ) detected! Resetting..." << TLOG_ENDL;
489  shmBuf->readPos = 0;
490  shmBuf->sem = BufferSemaphoreFlags::Full;
491  shmBuf->sem_id = -1;
492  return true;
493  }
494  return false;
495 }
496 
497 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
498 {
499  TLOG_ARB(13, "SharedMemoryManager") << "Write BEGIN" << TLOG_ENDL;
500  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
501  TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer));
502  auto shmBuf = getBufferInfo_(buffer);
503  checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
504  touchBuffer_(shmBuf);
505  TLOG_ARB(13, "SharedMemoryManager") << "Buffer Write Pos is " << std::to_string(shmBuf->writePos) << ", write size is " << std::to_string(size) << TLOG_ENDL;
506  if (shmBuf->writePos + size > shm_ptr_->buffer_size) Detach(true, "SharedMemoryWrite", "Attempted to write more data than fits into Shared Memory! \nRe-run with a larger buffer size!");
507 
508  auto pos = GetWritePos(buffer);
509  memcpy(pos, data, size);
510  shmBuf->writePos = shmBuf->writePos + size;
511  if (shmBuf->sequence_id > last_seen_id_)
512  {
513  last_seen_id_ = shmBuf->sequence_id;
514  }
515  TLOG_ARB(13, "SharedMemoryManager") << "Write END" << TLOG_ENDL;
516  return size;
517 }
518 
519 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
520 {
521  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
522  TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer));
523  auto shmBuf = getBufferInfo_(buffer);
524  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
525  touchBuffer_(shmBuf);
526  if (shmBuf->readPos + size > shm_ptr_->buffer_size) Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
527 
528  auto pos = GetReadPos(buffer);
529  memcpy(data, pos, size);
530  shmBuf->readPos += size;
531  touchBuffer_(shmBuf);
532  return checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
533 }
534 
536 {
537  std::ostringstream ostr;
538  ostr << "ShmStruct: " << std::endl
539  << "Reader Position: " << shm_ptr_->reader_pos << std::endl
540  << "Writer Position: " << shm_ptr_->writer_pos << std::endl
541  << "Next ID Number: " << shm_ptr_->next_id << std::endl
542  << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
543  << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
544  << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
545  << "Rank of Writer: " << shm_ptr_->rank << std::endl
546  << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::endl << std::endl;
547 
548  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
549  {
550  auto buf = getBufferInfo_(ii);
551  ostr << "ShmBuffer " << std::dec << ii << std::endl
552  << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
553  << "writePos: " << std::to_string(buf->writePos) << std::endl
554  << "readPos: " << std::to_string(buf->readPos) << std::endl
555  << "sem: " << FlagToString(buf->sem) << std::endl
556  << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
557  << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl << std::endl;
558  }
559 
560  return ostr.str();
561 }
562 
564 {
565  auto buf = getBufferInfo_(buffer);
566  return bufferStart_(buffer) + buf->readPos;
567 }
569 {
570  auto buf = getBufferInfo_(buffer);
571  return bufferStart_(buffer) + buf->writePos;
572 }
573 
575 {
576  return bufferStart_(buffer);
577 }
578 
579 uint8_t* artdaq::SharedMemoryManager::dataStart_() const
580 {
581  return reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + shm_ptr_->buffer_count * sizeof(ShmBuffer);
582 }
583 
584 uint8_t* artdaq::SharedMemoryManager::bufferStart_(int buffer)
585 {
586  if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
587  return dataStart_() + buffer * shm_ptr_->buffer_size;
588 }
589 
590 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(int buffer)
591 {
592  if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
593  return reinterpret_cast<ShmBuffer*>(reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + buffer * sizeof(ShmBuffer));
594 }
595 
596 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
597 {
598  if (exceptions)
599  {
600  if (buffer->sem != flags) Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
601  if (buffer->sem_id != manager_id_) Detach(true, "OwnerAccessViolation", "Shared Memory buffer is not owned by this manager instance!");
602  }
603  bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
604 
605  if (!ret)
606  {
607  TLOG_WARNING("SharedMemoryManager") << "CheckBuffer detected issue with buffer " << std::to_string(buffer->sequence_id) << "!"
608  << " ID: " << buffer->sem_id << " (" << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (" << FlagToString(flags) << "). "
609  << "ID -1 is okay if desired flag is \"Full\" or \"Empty\"." << TLOG_ENDL;
610  }
611 
612  return ret;
613 }
614 
615 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
616 {
617  if (buffer->sem_id != manager_id_) return;
618  TLOG_TRACE("SharedMemoryManager") << "touchBuffer_: Touching buffer with sequence_id " << std::to_string(buffer->sequence_id) << TLOG_ENDL;
619  buffer->last_touch_time = TimeUtils::gettimeofday_us();
620 }
621 
622 void artdaq::SharedMemoryManager::Detach(bool throwException, std::string category, std::string message)
623 {
624  if (IsValid())
625  {
626  auto bufs = GetBuffersOwnedByManager();
627  for (auto buf : bufs)
628  {
629  auto shmBuf = getBufferInfo_(buf);
630  if (shmBuf->sem == BufferSemaphoreFlags::Writing)
631  {
632  shmBuf->sem = BufferSemaphoreFlags::Empty;
633  }
634  else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
635  {
636  shmBuf->sem = BufferSemaphoreFlags::Full;
637  }
638  shmBuf->sem_id = -1;
639  }
640  }
641 
642  if (shm_ptr_)
643  {
644  shmdt(shm_ptr_);
645  shm_ptr_ = NULL;
646  }
647 
648  if (manager_id_ == 0 && shm_segment_id_ > -1)
649  {
650  shmctl(shm_segment_id_, IPC_RMID, NULL);
651  }
652 
653  if (category.size() > 0 && message.size() > 0)
654  {
655  TLOG_ERROR("SharedMemoryManager") << category << ": " << message << TLOG_ENDL;
656 
657  if (throwException)
658  {
659  throw cet::exception(category) << message;
660  }
661  }
662 }
663 
664 
665 
666 // Local Variables:
667 // mode: c++
668 // 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
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.
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.
virtual ~SharedMemoryManager() noexcept
SharedMemoryManager Destructor.
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 CheckBuffer(int buffer, BufferSemaphoreFlags flags)
Check both semaphore conditions (Mode flag and manager ID) for a given buffer.
void IncrementWritePos(int buffer, size_t written)
Increment the write position for a given buffer.
The TraceLock class allows a user to debug the acquisition and releasing of locks, by wrapping the unique_lock&lt;std::mutex&gt; API with TRACE calls.
Definition: TraceLock.hh:10
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.
std::deque< int > GetBuffersOwnedByManager()
Get the list of all buffers currently owned by this manager instance.
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.
void Detach(bool throwException=false, std::string category="", std::string message="")
Detach from the Shared Memory segment, optionally throwing a cet::exception with the specified proper...
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.
int GetBufferForWriting(bool overwrite)
Finds a buffer that is ready to be written to, and reserves it for the calling manager.
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. ...