join 1.0
lightweight network framework library
Loading...
Searching...
No Matches
memory.hpp
Go to the documentation of this file.
1
25#ifndef JOIN_CORE_MEMORY_HPP
26#define JOIN_CORE_MEMORY_HPP
27
28// libjoin.
29#include <join/error.hpp>
30#include <join/utils.hpp>
31
32// C++.
33#include <system_error>
34#include <stdexcept>
35#include <limits>
36#include <new>
37
38// C.
39#include <sys/types.h>
40#include <sys/mman.h>
41#include <sys/stat.h>
42#include <unistd.h>
43#ifdef JOIN_HAS_NUMA
44#include <numaif.h>
45#include <numa.h>
46#endif
47#include <fcntl.h>
48#include <cstddef>
49#include <cstdint>
50#include <cstdlib>
51
52namespace join
53{
55 class BumpAtomic;
56
57 class BumpPlain;
58
59 template <typename Backend, size_t Capacity, typename SyncPolicy>
61
62 template <typename Backend, size_t Count, size_t... Sizes>
64
66 template <typename Backend, template <typename, typename> class SyncPolicy>
67 struct SyncBinding;
68
69 template <typename, typename>
70 struct Spsc;
71
72 template <typename, typename>
73 struct Mpsc;
74
75 template <typename, typename>
76 struct Mpmc;
77
82 inline uint64_t pageSize () noexcept
83 {
84 long size = ::sysconf (_SC_PAGESIZE);
85 return (size > 0) ? static_cast<uint64_t> (size) : 4096;
86 }
87
92 inline uint64_t hugePageSize () noexcept
93 {
94 static const uint64_t size = [] () {
95 uint64_t bytes = 0;
96 int fd = ::memfd_create ("hugepage", MFD_HUGETLB | MFD_CLOEXEC);
97 if (fd != -1)
98 {
99 struct stat st;
100 if (::fstat (fd, &st) == 0)
101 {
102 bytes = static_cast<uint64_t> (st.st_blksize);
103 }
104 ::close (fd);
105 }
106 return bytes;
107 }();
108
109 return size;
110 }
111
112#ifdef JOIN_HAS_NUMA
120 inline int mbind (void* ptr, size_t size, int numa) noexcept
121 {
122 if (ptr == nullptr || numa < 0 || numa > (static_cast<int> (sizeof (unsigned long) * 8) - 1))
123 {
125 return -1;
126 }
127
128 unsigned long mask = (1UL << numa);
129 if (::mbind (ptr, size, MPOL_BIND, &mask, sizeof (mask) * 8, MPOL_MF_STRICT | MPOL_MF_MOVE) == -1)
130 {
131 lastError = std::error_code (errno, std::generic_category ());
132 return -1;
133 }
134
135 return 0;
136 }
137#endif
138
145 inline int mlock (void* ptr, size_t size) noexcept
146 {
147 if (ptr == nullptr)
148 {
150 return -1;
151 }
152
153 if (::mlock (ptr, size) == -1)
154 {
155 // LCOV_EXCL_START
156 lastError = std::error_code (errno, std::generic_category ());
157 return -1;
158 // LCOV_EXCL_STOP
159 }
160
161 return 0;
162 }
163
168 {
169 public:
170 template <size_t Capacity, typename SyncPolicy = BumpAtomic>
172
176 HeapMem () noexcept = default;
177
183 explicit HeapMem (uint64_t size)
184 : _ptr (((size != 0) && (size <= static_cast<uint64_t> (std::numeric_limits<ptrdiff_t>::max ())))
185 ? std::malloc (size)
186 : nullptr)
187 {
188 if (_ptr == nullptr)
189 {
190 throw std::bad_alloc ();
191 }
192 _size = size;
193 }
194
199 HeapMem (const HeapMem& other) = delete;
200
206 HeapMem& operator= (const HeapMem& other) = delete;
207
212 HeapMem (HeapMem&& other) noexcept
213 : _ptr (other._ptr)
214 , _size (other._size)
215 {
216 other._ptr = nullptr;
217 other._size = 0;
218 }
219
225 HeapMem& operator= (HeapMem&& other) noexcept
226 {
227 cleanup ();
228
229 _ptr = other._ptr;
230 _size = other._size;
231
232 other._ptr = nullptr;
233 other._size = 0;
234
235 return *this;
236 }
237
241 ~HeapMem () noexcept
242 {
243 cleanup ();
244 }
245
250 bool mapped () const noexcept
251 {
252 return _ptr != nullptr;
253 }
254
262 const void* get (uint64_t offset = 0) const
263 {
264 if (JOIN_UNLIKELY (_ptr == nullptr))
265 {
266 throw std::runtime_error ("memory not mapped");
267 }
268
269 if (JOIN_UNLIKELY (offset >= _size))
270 {
271 throw std::out_of_range ("offset out of bounds");
272 }
273
274 return static_cast<const char*> (_ptr) + offset;
275 }
276
284 void* get (uint64_t offset = 0)
285 {
286 if (JOIN_UNLIKELY (_ptr == nullptr))
287 {
288 throw std::runtime_error ("memory not mapped");
289 }
290
291 if (JOIN_UNLIKELY (offset >= _size))
292 {
293 throw std::out_of_range ("offset out of bounds");
294 }
295
296 return static_cast<char*> (_ptr) + offset;
297 }
298
299 private:
303 void cleanup () noexcept
304 {
305 std::free (_ptr);
306 _ptr = nullptr;
307 _size = 0;
308 }
309
311 void* _ptr = nullptr;
312
314 uint64_t _size = 0;
315 };
316
321 {
322 public:
323 template <size_t Capacity, typename SyncPolicy = BumpAtomic>
325
326 template <size_t Count, size_t... Sizes>
327 using SlabAllocator = BasicSlabAllocator<LocalMem, Count, Sizes...>;
328
332
336 LocalMem () noexcept = default;
337
343 explicit LocalMem (uint64_t size)
344 {
345 uint64_t page = pageSize ();
346 _size = (size + page - 1) & ~(page - 1);
347 create ();
348 }
349
354 LocalMem (const LocalMem& other) = delete;
355
361 LocalMem& operator= (const LocalMem& other) = delete;
362
367 LocalMem (LocalMem&& other) noexcept
368 : _size (other._size)
369 , _ptr (other._ptr)
370 {
371 other._size = 0;
372 other._ptr = nullptr;
373 }
374
380 LocalMem& operator= (LocalMem&& other) noexcept
381 {
382 cleanup ();
383
384 _size = other._size;
385 _ptr = other._ptr;
386
387 other._size = 0;
388 other._ptr = nullptr;
389
390 return *this;
391 }
392
396 ~LocalMem () noexcept
397 {
398 cleanup ();
399 }
400
405 bool mapped () const noexcept
406 {
407 return _ptr != nullptr;
408 }
409
417 const void* get (uint64_t offset = 0) const
418 {
419 if (JOIN_UNLIKELY (_ptr == nullptr))
420 {
421 throw std::runtime_error ("memory not mapped");
422 }
423
424 if (JOIN_UNLIKELY (offset >= _size))
425 {
426 throw std::out_of_range ("offset out of bounds");
427 }
428
429 return static_cast<const char*> (_ptr) + offset;
430 }
431
439 void* get (uint64_t offset = 0)
440 {
441 if (JOIN_UNLIKELY (_ptr == nullptr))
442 {
443 throw std::runtime_error ("memory not mapped");
444 }
445
446 if (JOIN_UNLIKELY (offset >= _size))
447 {
448 throw std::out_of_range ("offset out of bounds");
449 }
450
451 return static_cast<char*> (_ptr) + offset;
452 }
453
454#ifdef JOIN_HAS_NUMA
460 int mbind (int numa) const noexcept
461 {
462 return join::mbind (_ptr, _size, numa);
463 }
464#endif
465
470 int mlock () const noexcept
471 {
472 return join::mlock (_ptr, _size);
473 }
474
475 private:
480 void create ()
481 {
482 uint64_t huge = hugePageSize ();
483
484 if ((huge != 0) && (_size >= huge))
485 {
486 uint64_t size = (_size + huge - 1) & ~(huge - 1);
487
488 _ptr = ::mmap (nullptr, size, PROT_READ | PROT_WRITE,
489 MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE | MAP_HUGETLB, -1, 0);
490 if (_ptr != MAP_FAILED)
491 {
492 // LCOV_EXCL_START
493 _size = size;
494 return;
495 // LCOV_EXCL_STOP
496 }
497
498 if ((errno != ENOMEM) && (errno != EINVAL))
499 {
500 throw std::system_error (errno, std::generic_category (), "mmap failed"); // LCOV_EXCL_LINE
501 }
502 }
503
504 _ptr = ::mmap (nullptr, _size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_POPULATE, -1, 0);
505 if (_ptr == MAP_FAILED)
506 {
507 throw std::system_error (errno, std::generic_category (), "mmap failed");
508 }
509 }
510
514 void cleanup () noexcept
515 {
516 if ((_ptr != nullptr) && (_ptr != MAP_FAILED))
517 {
518 ::munlock (_ptr, _size);
519 ::munmap (_ptr, _size);
520 _ptr = nullptr;
521 }
522
523 _size = 0;
524 }
525
527 uint64_t _size = 0;
528
530 void* _ptr = nullptr;
531 };
532
536 class ShmMem
537 {
538 public:
539 template <size_t Count, size_t... Sizes>
540 using SlabAllocator = BasicSlabAllocator<ShmMem, Count, Sizes...>;
541
545
552 explicit ShmMem (uint64_t size, const std::string& name)
553 : _name (name)
554 {
555 uint64_t page = pageSize ();
556 _size = (size + page - 1) & ~(page - 1);
557 if (_size > static_cast<uint64_t> (std::numeric_limits<off_t>::max ()))
558 {
559 throw std::overflow_error ("size will overflow");
560 }
561 create ();
562 }
563
568 ShmMem (const ShmMem& other) = delete;
569
575 ShmMem& operator= (const ShmMem& other) = delete;
576
581 ShmMem (ShmMem&& other) noexcept
582 : _size (other._size)
583 , _name (std::move (other._name))
584 , _ptr (other._ptr)
585 , _fd (other._fd)
586 {
587 other._size = 0;
588 other._ptr = nullptr;
589 other._fd = -1;
590 }
591
597 ShmMem& operator= (ShmMem&& other) noexcept
598 {
599 cleanup ();
600
601 _size = other._size;
602 _name = std::move (other._name);
603 _ptr = other._ptr;
604 _fd = other._fd;
605
606 other._size = 0;
607 other._ptr = nullptr;
608 other._fd = -1;
609
610 return *this;
611 }
612
616 ~ShmMem () noexcept
617 {
618 cleanup ();
619 }
620
625 bool mapped () const noexcept
626 {
627 return _ptr != nullptr;
628 }
629
637 const void* get (uint64_t offset = 0) const
638 {
639 if (JOIN_UNLIKELY (_ptr == nullptr))
640 {
641 throw std::runtime_error ("memory not mapped");
642 }
643
644 if (JOIN_UNLIKELY (offset >= _size))
645 {
646 throw std::out_of_range ("offset out of bounds");
647 }
648
649 return static_cast<const char*> (_ptr) + offset;
650 }
651
659 void* get (uint64_t offset = 0)
660 {
661 if (JOIN_UNLIKELY (_ptr == nullptr))
662 {
663 throw std::runtime_error ("memory not mapped");
664 }
665
666 if (JOIN_UNLIKELY (offset >= _size))
667 {
668 throw std::out_of_range ("offset out of bounds");
669 }
670
671 return static_cast<char*> (_ptr) + offset;
672 }
673
674#ifdef JOIN_HAS_NUMA
680 int mbind (int numa) const noexcept
681 {
682 return join::mbind (_ptr, _size, numa);
683 }
684#endif
685
690 int mlock () const noexcept
691 {
692 return join::mlock (_ptr, _size);
693 }
694
700 static int unlink (const std::string& name) noexcept
701 {
702 if (::shm_unlink (name.c_str ()) == -1)
703 {
704 if (errno == ENOENT)
705 {
706 return 0;
707 }
708 lastError = std::error_code (errno, std::generic_category ());
709 return -1;
710 }
711
712 return 0;
713 }
714
715 private:
720 void create ()
721 {
722 bool created = true;
723
724 _fd = ::shm_open (_name.c_str (), O_CREAT | O_RDWR | O_EXCL | O_CLOEXEC, 0644);
725 if ((_fd == -1) && (errno == EEXIST))
726 {
727 created = false;
728 _fd = ::shm_open (_name.c_str (), O_RDWR | O_CLOEXEC, 0644);
729 }
730
731 if (_fd == -1)
732 {
733 throw std::system_error (errno, std::generic_category (), "shm_open failed");
734 }
735
736 if (!created)
737 {
738 struct stat st;
739
740 if (fstat (_fd, &st) == -1)
741 {
742 // LCOV_EXCL_START
743 int err = errno;
744 ::close (_fd);
745 throw std::system_error (err, std::generic_category (), "fstat failed");
746 // LCOV_EXCL_STOP
747 }
748
749 if (static_cast<uint64_t> (st.st_size) != _size)
750 {
751 ::close (_fd);
752 throw std::runtime_error ("shared memory size mismatch");
753 }
754 }
755 else
756 {
757 if (::ftruncate (_fd, _size) == -1)
758 {
759 int err = errno;
760 ::close (_fd);
761 throw std::system_error (err, std::generic_category (), "ftruncate failed");
762 }
763 }
764
765 _ptr = ::mmap (nullptr, _size, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0);
766
767 if (_ptr == MAP_FAILED)
768 {
769 int err = errno;
770 ::close (_fd);
771 throw std::system_error (err, std::generic_category (), "mmap failed");
772 }
773 }
774
778 void cleanup () noexcept
779 {
780 if ((_ptr != nullptr) && (_ptr != MAP_FAILED))
781 {
782 ::munlock (_ptr, _size);
783 ::munmap (_ptr, _size);
784 _ptr = nullptr;
785 }
786
787 if (_fd != -1)
788 {
789 ::close (_fd);
790 _fd = -1;
791 }
792
793 _name.clear ();
794 _size = 0;
795 }
796
798 uint64_t _size = 0;
799
801 std::string _name;
802
804 void* _ptr = nullptr;
805
807 int _fd = -1;
808 };
809}
810
811#endif
bump allocator owning its backends.
Definition memory.hpp:60
slab allocator owning a backend and managing one or more pools.
Definition slab_allocator.hpp:339
heap memory provider.
Definition memory.hpp:168
HeapMem(HeapMem &&other) noexcept
move constructor.
Definition memory.hpp:212
void * get(uint64_t offset=0)
get a pointer to the memory at a given offset.
Definition memory.hpp:284
bool mapped() const noexcept
check if the memory is allocated.
Definition memory.hpp:250
HeapMem(const HeapMem &other)=delete
copy constructor.
const void * get(uint64_t offset=0) const
get a const pointer to the memory at a given offset.
Definition memory.hpp:262
~HeapMem() noexcept
releases the memory.
Definition memory.hpp:241
HeapMem() noexcept=default
default constructor.
HeapMem & operator=(const HeapMem &other)=delete
copy assignment operator.
local anonymous memory provider.
Definition memory.hpp:321
int mlock() const noexcept
lock memory in RAM.
Definition memory.hpp:470
const void * get(uint64_t offset=0) const
get a const pointer to the memory at a given offset.
Definition memory.hpp:417
LocalMem & operator=(const LocalMem &other)=delete
copy assignment operator.
void * get(uint64_t offset=0)
get a pointer to the memory at a given offset.
Definition memory.hpp:439
LocalMem(const LocalMem &other)=delete
copy constructor.
LocalMem() noexcept=default
default constructor.
LocalMem(LocalMem &&other) noexcept
move constructor.
Definition memory.hpp:367
bool mapped() const noexcept
check if the memory is mapped.
Definition memory.hpp:405
~LocalMem() noexcept
releases the mapped memory.
Definition memory.hpp:396
posix shared memory provider.
Definition memory.hpp:537
bool mapped() const noexcept
check if the shared memory is mapped.
Definition memory.hpp:625
void * get(uint64_t offset=0)
get a pointer to the shared memory at a given offset.
Definition memory.hpp:659
ShmMem(const ShmMem &other)=delete
copy constructor.
ShmMem & operator=(const ShmMem &other)=delete
copy assignment operator.
static int unlink(const std::string &name) noexcept
destroy synchronization primitives and unlink the shared memory segment.
Definition memory.hpp:700
int mlock() const noexcept
lock memory in RAM.
Definition memory.hpp:690
ShmMem(ShmMem &&other) noexcept
move constructor.
Definition memory.hpp:581
~ShmMem() noexcept
unmaps the memory and closes the file descriptor.
Definition memory.hpp:616
ShmMem(uint64_t size, const std::string &name)
creates or opens a named shared memory segment.
Definition memory.hpp:552
const void * get(uint64_t offset=0) const
get a const pointer to the shared memory at a given offset.
Definition memory.hpp:637
Definition acceptor.hpp:32
uint64_t hugePageSize() noexcept
get the default huge page size.
Definition memory.hpp:92
uint64_t pageSize() noexcept
get the system page size.
Definition memory.hpp:82
int mlock(void *ptr, size_t size) noexcept
lock memory in RAM.
Definition memory.hpp:145
std::error_code make_error_code(join::Errc code) noexcept
Create an std::error_code object.
Definition error.cpp:195
Definition error.hpp:144
const std::string _name
Definition shared_futex_test.cpp:45
multiple producer multiple consumer ring buffer.
Definition queue.hpp:844
multiple producer single consumer ring buffer.
Definition queue.hpp:651
single producer single consumer ring buffer.
Definition queue.hpp:470
queue forward declarations.
Definition queue.hpp:1037
#define JOIN_UNLIKELY(x)
Definition utils.hpp:51