artdaq_core  v3_01_00
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  }
92 
93  shm_ptr_->ready_magic = 0xCAFE1111;
94  }
95  else
96  {
97  TLOG_DEBUG("SharedMemoryManager") << "Waiting for owner to initalize Shared Memory" << TLOG_ENDL;
98  while (shm_ptr_->ready_magic != 0xCAFE1111) { usleep(1000); }
99  TLOG_DEBUG("SharedMemoryManager") << "Getting ID from Shared Memory" << TLOG_ENDL;
100  GetNewId();
101  shm_ptr_->lowest_seq_id_read = 0;
102  TLOG_DEBUG("SharedMemoryManager") << "Getting Shared Memory Size parameters" << TLOG_ENDL;
103  }
104  //last_seen_id_ = shm_ptr_->next_sequence_id;
105  TLOG_DEBUG("SharedMemoryManager") << "Initialization Complete: "
106  << "key: 0x" << std::hex << shm_key_
107  << ", manager ID: " << manager_id_
108  << ", Buffer size: " << std::to_string(shm_ptr_->buffer_size)
109  << ", Buffer count: " << std::to_string(shm_ptr_->buffer_count) << TLOG_ENDL;
110  return;
111  }
112  else
113  {
114  TLOG_ERROR("SharedMemoryManager") << "Failed to attach to shared memory segment "
115  << shm_segment_id_ << TLOG_ENDL;
116  }
117  }
118  else
119  {
120  TLOG_ERROR("SharedMemoryManager") << "Failed to connect to shared memory segment"
121  << ", errno = " << errno << ". Please check "
122  << "if a stale shared memory segment needs to "
123  << "be cleaned up. (ipcs, ipcrm -m <segId>)" << TLOG_ENDL;
124  }
125  return;
126 }
127 
129 {
130  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading BEGIN" << TLOG_ENDL;
131 
132  //std::unique_lock<std::mutex> lk(search_mutex_);
133  TraceLock lk(search_mutex_, 11, "GetBufferForReadingSearch");
134  auto rp = shm_ptr_->reader_pos.load();
135 
136  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading lock acquired, scanning buffers" << TLOG_ENDL;
137  bool retry = true;
138  int buffer_num = -1;
139  while (retry)
140  {
141  ShmBuffer* buffer_ptr = nullptr;
142  uint64_t seqID = -1;
143  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
144  {
145  auto buffer = (ii + rp) % shm_ptr_->buffer_count;
146 
147 
148  TLOG_ARB(14, "SharedMemoryManager") << "GetBufferForReading Checking if buffer " << buffer << " is stale" << TLOG_ENDL;
149  ResetBuffer(buffer);
150 
151  auto buf = getBufferInfo_(buffer);
152  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;
153  if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
154  {
155  if (buf->sequence_id < seqID)
156  {
157  buffer_ptr = buf;
158  seqID = buf->sequence_id;
159  buffer_num = buffer;
160  }
161  }
162  }
163 
164  if (!buffer_ptr || (buffer_ptr && buffer_ptr->sem_id != -1 && buffer_ptr->sem_id != manager_id_))
165  {
166  continue;
167  }
168 
169  if (buffer_num >= 0)
170  {
171  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading Found buffer " << buffer_num << TLOG_ENDL;
172  buffer_ptr->sem_id = manager_id_;
173  buffer_ptr->sem = BufferSemaphoreFlags::Reading;
174  if (buffer_ptr->sem_id != manager_id_) { continue; } // Failed to acquire buffer
175  buffer_ptr->readPos = 0;
176  touchBuffer_(buffer_ptr);
177  if (buffer_ptr->sem_id != manager_id_) { continue; } // Failed to acquire buffer
178  if (shm_ptr_->destructive_read_mode && shm_ptr_->lowest_seq_id_read == last_seen_id_)
179  {
180  shm_ptr_->lowest_seq_id_read = seqID;
181  }
182  last_seen_id_ = seqID;
183  if (shm_ptr_->destructive_read_mode) shm_ptr_->reader_pos = (buffer_num + 1) % shm_ptr_->buffer_count;
184  }
185  retry = false;
186  }
187 
188  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForReading returning -1 because no buffers are ready" << TLOG_ENDL;
189  return buffer_num;
190 }
191 
193 {
194  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting BEGIN" << TLOG_ENDL;
195  //std::unique_lock<std::mutex> lk(search_mutex_);
196  TraceLock lk(search_mutex_, 12, "GetBufferForWritingSearch");
197  auto wp = shm_ptr_->writer_pos.load();
198 
199  // First, only look for "Empty" buffers
200  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
201  {
202  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
203 
204  ResetBuffer(buffer);
205 
206  auto buf = getBufferInfo_(buffer);
207  if (buf->sem == BufferSemaphoreFlags::Empty)
208  {
209  buf->sem_id = manager_id_;
210  buf->sem = BufferSemaphoreFlags::Writing;
211  if (buf->sem_id != manager_id_) continue;
212  buf->sequence_id = ++shm_ptr_->next_sequence_id;
213  buf->writePos = 0;
214  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
215  touchBuffer_(buf);
216  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting returning " << buffer << TLOG_ENDL;
217  return buffer;
218  }
219  }
220 
221  if (overwrite)
222  {
223  // Then, look for "Full" buffers
224  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
225  {
226  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
227 
228  ResetBuffer(buffer);
229 
230  auto buf = getBufferInfo_(buffer);
231  if (buf->sem == BufferSemaphoreFlags::Full)
232  {
233  buf->sem_id = manager_id_;
234  buf->sem = BufferSemaphoreFlags::Writing;
235  if (buf->sem_id != manager_id_) continue;
236  buf->sequence_id = ++shm_ptr_->next_sequence_id;
237  buf->writePos = 0;
238  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
239  touchBuffer_(buf);
240  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting returning " << buffer << TLOG_ENDL;
241  return buffer;
242  }
243  }
244 
245  // Finally, if we still haven't found a buffer, we have to clobber a reader...
246  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
247  {
248  auto buffer = (ii + wp) % shm_ptr_->buffer_count;
249 
250  ResetBuffer(buffer);
251 
252  auto buf = getBufferInfo_(buffer);
253  if (buf->sem == BufferSemaphoreFlags::Reading)
254  {
255  buf->sem_id = manager_id_;
256  buf->sem = BufferSemaphoreFlags::Writing;
257  if (buf->sem_id != manager_id_) continue;
258  buf->sequence_id = ++shm_ptr_->next_sequence_id;
259  buf->writePos = 0;
260  shm_ptr_->writer_pos = (buffer + 1) % shm_ptr_->buffer_count;
261  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting returning " << buffer << TLOG_ENDL;
262  touchBuffer_(buf);
263  return buffer;
264  }
265  }
266  }
267 
268  TLOG_ARB(13, "SharedMemoryManager") << "GetBufferForWriting Returning -1 because no buffers are ready" << TLOG_ENDL;
269  return -1;
270 }
271 
273 {
274  if (!IsValid()) return 0;
275  TLOG_ARB(23, "SharedMemoryManager") << "ReadReadyCount BEGIN" << TLOG_ENDL;
276  //std::unique_lock<std::mutex> lk(search_mutex_);
277  TraceLock lk(search_mutex_, 14, "ReadReadyCountSearch");
278  size_t count = 0;
279  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
280  {
281  TLOG_ARB(24, "SharedMemoryManager") << "ReadReadyCount: Checking if buffer " << ii << " is stale." << TLOG_ENDL;
282  ResetBuffer(ii);
283  auto buf = getBufferInfo_(ii);
284  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;
285  if (buf->sem == BufferSemaphoreFlags::Full && (buf->sem_id == -1 || buf->sem_id == manager_id_) && buf->sequence_id > last_seen_id_)
286  {
287  TLOG_ARB(24, "SharedMemoryManager") << "ReadReadyCount: Buffer " << ii << " is either unowned or owned by this manager, and is marked full." << TLOG_ENDL;
288  ++count;
289  }
290  }
291 
292  TLOG_ARB(23, "SharedMemoryManager") << "ReadReadyCount returning " << std::to_string(count) << TLOG_ENDL;
293  return count;
294 }
295 
297 {
298  if (!IsValid()) return 0;
299  //std::unique_lock<std::mutex> lk(search_mutex_);
300  TraceLock lk(search_mutex_, 15, "WriteReadyCountSearch");
301  size_t count = 0;
302  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
303  {
304  ResetBuffer(ii);
305  auto buf = getBufferInfo_(ii);
306  if ((buf->sem == BufferSemaphoreFlags::Empty && buf->sem_id == -1)
307  || (overwrite && buf->sem != BufferSemaphoreFlags::Writing))
308  {
309  ++count;
310  }
311  }
312  return count;
313 }
314 
316 {
317  //std::unique_lock<std::mutex> lk(search_mutex_);
318  std::deque<int> output;
319  if (!IsValid()) return output;
320  TraceLock lk(search_mutex_, 16, "GetOwnedSearch");
321  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
322  {
323  auto buf = getBufferInfo_(ii);
324  if (buf->sem_id == manager_id_)
325  {
326  output.push_back(ii);
327  }
328  }
329 
330  return output;
331 }
332 
334 {
335  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
336  TraceLock lk(buffer_mutexes_[buffer], 17, "DataSizeBuffer" + std::to_string(buffer));
337  auto buf = getBufferInfo_(buffer);
338  touchBuffer_(buf);
339  return buf->writePos;
340 }
341 
342 
344 {
345  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
346  TraceLock lk(buffer_mutexes_[buffer], 18, "ResetReadPosBuffer" + std::to_string(buffer));
347  auto buf = getBufferInfo_(buffer);
348  touchBuffer_(buf);
349  buf->readPos = 0;
350 }
351 
353 {
354  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
355  TraceLock lk(buffer_mutexes_[buffer], 18, "ResetWritePosBuffer" + std::to_string(buffer));
356  auto buf = getBufferInfo_(buffer);
357  checkBuffer_(buf, BufferSemaphoreFlags::Writing);
358  touchBuffer_(buf);
359  buf->writePos = 0;
360 }
361 
363 {
364  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
365  TraceLock lk(buffer_mutexes_[buffer], 19, "IncReadPosBuffer" + std::to_string(buffer));
366  auto buf = getBufferInfo_(buffer);
367  touchBuffer_(buf);
368  TLOG_ARB(13, "SharedMemoryManager") << "IncrementReadPos: buffer= " << buffer << ", readPos=" << std::to_string(buf->readPos) << ", bytes read=" << std::to_string(read) << TLOG_ENDL;
369  buf->readPos = buf->readPos + read;
370  TLOG_ARB(13, "SharedMemoryManager") << "IncrementReadPos: buffer= " << buffer << ", New readPos is " << std::to_string(buf->readPos) << TLOG_ENDL;
371  if (read == 0)
372  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  if (TimeUtils::gettimeofday_us() - shmBuf->last_touch_time <= shm_ptr_->buffer_timeout_us) return false;
464 
465  if (shmBuf->sem_id == manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Writing)
466  {
467  return true;
468  }
469  if (!shm_ptr_->destructive_read_mode && shmBuf->sem == BufferSemaphoreFlags::Full)
470  {
471  TLOG_DEBUG("SharedMemoryManager") << "Resetting old broadcast mode buffer" << TLOG_ENDL;
472  shmBuf->writePos = 0;
473  shmBuf->sem = BufferSemaphoreFlags::Empty;
474  shmBuf->sem_id = -1;
475  if (shm_ptr_->reader_pos = buffer)shm_ptr_->reader_pos = (buffer + 1) % shm_ptr_->buffer_count;
476  return true;
477  }
478 
479  if (shmBuf->sem_id != manager_id_ && shmBuf->sem == BufferSemaphoreFlags::Reading)
480  {
481  TLOG_WARNING("SharedMemoryManager") << "Stale Read buffer ( " << std::to_string(TimeUtils::gettimeofday_us() - shmBuf->last_touch_time) << " / " << std::to_string(shm_ptr_->buffer_timeout_us) << " us ) detected! Resetting..." << TLOG_ENDL;
482  shmBuf->readPos = 0;
483  shmBuf->sem = BufferSemaphoreFlags::Full;
484  shmBuf->sem_id = -1;
485  return true;
486  }
487  return false;
488 }
489 
490 size_t artdaq::SharedMemoryManager::Write(int buffer, void* data, size_t size)
491 {
492  TLOG_ARB(13, "SharedMemoryManager") << "Write BEGIN" << TLOG_ENDL;
493  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
494  TraceLock lk(buffer_mutexes_[buffer], 26, "WriteBuffer" + std::to_string(buffer));
495  auto shmBuf = getBufferInfo_(buffer);
496  checkBuffer_(shmBuf, BufferSemaphoreFlags::Writing);
497  touchBuffer_(shmBuf);
498  TLOG_ARB(13, "SharedMemoryManager") << "Buffer Write Pos is " << std::to_string(shmBuf->writePos) << ", write size is " << std::to_string(size) << TLOG_ENDL;
499  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!");
500 
501  auto pos = GetWritePos(buffer);
502  memcpy(pos, data, size);
503  shmBuf->writePos = shmBuf->writePos + size;
504  if (shmBuf->sequence_id > last_seen_id_)
505  {
506  last_seen_id_ = shmBuf->sequence_id;
507  }
508  TLOG_ARB(13, "SharedMemoryManager") << "Write END" << TLOG_ENDL;
509  return size;
510 }
511 
512 bool artdaq::SharedMemoryManager::Read(int buffer, void* data, size_t size)
513 {
514  //std::unique_lock<std::mutex> lk(buffer_mutexes_[buffer]);
515  TraceLock lk(buffer_mutexes_[buffer], 27, "ReadBuffer" + std::to_string(buffer));
516  auto shmBuf = getBufferInfo_(buffer);
517  checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading);
518  touchBuffer_(shmBuf);
519  if (shmBuf->readPos + size > shm_ptr_->buffer_size) Detach(true, "SharedMemoryRead", "Attempted to read more data than exists in Shared Memory!");
520 
521  auto pos = GetReadPos(buffer);
522  memcpy(data, pos, size);
523  shmBuf->readPos += size;
524  return checkBuffer_(shmBuf, BufferSemaphoreFlags::Reading, false);
525 }
526 
528 {
529  std::ostringstream ostr;
530  ostr << "ShmStruct: " << std::endl
531  << "Reader Position: " << shm_ptr_->reader_pos << std::endl
532  << "Writer Position: " << shm_ptr_->writer_pos << std::endl
533  << "Next ID Number: " << shm_ptr_->next_id << std::endl
534  << "Buffer Count: " << shm_ptr_->buffer_count << std::endl
535  << "Buffer Size: " << std::to_string(shm_ptr_->buffer_size) << " bytes" << std::endl
536  << "Buffers Written: " << std::to_string(shm_ptr_->next_sequence_id) << std::endl
537  << "Rank of Writer: " << shm_ptr_->rank << std::endl
538  << "Ready Magic Bytes: 0x" << std::hex << shm_ptr_->ready_magic << std::endl << std::endl;
539 
540  for (auto ii = 0; ii < shm_ptr_->buffer_count; ++ii)
541  {
542  auto buf = getBufferInfo_(ii);
543  ostr << "ShmBuffer " << std::dec << ii << std::endl
544  << "sequenceID: " << std::to_string(buf->sequence_id) << std::endl
545  << "writePos: " << std::to_string(buf->writePos) << std::endl
546  << "readPos: " << std::to_string(buf->readPos) << std::endl
547  << "sem: " << FlagToString(buf->sem) << std::endl
548  << "Owner: " << std::to_string(buf->sem_id.load()) << std::endl
549  << "Last Touch Time: " << std::to_string(buf->last_touch_time / 1000000.0) << std::endl << std::endl;
550  }
551 
552  return ostr.str();
553 }
554 
556 {
557  auto buf = getBufferInfo_(buffer);
558  return bufferStart_(buffer) + buf->readPos;
559 }
561 {
562  auto buf = getBufferInfo_(buffer);
563  return bufferStart_(buffer) + buf->writePos;
564 }
565 
567 {
568  return bufferStart_(buffer);
569 }
570 
571 uint8_t* artdaq::SharedMemoryManager::dataStart_() const
572 {
573  return reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + shm_ptr_->buffer_count * sizeof(ShmBuffer);
574 }
575 
576 uint8_t* artdaq::SharedMemoryManager::bufferStart_(int buffer)
577 {
578  if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
579  return dataStart_() + buffer * shm_ptr_->buffer_size;
580 }
581 
582 artdaq::SharedMemoryManager::ShmBuffer* artdaq::SharedMemoryManager::getBufferInfo_(int buffer)
583 {
584  if (buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
585  return reinterpret_cast<ShmBuffer*>(reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + buffer * sizeof(ShmBuffer));
586 }
587 
588 bool artdaq::SharedMemoryManager::checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions)
589 {
590  if (exceptions)
591  {
592  if (buffer->sem != flags) Detach(true, "StateAccessViolation", "Shared Memory buffer is not in the correct state! (expected " + FlagToString(flags) + ", actual " + FlagToString(buffer->sem) + ")");
593  if (buffer->sem_id != manager_id_) Detach(true, "OwnerAccessViolation", "Shared Memory buffer is not owned by this manager instance!");
594  }
595  bool ret = (buffer->sem_id == manager_id_ || (buffer->sem_id == -1 && (flags == BufferSemaphoreFlags::Full || flags == BufferSemaphoreFlags::Empty))) && buffer->sem == flags;
596 
597  if (!ret)
598  {
599  TLOG_WARNING("SharedMemoryManager") << "CheckBuffer detected issue with buffer " << std::to_string(buffer->sequence_id) << "!"
600  << " ID: " << buffer->sem_id << " (" << manager_id_ << "), Flag: " << FlagToString(buffer->sem) << " (" << FlagToString(flags) << "). "
601  << "ID -1 is okay if desired flag is \"Full\" or \"Empty\"." << TLOG_ENDL;
602  }
603 
604  return ret;
605 }
606 
607 void artdaq::SharedMemoryManager::touchBuffer_(ShmBuffer* buffer)
608 {
609  if (buffer->sem_id != manager_id_) return;
610  TLOG_TRACE("SharedMemoryManager") << "touchBuffer_: Touching buffer with sequence_id " << std::to_string(buffer->sequence_id) << TLOG_ENDL;
611  buffer->last_touch_time = TimeUtils::gettimeofday_us();
612 }
613 
614 void artdaq::SharedMemoryManager::Detach(bool throwException, std::string category, std::string message)
615 {
616  if (IsValid())
617  {
618  auto bufs = GetBuffersOwnedByManager();
619  for (auto buf : bufs)
620  {
621  auto shmBuf = getBufferInfo_(buf);
622  if (shmBuf->sem == BufferSemaphoreFlags::Writing)
623  {
624  shmBuf->sem = BufferSemaphoreFlags::Empty;
625  }
626  else if (shmBuf->sem == BufferSemaphoreFlags::Reading)
627  {
628  shmBuf->sem = BufferSemaphoreFlags::Full;
629  }
630  shmBuf->sem_id = -1;
631  }
632  }
633 
634  if (shm_ptr_)
635  {
636  shmdt(shm_ptr_);
637  shm_ptr_ = NULL;
638  }
639 
640  if (manager_id_ == 0 && shm_segment_id_ > -1)
641  {
642  shmctl(shm_segment_id_, IPC_RMID, NULL);
643  }
644 
645  if (category.size() > 0 && message.size() > 0)
646  {
647  TLOG_ERROR("SharedMemoryManager") << category << ": " << message << TLOG_ENDL;
648 
649  if (throwException)
650  {
651  throw cet::exception(category) << message;
652  }
653  }
654 }
655 
656 
657 
658 // Local Variables:
659 // mode: c++
660 // 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. ...