AMPS C/C++ Client Class Reference
AMPS C/C++ Client Version 5.3.5.17
MMapStoreBuffer.hpp
Go to the documentation of this file.
1 //
3 // Copyright (c) 2010-2026 60East Technologies Inc., All Rights Reserved.
4 //
5 // This computer software is owned by 60East Technologies Inc. and is
6 // protected by U.S. copyright laws and other laws and by international
7 // treaties. This computer software is furnished by 60East Technologies
8 // Inc. pursuant to a written license agreement and may be used, copied,
9 // transmitted, and stored only in accordance with the terms of such
10 // license agreement and with the inclusion of the above copyright notice.
11 // This computer software or any other copies thereof may not be provided
12 // or otherwise made available to any other person.
13 //
14 // U.S. Government Restricted Rights. This computer software: (a) was
15 // developed at private expense and is in all respects the proprietary
16 // information of 60East Technologies Inc.; (b) was not developed with
17 // government funds; (c) is a trade secret of 60East Technologies Inc.
18 // for all purposes of the Freedom of Information Act; and (d) is a
19 // commercial item and thus, pursuant to Section 12.212 of the Federal
20 // Acquisition Regulations (FAR) and DFAR Supplement Section 227.7202,
21 // Government's use, duplication or disclosure of the computer software
22 // is subject to the restrictions set forth by 60East Technologies Inc..
23 //
25 
26 #ifndef _MMAPSTOREBUFFER_H_
27 #define _MMAPSTOREBUFFER_H_
28 
29 #include <amps/ampsplusplus.hpp>
31 #include <limits>
32 #include <string>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <fcntl.h>
36 #ifdef _WIN32
37  #include <windows.h>
38 #else
39  #include <sys/mman.h>
40 #endif
41 
44 
45 
46 namespace AMPS
47 {
51  {
52  public:
63 #ifdef _WIN32
64  MMapStoreBuffer(const std::string& fileName_,
65  size_t initialSize_ = AMPS_MEMORYBUFFER_DEFAULT_LENGTH,
66  size_t sizeAlignment_ = 1,
67  bool useStoredSizeAlignment_ = false)
68  : MemoryStoreBuffer(MemoryStoreBuffer::EMPTY)
69  , _sizeAlignment(sizeAlignment_ ? sizeAlignment_ : 1)
70  , _mapFile(INVALID_HANDLE_VALUE), _file(INVALID_HANDLE_VALUE)
71  {
72  try
73  {
74  if (initialSize_ == 0)
75  {
76  initialSize_ = AMPS_MEMORYBUFFER_DEFAULT_LENGTH;
77  }
78  _file = CreateFileA(fileName_.c_str(), GENERIC_READ | GENERIC_WRITE, 0,
79  NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
80  if ( _file == INVALID_HANDLE_VALUE )
81  {
82  std::ostringstream os;
83  os << "Failed to create file " << fileName_ << " for MMapStoreBuffer";
84  error(os.str());
85  }
86  LARGE_INTEGER liFileSize;
87  if (GetFileSizeEx(_file, &liFileSize) == 0)
88  {
89  std::ostringstream os;
90  os << "Failure getting file size of " << fileName_ << " for MMapStoreBuffer";
91  error(os.str());
92  }
93 #ifdef _WIN64
94  size_t fileSize = liFileSize.QuadPart;
95 #else
96  size_t fileSize = liFileSize.LowPart;
97 #endif
98  setStoredSizeAlignment(fileName_, fileSize,
99  useStoredSizeAlignment_);
100  _setSize( initialSize_ > fileSize ?
101  initialSize_ : fileSize);
102  }
103  catch (...)
104  {
105  closeOnConstructionError();
106  throw;
107  }
108  }
109 #else
110  MMapStoreBuffer(const std::string& fileName_,
111  size_t initialSize_ = AMPS_MEMORYBUFFER_DEFAULT_LENGTH,
112  size_t sizeAlignment_ = 1,
113  bool useStoredSizeAlignment_ = false)
115  , _sizeAlignment(sizeAlignment_ ? sizeAlignment_ : 1)
116  , _fd(-1)
117  {
118  try
119  {
120  if (initialSize_ == 0)
121  {
122  initialSize_ = AMPS_MEMORYBUFFER_DEFAULT_LENGTH;
123  }
124  _fd = ::open(fileName_.c_str(), O_CREAT | O_RDWR, (mode_t)0644);
125  struct stat statBuf;
126  memset(&statBuf, 0, sizeof(statBuf));
127  if (fstat(_fd, &statBuf) == -1)
128  {
129  std::ostringstream os;
130  os << "Failed to stat file " << fileName_ << " for MMapStoreBuffer";
131  error(os.str());
132  }
133  bool recovery = (size_t)statBuf.st_size >= initialSize_;
134  setStoredSizeAlignment(fileName_, (size_t)statBuf.st_size,
135  useStoredSizeAlignment_);
136  _setSize(recovery ? (size_t)statBuf.st_size
137  : initialSize_,
138  recovery);
139  }
140  catch (...)
141  {
142  closeOnConstructionError();
143  throw;
144  }
145  }
146 #endif
147 
148  ~MMapStoreBuffer()
149  {
150  if (_buffer)
151  {
152  close();
153  }
154  }
155 
156  void close()
157  {
158  sync();
159 #ifdef _WIN32
160  FlushFileBuffers(_file);
161  UnmapViewOfFile(_buffer);
162  CloseHandle(_mapFile);
163  CloseHandle(_file);
164  _mapFile = INVALID_HANDLE_VALUE;
165  _file = INVALID_HANDLE_VALUE;
166 #else
167  munmap(_buffer, _bufferLen);
168  ::close(_fd);
169  _fd = 0;
170 #endif
171  _buffer = NULL;
172  _bufferLen = 0;
173  }
174 
175  void sync()
176  {
177  if (_buffer != NULL && _bufferLen)
178  {
179 #ifdef _WIN32
180  if (!FlushViewOfFile(_buffer, _bufferPos))
181 #else
182  if (msync(_buffer, _bufferPos, MS_ASYNC) != 0)
183 #endif
184  {
185  std::ostringstream os;
186  os << "Failed to sync mapped memory; buffer: " << (size_t)_buffer
187  << " pos: " << _bufferPos;
188  error(os.str());
189  }
190  }
191  }
192 
193  virtual void setSize(size_t newSize_)
194  {
195  _setSize(newSize_);
196  }
197 
200  void setSizeAlignment(size_t sizeAlignment_)
201  {
202  const size_t alignment = sizeAlignment_ ? sizeAlignment_ : 1;
203  if (_sizeAlignment == alignment)
204  {
205  return;
206  }
207  const size_t alignedSize = getMappedSize(_bufferLen, alignment);
208  _sizeAlignment = alignment;
209  if (_bufferLen && alignedSize != _bufferLen)
210  {
211  _setSize(alignedSize);
212  }
213  }
214 
215  void _setSize(size_t newSize_, bool recovery_ = false)
216  {
217  if (_bufferLen > 0)
218  {
219  sync();
220  }
221  // Keep the logical buffer aligned to the caller's allocation unit.
222  // Mapping APIs handle partial final pages internally.
223  size_t sz = getMappedSize(newSize_);
224 #ifdef _WIN32
225  if (_mapFile != INVALID_HANDLE_VALUE && _mapFile != NULL)
226  {
227  FlushFileBuffers(_file);
228  UnmapViewOfFile(_buffer);
229  CloseHandle(_mapFile);
230  }
231 #ifdef _WIN64
232  _mapFile = CreateFileMapping( _file, NULL, PAGE_READWRITE, (DWORD)((sz >> 32) & 0xffffffff), (DWORD)sz, NULL);
233 #else
234  _mapFile = CreateFileMapping( _file, NULL, PAGE_READWRITE, 0, (DWORD)sz, NULL);
235 #endif
236  if (_mapFile == INVALID_HANDLE_VALUE || _mapFile == NULL)
237  {
238  _buffer = 0;
239  _bufferLen = 0;
240  error("Failed to create map of log file");
241  }
242  else
243  {
244  _buffer = (char*)MapViewOfFile(_mapFile, FILE_MAP_ALL_ACCESS, 0, 0, sz);
245  if (_buffer == NULL)
246  {
247  std::ostringstream os;
248  os << "Failed to map log file to memory; buffer: " << (size_t)_buffer << " length: " << sz << " previous size: " << _bufferLen;
249  _buffer = 0;
250  _bufferLen = 0;
251  error(os.str());
252  }
253  }
254 #else
255  // If not at current size, extend the underlying file
256  if (sz > _bufferLen)
257  {
258  if (lseek(_fd, (off_t)sz - 1, SEEK_SET) == -1)
259  {
260  std::ostringstream os;
261  os << "Seek failed for buffer extension; buffer: " << (size_t)_buffer
262  << " length: " << _bufferLen << " pos: " << _bufferPos
263  << " requested new size: " << newSize_;
264  error(os.str());
265  }
266  if (!recovery_ || sz > newSize_)
267  {
268  if (::write(_fd, "", 1) == -1)
269  {
270  std::ostringstream os;
271  os << "Failed to grow buffer; buffer: " << (size_t)_buffer << " length: "
272  << _bufferLen << " pos: " << _bufferPos << " requested new size: "
273  << newSize_;
274  error(os.str());
275  }
276  }
277  }
278 
279  void* result = NULL;
280  if (_buffer == NULL)
281  {
282  result = mmap(0, sz, PROT_WRITE | PROT_READ, MAP_SHARED, _fd, 0);
283  }
284  else if (_bufferLen < sz)
285  {
286 #if defined(linux)
287  result = mremap(_buffer, _bufferLen, sz, MREMAP_MAYMOVE);
288 #else
289  munmap(_buffer, _bufferLen);
290  result = mmap(0, sz, PROT_WRITE | PROT_READ, MAP_SHARED, _fd, 0);
291 #endif
292  }
293  if (result == MAP_FAILED || result == NULL)
294  {
295  std::ostringstream os;
296  os << "Failed to map log file to memory; buffer: "
297  << (size_t)_buffer << " length: " << sz
298  << " previous size: " << _bufferLen;
299  _buffer = 0;
300  _bufferLen = 0;
301  error(os.str());
302  }
303  else
304  {
305  _buffer = (char*)result;
306  }
307 #endif
308  if (_buffer)
309  {
310  _bufferLen = sz;
311  }
312  }
313 
314  private:
315  void setStoredSizeAlignment(const std::string& fileName_, size_t fileSize_,
316  bool useStoredSizeAlignment_)
317  {
318  if (!useStoredSizeAlignment_
319  || fileSize_ < sizeof(amps_uint32_t))
320  {
321  return;
322  }
323  amps_uint32_t storedSizeAlignment = 0;
324 #ifdef _WIN32
325  LARGE_INTEGER beginning;
326  beginning.QuadPart = 0;
327  DWORD bytesRead = 0;
328  const DWORD alignmentSize = (DWORD)sizeof(storedSizeAlignment);
329  bool readFailed = !SetFilePointerEx(_file, beginning, NULL, FILE_BEGIN)
330  || !ReadFile(_file, &storedSizeAlignment,
331  alignmentSize, &bytesRead, NULL)
332  || bytesRead != alignmentSize;
333 #else
334  bool readFailed = pread(_fd, &storedSizeAlignment,
335  sizeof(storedSizeAlignment), 0)
336  != (ssize_t)sizeof(storedSizeAlignment);
337 #endif
338  if (readFailed)
339  {
340  std::ostringstream os;
341  os << "Failure reading size alignment from " << fileName_
342  << " for MMapStoreBuffer";
343  error(os.str());
344  }
345  if (storedSizeAlignment > 1)
346  {
347  if ((size_t)storedSizeAlignment > fileSize_)
348  {
349  throw StoreException(
350  "MMapStoreBuffer contains an invalid size alignment.");
351  }
352  _sizeAlignment = storedSizeAlignment;
353  }
354  }
355 
356  void closeOnConstructionError()
357  {
358 #ifdef _WIN32
359  if (_buffer)
360  {
361  UnmapViewOfFile(_buffer);
362  }
363  if (_mapFile != INVALID_HANDLE_VALUE && _mapFile != NULL)
364  {
365  CloseHandle(_mapFile);
366  }
367  if (_file != INVALID_HANDLE_VALUE)
368  {
369  CloseHandle(_file);
370  }
371  _mapFile = INVALID_HANDLE_VALUE;
372  _file = INVALID_HANDLE_VALUE;
373 #else
374  if (_bufferLen > 0)
375  {
376  munmap(_buffer, _bufferLen);
377  }
378  if (_fd >= 0)
379  {
380  ::close(_fd);
381  }
382  _fd = -1;
383 #endif
384  _buffer = NULL;
385  _bufferLen = 0;
386  }
387 
388  size_t getMappedSize(size_t requestedSize_) const
389  {
390  return getMappedSize(requestedSize_, _sizeAlignment);
391  }
392 
393  size_t getMappedSize(size_t requestedSize_, size_t sizeAlignment_) const
394  {
395  const size_t alignment = sizeAlignment_ > 1
396  ? sizeAlignment_ : getPageSize();
397  if (requestedSize_ > (std::numeric_limits<size_t>::max)()
398  - (alignment - 1))
399  {
400  throw StoreException("MMapStoreBuffer size overflow.");
401  }
402  return (requestedSize_ + alignment - 1) / alignment * alignment;
403  }
404 
405  void error(const std::string & message)
406  {
407  std::ostringstream os;
408 #ifdef _WIN32
409  const size_t errorBufferSize = 1024;
410  char errorBuffer[errorBufferSize];
411  memset(errorBuffer, 0, errorBufferSize);
412  strerror_s(errorBuffer, errorBufferSize, errno);
413  os << message << ". Error is " << errorBuffer;
414 #else
415  os << message << ". Error is " << strerror(errno);
416 #endif
417  throw StoreException(os.str());
418  }
419 
420  size_t _sizeAlignment;
421 #ifdef _WIN32
422  HANDLE _mapFile;
423  HANDLE _file;
424 #else
425  int _fd;
426 #endif
427  static size_t getPageSize()
428  {
429  static const size_t pageSize = queryPageSize(); // -V1096
430  return pageSize;
431  }
432 
433  static size_t queryPageSize()
434  {
435  size_t pageSize;
436 #ifdef _WIN32
437  SYSTEM_INFO SYS_INFO;
438  GetSystemInfo(&SYS_INFO);
439  pageSize = SYS_INFO.dwPageSize;
440 #else
441  pageSize = (size_t)sysconf(_SC_PAGESIZE);
442 #endif
443  if (pageSize == (size_t)-1)
444  {
445  // Guess
446  pageSize = 4096;
447  }
448  return pageSize;
449  }
450 
451  };
452 
453 } // end namespace AMPS
454 
455 #endif //_MMAPSTOREBUFFER_H_
456 
virtual void setSize(size_t newSize_)
Set the size for the buffer.
Definition: MMapStoreBuffer.hpp:193
A Buffer implementation that uses memory for storage.
Definition: MemoryStoreBuffer.hpp:50
void setSizeAlignment(size_t sizeAlignment_)
Set the allocation-unit alignment for logical buffer sizes.
Definition: MMapStoreBuffer.hpp:200
Core type, function, and class declarations for the AMPS C++ client.
A Buffer implementation that uses a memory mapped file as its storage.
Definition: MMapStoreBuffer.hpp:50
Provides AMPS::MemoryStoreBuffer, used by an AMPS::HAClient to store messages in memory.
MMapStoreBuffer(const std::string &fileName_, size_t initialSize_=AMPS_MEMORYBUFFER_DEFAULT_LENGTH, size_t sizeAlignment_=1, bool useStoredSizeAlignment_=false)
Create an MMapStoreBuffer using fileName_ as the name of the memory mapped file used for storage...
Definition: MMapStoreBuffer.hpp:110
Definition: AMPSException.hpp:32