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