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