artdaq_core  3.10.01
SharedMemoryManager.hh
1 #ifndef artdaq_core_Core_SharedMemoryManager_hh
2 #define artdaq_core_Core_SharedMemoryManager_hh 1
3 
4 #include <atomic>
5 #include <deque>
6 #include <iomanip>
7 #include <list>
8 #include <mutex>
9 #include <sstream>
10 #include <string>
11 #include <vector>
12 #include "artdaq-core/Utilities/TimeUtils.hh"
13 #include "sys/sysinfo.h"
14 
15 namespace artdaq {
21 {
22 public:
27  {
28  Empty,
29  Writing,
30  Full,
31  Reading
32  };
33 
39  static inline std::string FlagToString(BufferSemaphoreFlags flag)
40  {
41  switch (flag)
42  {
44  return "Empty";
46  return "Writing";
48  return "Full";
50  return "Reading";
51  }
52  return "Unknown";
53  }
54 
64  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);
65 
69  virtual ~SharedMemoryManager() noexcept; // NOLINT(bugprone-exception-escape) See implementation for more details
70 
74  bool Attach(size_t timeout_usec = 0);
75 
80  int GetBufferForReading();
81 
87  int GetBufferForWriting(bool overwrite);
88 
93  bool ReadyForRead();
94 
100  virtual bool ReadyForWrite(bool overwrite);
101 
106  size_t ReadReadyCount();
107 
113  size_t WriteReadyCount(bool overwrite);
114 
120  std::deque<int> GetBuffersOwnedByManager(bool locked = true);
121 
127  size_t BufferDataSize(int buffer);
128 
133  size_t BufferSize() { return (shm_ptr_ != nullptr ? shm_ptr_->buffer_size : 0); }
134 
139  void ResetReadPos(int buffer);
140 
145  void ResetWritePos(int buffer);
151  void IncrementReadPos(int buffer, size_t read);
152 
159  bool IncrementWritePos(int buffer, size_t written);
160 
166  bool MoreDataInBuffer(int buffer);
167 
174  bool CheckBuffer(int buffer, BufferSemaphoreFlags flags);
175 
181  void MarkBufferFull(int buffer, int destination = -1);
182 
189  void MarkBufferEmpty(int buffer, bool force = false, bool detachOnException = true);
190 
197  bool ResetBuffer(int buffer);
198 
202  void GetNewId()
203  {
204  if (manager_id_ < 0 && IsValid()) manager_id_ = shm_ptr_->next_id.fetch_add(1);
205  }
206 
211  uint16_t GetAttachedCount() const;
212 
216  void ResetAttachedCount() const
217  {
218  if (manager_id_ == 0 && IsValid()) shm_ptr_->next_id = 1;
219  }
220 
225  int GetMyId() const { return manager_id_; }
226 
231  int GetRank() const { return IsValid() ? shm_ptr_->rank : -1; }
232 
237  void SetRank(int rank)
238  {
239  if (manager_id_ == 0 && IsValid()) shm_ptr_->rank = rank;
240  }
241 
246  bool IsValid() const { return shm_ptr_ ? true : false; }
247 
251  bool IsEndOfData() const;
252 
257  size_t size() const { return IsValid() ? shm_ptr_->buffer_count : 0; }
258 
266  size_t Write(int buffer, void* data, size_t size);
267 
275  bool Read(int buffer, void* data, size_t size);
276 
281  virtual std::string toString();
282 
287  uint32_t GetKey() const { return shm_key_; }
288 
294  void* GetReadPos(int buffer);
295 
301  void* GetWritePos(int buffer);
302 
308  void* GetBufferStart(int buffer);
309 
317  void Detach(bool throwException = false, const std::string& category = "", const std::string& message = "", bool force = false);
318 
323  uint64_t GetBufferTimeout() const { return IsValid() ? shm_ptr_->buffer_timeout_us : 0; }
324 
329  size_t GetBufferCount() const { return IsValid() ? shm_ptr_->next_sequence_id : 0; }
330 
335  size_t GetLastSeenBufferID() const { return last_seen_id_; }
336 
340  size_t GetLowestSeqIDRead() const { return IsValid() ? shm_ptr_->lowest_seq_id_read : 0; }
341 
346  void SetMinWriteSize(size_t size) { min_write_size_ = size; }
347 
352  std::vector<std::pair<int, BufferSemaphoreFlags>> GetBufferReport();
353 
357  void TouchBuffer(int buffer) { return touchBuffer_(getBufferInfo_(buffer)); }
358 
359  static uint64_t GetAvailableRAM()
360  {
361  struct sysinfo meminfo;
362  auto err = sysinfo(&meminfo);
363  if (err == 0)
364  {
365  return meminfo.freeram * meminfo.mem_unit;
366  }
367  return 0;
368  }
369 
370  static std::string PrintBytes(uint64_t bytes)
371  {
372  double print = bytes / 1024.0 / 1024.0 / 1024.0;
373  std::string unit = "GB";
374  if (bytes < 1024)
375  {
376  print = bytes;
377  unit = "B";
378  }
379  else if (bytes < 1024 * 1024)
380  {
381  print = bytes / 1024.0;
382  unit = "KB";
383  }
384  else if (bytes < 1024 * 1024 * 1024)
385  {
386  print = bytes / 1024.0 / 1024.0;
387  unit = "MB";
388  }
389 
390  std::stringstream ss;
391  ss << std::fixed << std::setprecision(4) << print << " " << unit;
392 
393  return ss.str();
394  }
395 
396 private:
397  SharedMemoryManager(SharedMemoryManager const&) = delete;
399  SharedMemoryManager& operator=(SharedMemoryManager const&) = delete;
400  SharedMemoryManager& operator=(SharedMemoryManager&&) = delete;
401 
402  struct ShmBuffer
403  {
404  size_t writePos;
405  size_t readPos;
406  std::atomic<BufferSemaphoreFlags> sem;
407  std::atomic<int16_t> sem_id;
408  std::atomic<size_t> sequence_id;
409  std::atomic<uint64_t> last_touch_time;
410  };
411 
412  struct ShmStruct
413  {
414  std::atomic<unsigned int> reader_pos;
415  std::atomic<unsigned int> writer_pos;
416  int buffer_count;
417  size_t buffer_size;
418  size_t buffer_timeout_us;
419  size_t next_sequence_id;
420  size_t lowest_seq_id_read;
421  bool destructive_read_mode;
422 
423  std::atomic<int> writer_count;
424  std::atomic<int> reader_count;
425 
426  std::atomic<int> next_id;
427  int rank;
428  unsigned ready_magic;
429  };
430 
431  inline uint8_t* dataStart_() const
432  {
433  if (shm_ptr_ == nullptr) return nullptr;
434  return reinterpret_cast<uint8_t*>(shm_ptr_ + 1) + shm_ptr_->buffer_count * sizeof(ShmBuffer); // NOLINT(cppcoreguidelines-pro-type-reinterpret-cast,cppcoreguidelines-pro-bounds-pointer-arithmetic)
435  }
436 
437  inline uint8_t* bufferStart_(int buffer)
438  {
439  if (shm_ptr_ == nullptr) return nullptr;
440  if (buffer >= requested_shm_parameters_.buffer_count && buffer >= shm_ptr_->buffer_count) Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
441  return dataStart_() + buffer * shm_ptr_->buffer_size; // NOLINT(cppcoreguidelines-pro-bounds-pointer-arithmetic)
442  }
443 
444  inline ShmBuffer* getBufferInfo_(int buffer)
445  {
446  if (shm_ptr_ == nullptr) return nullptr;
447  // Check local variable first, but re-check shared memory
448  if (buffer >= requested_shm_parameters_.buffer_count && buffer >= shm_ptr_->buffer_count)
449  Detach(true, "ArgumentOutOfRange", "The specified buffer does not exist!");
450  return buffer_ptrs_[buffer];
451  }
452  bool checkBuffer_(ShmBuffer* buffer, BufferSemaphoreFlags flags, bool exceptions = true);
453  void touchBuffer_(ShmBuffer* buffer);
454 
455  ShmStruct requested_shm_parameters_;
456 
457  int shm_segment_id_;
458  ShmStruct* shm_ptr_;
459  uint32_t shm_key_;
460  int manager_id_;
461  std::vector<ShmBuffer*> buffer_ptrs_;
462  mutable std::vector<std::mutex> buffer_mutexes_;
463  mutable std::mutex search_mutex_;
464 
465  std::atomic<size_t> last_seen_id_;
466  bool registered_reader_{false};
467  bool registered_writer_{false};
468  size_t min_write_size_;
469 };
470 
471 } // namespace artdaq
472 
473 #endif // artdaq_core_Core_SharedMemoryManager_hh
void Detach(bool throwException=false, const std::string &category="", const std::string &message="", bool force=false)
Detach from the Shared Memory segment, optionally throwing a cet::exception with the specified proper...
void TouchBuffer(int buffer)
Touch the given buffer (update its last_touch_time)
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.
void ResetAttachedCount() const
Reset the attached manager count to 0.
bool IsValid() const
Is the shared memory pointer valid?
bool Attach(size_t timeout_usec=0)
Reconnect to the shared memory segment.
std::deque< int > GetBuffersOwnedByManager(bool locked=true)
Get the list of all buffers currently owned by this manager instance.
void ResetReadPos(int buffer)
Set the read position of the given buffer to the beginning of the buffer.
static std::string FlagToString(BufferSemaphoreFlags flag)
Convert a BufferSemaphoreFlags variable to its string represenatation.
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.
void GetNewId()
Assign a new ID to the current SharedMemoryManager, if one has not yet been assigned.
The buffer is full, and waiting for a reader.
std::vector< std::pair< int, BufferSemaphoreFlags > > GetBufferReport()
Get a report on the status of each buffer.
BufferSemaphoreFlags
The BufferSemaphoreFlags enumeration represents the different possible &quot;states&quot; of a given shared mem...
size_t BufferDataSize(int buffer)
Get the current size of the buffer&#39;s data.
size_t BufferSize()
Get the size of of a single buffer.
virtual ~SharedMemoryManager() noexcept
SharedMemoryManager Destructor.
The buffer is currently being read from.
The buffer is currently being written to.
The SharedMemoryManager creates a Shared Memory area which is divided into a number of fixed-size buf...
The buffer is empty, and waiting for a writer.
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...
int GetRank() const
Get the rank of the owner of the Shared Memory (artdaq assigns rank to each artdaq process for data f...
size_t GetLowestSeqIDRead() const
Gets the lowest sequence ID that has been read by any reader, as reported by the readers.
size_t size() const
Get the number of buffers in the shared memory segment.
bool IncrementWritePos(int buffer, size_t written)
Increment the write position for a given buffer.
bool IsEndOfData() const
Determine whether the Shared Memory is marked for destruction (End of Data)
bool CheckBuffer(int buffer, BufferSemaphoreFlags flags)
Check both semaphore conditions (Mode flag and manager ID) for a given buffer.
size_t GetLastSeenBufferID() const
Gets the highest buffer number either written or read by this SharedMemoryManager.
uint32_t GetKey() const
Get the key of the shared memory attached to this SharedMemoryManager.
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, bool detachOnException=true)
Release a buffer from a reader, marking it Empty and ready to accept more data.
uint64_t GetBufferTimeout() const
Gets the configured timeout for buffers to be declared &quot;stale&quot;.
void ResetWritePos(int buffer)
Set the write position of the given buffer to the beginning of the buffer.
void SetMinWriteSize(size_t size)
Sets the threshold after which a buffer should be considered &quot;non-empty&quot; (in case of default headers)...
size_t GetBufferCount() const
Gets the number of buffers which have been processed through the Shared Memory.
void * GetWritePos(int buffer)
Get a pointer to the current write position of the buffer.
size_t Write(int buffer, void *data, size_t size)
Write size bytes of data from the given pointer to a buffer.
void * GetReadPos(int buffer)
Get a pointer to the current read position of the buffer.
void * GetBufferStart(int buffer)
Get a pointer to the start position of the buffer.
SharedMemoryManager(uint32_t shm_key, size_t buffer_count=0, size_t buffer_size=0, uint64_t buffer_timeout_us=100 *1000000, bool destructive_read_mode=true)
SharedMemoryManager Constructor.
bool ReadyForRead()
Whether any buffer is ready for read.
void SetRank(int rank)
Set the rank stored in the Shared Memory, if the current instance is the owner of the shared memory...
int GetBufferForWriting(bool overwrite)
Finds a buffer that is ready to be written to, and reserves it for the calling manager.
uint16_t GetAttachedCount() const
Get the number of attached SharedMemoryManagers.
size_t WriteReadyCount(bool overwrite)
Count the number of buffers that are ready for writing.
int GetMyId() const
Get the ID number of the current SharedMemoryManager.
int GetBufferForReading()
Finds a buffer that is ready to be read, and reserves it for the calling manager. ...
virtual bool ReadyForWrite(bool overwrite)
Whether any buffer is available for write.