mesh-rb 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +1 -1
  3. data/ext/mesh/extconf.rb +22 -4
  4. data/ext/mesh/mesh.tar.gz +0 -0
  5. data/lib/mesh/version.rb +1 -1
  6. data/mesh.gemspec +3 -2
  7. metadata +4 -120
  8. data/ext/mesh/mesh/.bazelrc +0 -20
  9. data/ext/mesh/mesh/.bazelversion +0 -1
  10. data/ext/mesh/mesh/.clang-format +0 -15
  11. data/ext/mesh/mesh/.dockerignore +0 -5
  12. data/ext/mesh/mesh/.editorconfig +0 -16
  13. data/ext/mesh/mesh/.gitattributes +0 -4
  14. data/ext/mesh/mesh/.github/workflows/main.yml +0 -144
  15. data/ext/mesh/mesh/.gitignore +0 -51
  16. data/ext/mesh/mesh/AUTHORS +0 -5
  17. data/ext/mesh/mesh/CMakeLists.txt +0 -270
  18. data/ext/mesh/mesh/CODE_OF_CONDUCT.md +0 -77
  19. data/ext/mesh/mesh/Dockerfile +0 -30
  20. data/ext/mesh/mesh/LICENSE +0 -201
  21. data/ext/mesh/mesh/Makefile +0 -81
  22. data/ext/mesh/mesh/README.md +0 -97
  23. data/ext/mesh/mesh/WORKSPACE +0 -50
  24. data/ext/mesh/mesh/bazel +0 -350
  25. data/ext/mesh/mesh/mesh-pldi19-powers.pdf +0 -0
  26. data/ext/mesh/mesh/src/BUILD +0 -222
  27. data/ext/mesh/mesh/src/CMakeLists.txt +0 -85
  28. data/ext/mesh/mesh/src/bitmap.h +0 -590
  29. data/ext/mesh/mesh/src/cheap_heap.h +0 -170
  30. data/ext/mesh/mesh/src/common.h +0 -377
  31. data/ext/mesh/mesh/src/copts.bzl +0 -31
  32. data/ext/mesh/mesh/src/d_assert.cc +0 -75
  33. data/ext/mesh/mesh/src/fixed_array.h +0 -124
  34. data/ext/mesh/mesh/src/global_heap.cc +0 -547
  35. data/ext/mesh/mesh/src/global_heap.h +0 -569
  36. data/ext/mesh/mesh/src/gnu_wrapper.cc +0 -75
  37. data/ext/mesh/mesh/src/internal.h +0 -356
  38. data/ext/mesh/mesh/src/libmesh.cc +0 -239
  39. data/ext/mesh/mesh/src/mac_wrapper.cc +0 -528
  40. data/ext/mesh/mesh/src/measure_rss.cc +0 -44
  41. data/ext/mesh/mesh/src/measure_rss.h +0 -20
  42. data/ext/mesh/mesh/src/meshable_arena.cc +0 -776
  43. data/ext/mesh/mesh/src/meshable_arena.h +0 -309
  44. data/ext/mesh/mesh/src/meshing.h +0 -60
  45. data/ext/mesh/mesh/src/mini_heap.h +0 -532
  46. data/ext/mesh/mesh/src/mmap_heap.h +0 -104
  47. data/ext/mesh/mesh/src/one_way_mmap_heap.h +0 -77
  48. data/ext/mesh/mesh/src/partitioned_heap.h +0 -111
  49. data/ext/mesh/mesh/src/plasma/mesh.h +0 -33
  50. data/ext/mesh/mesh/src/real.cc +0 -52
  51. data/ext/mesh/mesh/src/real.h +0 -36
  52. data/ext/mesh/mesh/src/rng/mwc.h +0 -296
  53. data/ext/mesh/mesh/src/rng/mwc64.h +0 -58
  54. data/ext/mesh/mesh/src/rpl_printf.c +0 -1991
  55. data/ext/mesh/mesh/src/runtime.cc +0 -393
  56. data/ext/mesh/mesh/src/runtime.h +0 -114
  57. data/ext/mesh/mesh/src/shuffle_vector.h +0 -287
  58. data/ext/mesh/mesh/src/size_classes.def +0 -251
  59. data/ext/mesh/mesh/src/static/if.h +0 -36
  60. data/ext/mesh/mesh/src/static/log.h +0 -43
  61. data/ext/mesh/mesh/src/testing/benchmark/local_refill.cc +0 -103
  62. data/ext/mesh/mesh/src/testing/big-alloc.c +0 -28
  63. data/ext/mesh/mesh/src/testing/fragmenter.cc +0 -128
  64. data/ext/mesh/mesh/src/testing/global-large-stress.cc +0 -25
  65. data/ext/mesh/mesh/src/testing/local-alloc.c +0 -16
  66. data/ext/mesh/mesh/src/testing/meshing_benchmark.cc +0 -189
  67. data/ext/mesh/mesh/src/testing/thread.cc +0 -35
  68. data/ext/mesh/mesh/src/testing/unit/alignment.cc +0 -56
  69. data/ext/mesh/mesh/src/testing/unit/bitmap_test.cc +0 -274
  70. data/ext/mesh/mesh/src/testing/unit/concurrent_mesh_test.cc +0 -185
  71. data/ext/mesh/mesh/src/testing/unit/mesh_test.cc +0 -143
  72. data/ext/mesh/mesh/src/testing/unit/rng_test.cc +0 -22
  73. data/ext/mesh/mesh/src/testing/unit/size_class_test.cc +0 -66
  74. data/ext/mesh/mesh/src/testing/unit/triple_mesh_test.cc +0 -285
  75. data/ext/mesh/mesh/src/testing/userfaultfd-kernel-copy.cc +0 -164
  76. data/ext/mesh/mesh/src/thread_local_heap.cc +0 -163
  77. data/ext/mesh/mesh/src/thread_local_heap.h +0 -268
  78. data/ext/mesh/mesh/src/wrapper.cc +0 -433
  79. data/ext/mesh/mesh/support/export_mesh.cmake +0 -28
  80. data/ext/mesh/mesh/support/gen-size-classes +0 -57
  81. data/ext/mesh/mesh/support/install_all_configs +0 -33
  82. data/ext/mesh/mesh/support/remove_export_mesh.cmake +0 -48
  83. data/ext/mesh/mesh/support/update-bazelisk +0 -8
  84. data/ext/mesh/mesh/theory/32m80.png +0 -0
  85. data/ext/mesh/mesh/theory/64m80ind.png +0 -0
  86. data/ext/mesh/mesh/theory/bound_comparison.py +0 -67
  87. data/ext/mesh/mesh/theory/bounds/impdeg+1 +0 -135
  88. data/ext/mesh/mesh/theory/choose.py +0 -43
  89. data/ext/mesh/mesh/theory/common.py +0 -42
  90. data/ext/mesh/mesh/theory/compute_exp_Y.py +0 -134
  91. data/ext/mesh/mesh/theory/createRandomString.py +0 -69
  92. data/ext/mesh/mesh/theory/deg_bound_check.py +0 -100
  93. data/ext/mesh/mesh/theory/degcheck.py +0 -47
  94. data/ext/mesh/mesh/theory/dumps/32,1,80,dumb.txt +0 -81
  95. data/ext/mesh/mesh/theory/dumps/32,2,80,dumb.txt +0 -81
  96. data/ext/mesh/mesh/theory/dumps/32,3,80,dumb.txt +0 -81
  97. data/ext/mesh/mesh/theory/dumps/32,4,80,dumb.txt +0 -81
  98. data/ext/mesh/mesh/theory/dumps/32,5,80,dumb.txt +0 -81
  99. data/ext/mesh/mesh/theory/dumps/32,6,80,dumb.txt +0 -81
  100. data/ext/mesh/mesh/theory/dumps/32,7,80,dumb.txt +0 -81
  101. data/ext/mesh/mesh/theory/dumps/32,8,80,dumb.txt +0 -81
  102. data/ext/mesh/mesh/theory/dumps/32,9,80,dumb.txt +0 -81
  103. data/ext/mesh/mesh/theory/experiment.py +0 -303
  104. data/ext/mesh/mesh/theory/experiment_raw_results/.gitignore +0 -0
  105. data/ext/mesh/mesh/theory/greedy_experiment.py +0 -66
  106. data/ext/mesh/mesh/theory/greedy_experiment_copy.py +0 -46
  107. data/ext/mesh/mesh/theory/greedy_experiment_q.py +0 -75
  108. data/ext/mesh/mesh/theory/makeGraph.py +0 -64
  109. data/ext/mesh/mesh/theory/manyreps.png +0 -0
  110. data/ext/mesh/mesh/theory/manystrings.png +0 -0
  111. data/ext/mesh/mesh/theory/match_vs_color_experiment.py +0 -94
  112. data/ext/mesh/mesh/theory/maxmatch_vs_E[Y].py +0 -162
  113. data/ext/mesh/mesh/theory/maxmatch_vs_greedymatch.py +0 -96
  114. data/ext/mesh/mesh/theory/maxvdeg+1imp++32,80.png +0 -0
  115. data/ext/mesh/mesh/theory/mesh_util.py +0 -322
  116. data/ext/mesh/mesh/theory/meshers.py +0 -452
  117. data/ext/mesh/mesh/theory/meshingBenchmark.py +0 -96
  118. data/ext/mesh/mesh/theory/occupancyComparison.py +0 -133
  119. data/ext/mesh/mesh/theory/randmatch_vs_greedymatch.py +0 -97
  120. data/ext/mesh/mesh/theory/randmatch_vs_greedymatch_q.py +0 -103
  121. data/ext/mesh/mesh/theory/randmatch_vs_greedymatch_time.py +0 -117
  122. data/ext/mesh/mesh/theory/read_mesh_dump.py +0 -82
  123. data/ext/mesh/mesh/theory/test.py +0 -70
  124. data/ext/mesh/mesh/tools/bazel +0 -1
@@ -1,164 +0,0 @@
1
- #include <fcntl.h>
2
- #include <linux/userfaultfd.h>
3
- #include <sys/ioctl.h>
4
- #include <sys/mman.h>
5
- #include <sys/syscall.h>
6
- #include <sys/types.h>
7
- #include <unistd.h>
8
-
9
- #include <cstdarg>
10
- #include <cstring>
11
- #include <thread>
12
-
13
- constexpr size_t kPageSize = 4096;
14
- constexpr size_t kDataLen = 128;
15
- constexpr size_t kArenaSize = kPageSize * 2;
16
-
17
- void __attribute__((noreturn)) die(const char *fmt, ...) {
18
- va_list args;
19
-
20
- va_start(args, fmt);
21
- vfprintf(stderr, fmt, args);
22
- va_end(args);
23
-
24
- exit(EXIT_FAILURE);
25
- }
26
-
27
- void kernelCopy(int tmpFd, char *addr, char const *knownGood) {
28
- errno = 0;
29
- ssize_t len = read(tmpFd, addr, kDataLen);
30
- if (len != kDataLen) {
31
- fprintf(stderr, "read: %s (len: %zd)\n", strerror(errno), len);
32
- return;
33
- }
34
-
35
- if (memcmp(knownGood, addr, kDataLen) != 0) {
36
- die("data read from file doesn't match knownGood\n");
37
- }
38
-
39
- printf("read from file went as expected; data looks good.\n");
40
- }
41
-
42
- void setupPageTrap(int faultFd, char *arenaBegin, size_t pageOff, size_t len) {
43
- char *mappingBegin = arenaBegin + pageOff * kPageSize;
44
-
45
- // step 0: register the range with userfaultfd
46
- struct uffdio_register ufRegister = {};
47
- ufRegister.range.start = reinterpret_cast<unsigned long>(mappingBegin);
48
- ufRegister.range.len = len;
49
- ufRegister.mode = UFFDIO_REGISTER_MODE_WP;
50
- if (ioctl(faultFd, UFFDIO_REGISTER, &ufRegister) == -1) {
51
- die("ioctl(UFFDIO_REGISTER): %s\n", strerror(errno));
52
- }
53
-
54
- // step 1: use userfaultfd (rather than mprotect) to remap the pages read-only
55
- struct uffdio_writeprotect ufWriteProtect = {};
56
- ufWriteProtect.range.start = reinterpret_cast<unsigned long>(mappingBegin);
57
- ufWriteProtect.range.len = len;
58
- ufWriteProtect.mode = UFFDIO_WRITEPROTECT_MODE_WP;
59
- if (ioctl(faultFd, UFFDIO_WRITEPROTECT, &ufWriteProtect) == -1) {
60
- die("ioctl(UFFDIO_WRITEPROTECT): %s\n", strerror(errno));
61
- }
62
- }
63
-
64
- int main() {
65
- // get some random bytes + write it to a temp file
66
- char data[kDataLen] = {};
67
- if (getentropy(data, kDataLen) == -1) {
68
- die("getentropy: %s\n", strerror(errno));
69
- }
70
-
71
- char tmpFilename[] = "/tmp/userfaultfd-test-XXXXXX";
72
- int tmpFd = mkstemp(tmpFilename);
73
- if (tmpFd == -1) {
74
- die("mkstemp: %s\n", strerror(errno));
75
- }
76
- if (unlink(tmpFilename) == -1) {
77
- die("unlink: %s\n", strerror(errno));
78
- }
79
-
80
- size_t len = write(tmpFd, data, kDataLen);
81
- if (len < kDataLen) {
82
- die("write: partial write of %d\n", len);
83
- }
84
- if (lseek(tmpFd, 0, SEEK_SET) != 0) {
85
- die("lseek failed\n");
86
- }
87
-
88
- // the write-protection patchset doesn't yet support non-anonymous (or hugepage) VMAs
89
- char *arenaBegin = reinterpret_cast<char *>(
90
- mmap(nullptr, kArenaSize, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0));
91
- if (arenaBegin == MAP_FAILED) {
92
- die("mmap: %s\n", strerror(errno));
93
- }
94
-
95
- // pre-fault things in; the write protection stuff below doesn't seem to work
96
- // well if the pages aren't already faulted in. in a real allocator/GC the pages
97
- // we're dealing with will be faulted in so this seems fine.
98
- for (size_t i = 0; i < kArenaSize; i += kPageSize) {
99
- arenaBegin[i] = 0;
100
- }
101
-
102
- int faultFd = static_cast<int>(syscall(__NR_userfaultfd, O_CLOEXEC));
103
- if (faultFd == -1) {
104
- die("userfaultfd: %s\n", strerror(errno));
105
- }
106
-
107
- // the only feature we care about is write-protection faults -- this will fail
108
- // on a mainline kernel as the patchset isn't upstream yet.
109
- struct uffdio_api ufApi = {};
110
- ufApi.api = UFFD_API;
111
- ufApi.features = UFFD_FEATURE_PAGEFAULT_FLAG_WP;
112
- if (ioctl(faultFd, UFFDIO_API, &ufApi) == -1) {
113
- die("ioctl(UFFDIO_API): %s\n", strerror(errno));
114
- }
115
- if (ufApi.api != UFFD_API) {
116
- die("ioctl(UFFDIO_API) API version mismatch %lld != %lld\n", ufApi.api, UFFD_API);
117
- }
118
-
119
- // prefault in the entire arena to match the fact that the heap will already
120
- // be faulted in when meshing pages together
121
- for (size_t i = 0; i < kArenaSize; i += kPageSize) {
122
- arenaBegin[i] = 0;
123
- }
124
-
125
- // write-protect part of the arena
126
- setupPageTrap(faultFd, arenaBegin, 0, kPageSize);
127
-
128
- printf("part of the arena is read-only now and writes raise an event on our userfaultfd\n");
129
-
130
- // even works with kernelspace; +57 is to verify that non-page-aligned access work
131
- std::thread t1(kernelCopy, tmpFd, arenaBegin + 57, data);
132
-
133
- // now suspend until we receive an event from the kernelCopy thread
134
- struct uffd_msg msg = {};
135
- int nRead = read(faultFd, &msg, sizeof(msg));
136
- if (nRead == 0) {
137
- die("EOF on userfaultfd!\n");
138
- } else if (nRead == -1) {
139
- die("read: %s\n", strerror(errno));
140
- }
141
-
142
- if (msg.event != UFFD_EVENT_PAGEFAULT) {
143
- die("Unexpected event on userfaultfd\n");
144
- }
145
-
146
- printf(" UFFD_EVENT_PAGEFAULT event: ");
147
- printf("flags = %llx; ", msg.arg.pagefault.flags);
148
- printf("address = %llx\n", msg.arg.pagefault.address);
149
-
150
- sleep(2);
151
-
152
- printf("removing write protection and waking up other thread\n");
153
-
154
- // turn off write-protection; implicitly wakes the other thread up
155
- struct uffdio_writeprotect ufWriteProtect = {};
156
- ufWriteProtect.range.start = msg.arg.pagefault.address;
157
- ufWriteProtect.range.len = kPageSize;
158
- ufWriteProtect.mode = 0;
159
- if (ioctl(faultFd, UFFDIO_WRITEPROTECT, &ufWriteProtect) == -1) {
160
- die("ioctl(UFFDIO_WRITEPROTECT): %s\n", strerror(errno));
161
- }
162
-
163
- t1.join();
164
- }
@@ -1,163 +0,0 @@
1
- // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2
- // Copyright 2019 The Mesh Authors. All rights reserved.
3
- // Use of this source code is governed by the Apache License,
4
- // Version 2.0, that can be found in the LICENSE file.
5
-
6
- #include "thread_local_heap.h"
7
-
8
- namespace mesh {
9
-
10
- #ifdef MESH_HAVE_TLS
11
- __thread ThreadLocalHeap *ThreadLocalHeap::_threadLocalHeap ATTR_INITIAL_EXEC CACHELINE_ALIGNED;
12
- #endif
13
- ThreadLocalHeap *ThreadLocalHeap::_threadLocalHeaps{nullptr};
14
- bool ThreadLocalHeap::_tlhInitialized{false};
15
- pthread_key_t ThreadLocalHeap::_heapKey{0};
16
-
17
- void ThreadLocalHeap::DestroyThreadLocalHeap(void *ptr) {
18
- if (ptr != nullptr) {
19
- #ifdef MESH_HAVE_TLS
20
- _threadLocalHeap = nullptr;
21
- #endif
22
- DeleteHeap(reinterpret_cast<ThreadLocalHeap *>(ptr));
23
- }
24
- }
25
-
26
- // similar to what TCMalloc does; the constructor initializes our
27
- // pthread key and does this in such a way that we aren't recursively
28
- // calling into pthread code (and deadlocking).
29
- class MeshMallocGuard {
30
- public:
31
- MeshMallocGuard() {
32
- ThreadLocalHeap::InitTLH();
33
- }
34
- };
35
-
36
- static MeshMallocGuard module_initialization_hook;
37
-
38
- void ThreadLocalHeap::InitTLH() {
39
- hard_assert(!_tlhInitialized);
40
- pthread_key_create(&_heapKey, DestroyThreadLocalHeap);
41
- _tlhInitialized = true;
42
- }
43
-
44
- ThreadLocalHeap *ThreadLocalHeap::NewHeap(pthread_t current) {
45
- // we just allocate out of our internal heap
46
- void *buf = mesh::internal::Heap().malloc(sizeof(ThreadLocalHeap));
47
- static_assert(sizeof(ThreadLocalHeap) < 4096 * 8, "tlh should have a reasonable size");
48
- hard_assert(buf != nullptr);
49
- hard_assert(reinterpret_cast<uintptr_t>(buf) % CACHELINE_SIZE == 0);
50
-
51
- auto heap = new (buf) ThreadLocalHeap(&mesh::runtime().heap(), current);
52
-
53
- heap->_prev = nullptr;
54
- heap->_next = _threadLocalHeaps;
55
- if (_threadLocalHeaps != nullptr) {
56
- _threadLocalHeaps->_prev = heap;
57
- }
58
- _threadLocalHeaps = heap;
59
-
60
- return heap;
61
- }
62
-
63
- ThreadLocalHeap *ThreadLocalHeap::CreateHeapIfNecessary() {
64
- #ifdef MESH_HAVE_TLS
65
- const bool maybeReentrant = !_tlhInitialized;
66
- // check to see if we really need to create the heap
67
- if (_tlhInitialized && _threadLocalHeap != nullptr) {
68
- return _threadLocalHeap;
69
- }
70
- #else
71
- const bool maybeReentrant = true;
72
- #endif
73
-
74
- ThreadLocalHeap *heap = nullptr;
75
-
76
- {
77
- std::lock_guard<GlobalHeap> lock(mesh::runtime().heap());
78
-
79
- const pthread_t current = pthread_self();
80
-
81
- if (maybeReentrant) {
82
- for (ThreadLocalHeap *h = _threadLocalHeaps; h != nullptr; h = h->_next) {
83
- if (h->_pthreadCurrent == current) {
84
- heap = h;
85
- break;
86
- }
87
- }
88
- }
89
-
90
- if (heap == nullptr) {
91
- heap = NewHeap(current);
92
- }
93
- }
94
-
95
- if (!heap->_inSetSpecific && _tlhInitialized) {
96
- heap->_inSetSpecific = true;
97
- #ifdef MESH_HAVE_TLS
98
- _threadLocalHeap = heap;
99
- #endif
100
- pthread_setspecific(_heapKey, heap);
101
- heap->_inSetSpecific = false;
102
- }
103
-
104
- return heap;
105
- }
106
-
107
- void ThreadLocalHeap::DeleteHeap(ThreadLocalHeap *heap) {
108
- if (heap == nullptr) {
109
- return;
110
- }
111
-
112
- // manually invoke the destructor
113
- heap->ThreadLocalHeap::~ThreadLocalHeap();
114
-
115
- if (heap->_next != nullptr) {
116
- heap->_next->_prev = heap->_prev;
117
- }
118
- if (heap->_prev != nullptr) {
119
- heap->_prev->_next = heap->_next;
120
- }
121
- if (_threadLocalHeaps == heap) {
122
- _threadLocalHeaps = heap->_next;
123
- }
124
-
125
- mesh::internal::Heap().free(reinterpret_cast<void *>(heap));
126
- }
127
-
128
- void ThreadLocalHeap::releaseAll() {
129
- for (size_t i = 1; i < kNumBins; i++) {
130
- _shuffleVector[i].refillMiniheaps();
131
- _global->releaseMiniheaps(_shuffleVector[i].miniheaps());
132
- }
133
- }
134
-
135
- // we get here if the shuffleVector is exhausted
136
- void *CACHELINE_ALIGNED_FN ThreadLocalHeap::smallAllocSlowpath(size_t sizeClass) {
137
- ShuffleVector &shuffleVector = _shuffleVector[sizeClass];
138
-
139
- // we grab multiple MiniHeaps at a time from the global heap. often
140
- // it is possible to refill the freelist from a not-yet-used
141
- // MiniHeap we already have, without global cross-thread
142
- // synchronization
143
- if (likely(shuffleVector.localRefill())) {
144
- return shuffleVector.malloc();
145
- }
146
-
147
- return smallAllocGlobalRefill(shuffleVector, sizeClass);
148
- }
149
-
150
- void *CACHELINE_ALIGNED_FN ThreadLocalHeap::smallAllocGlobalRefill(ShuffleVector &shuffleVector, size_t sizeClass) {
151
- const size_t sizeMax = SizeMap::ByteSizeForClass(sizeClass);
152
-
153
- _global->allocSmallMiniheaps(sizeClass, sizeMax, shuffleVector.miniheaps(), _current);
154
- shuffleVector.reinit();
155
-
156
- d_assert(!shuffleVector.isExhausted());
157
-
158
- void *ptr = shuffleVector.malloc();
159
- d_assert(ptr != nullptr);
160
-
161
- return ptr;
162
- }
163
- } // namespace mesh
@@ -1,268 +0,0 @@
1
- // -*- mode: c++; c-basic-offset: 2; indent-tabs-mode: nil -*-
2
- // Copyright 2019 The Mesh Authors. All rights reserved.
3
- // Use of this source code is governed by the Apache License,
4
- // Version 2.0, that can be found in the LICENSE file.
5
-
6
- #ifndef MESH_THREAD_LOCAL_HEAP_H
7
- #define MESH_THREAD_LOCAL_HEAP_H
8
-
9
- #if !defined(_WIN32)
10
- #include <pthread.h>
11
- #include <stdalign.h>
12
- #endif
13
-
14
- #include <sys/types.h>
15
-
16
- #include <algorithm>
17
- #include <atomic>
18
-
19
- #include "internal.h"
20
- #include "mini_heap.h"
21
- #include "shuffle_vector.h"
22
-
23
- #include "rng/mwc.h"
24
-
25
- #include "heaplayers.h"
26
-
27
- #include "runtime.h"
28
-
29
- using namespace HL;
30
-
31
- namespace mesh {
32
-
33
- class LocalHeapStats {
34
- public:
35
- atomic_size_t allocCount{0};
36
- atomic_size_t freeCount{0};
37
- };
38
-
39
- class ThreadLocalHeap {
40
- private:
41
- DISALLOW_COPY_AND_ASSIGN(ThreadLocalHeap);
42
-
43
- public:
44
- enum { Alignment = 16 };
45
-
46
- ThreadLocalHeap(GlobalHeap *global, pthread_t pthreadCurrent)
47
- : _current(gettid()),
48
- _global(global),
49
- _pthreadCurrent(pthreadCurrent),
50
- _prng(internal::seed(), internal::seed()),
51
- _maxObjectSize(SizeMap::ByteSizeForClass(kNumBins - 1)) {
52
- const auto arenaBegin = _global->arenaBegin();
53
- // when asked, give 16-byte allocations for 0-byte requests
54
- _shuffleVector[0].initialInit(arenaBegin, SizeMap::ByteSizeForClass(1));
55
- for (size_t i = 1; i < kNumBins; i++) {
56
- _shuffleVector[i].initialInit(arenaBegin, SizeMap::ByteSizeForClass(i));
57
- }
58
- d_assert(_global != nullptr);
59
- }
60
-
61
- ~ThreadLocalHeap() {
62
- releaseAll();
63
- }
64
-
65
- // pthread_set_sepcific destructor
66
- static void DestroyThreadLocalHeap(void *ptr);
67
-
68
- static void InitTLH();
69
-
70
- void releaseAll();
71
-
72
- void *ATTRIBUTE_NEVER_INLINE CACHELINE_ALIGNED_FN smallAllocSlowpath(size_t sizeClass);
73
- void *ATTRIBUTE_NEVER_INLINE CACHELINE_ALIGNED_FN smallAllocGlobalRefill(ShuffleVector &shuffleVector,
74
- size_t sizeClass);
75
-
76
- inline void *memalign(size_t alignment, size_t size) {
77
- // Check for non power-of-two alignment.
78
- if ((alignment == 0) || (alignment & (alignment - 1))) {
79
- return nullptr;
80
- }
81
-
82
- if (size < 8) {
83
- size = 8;
84
- }
85
-
86
- uint32_t sizeClass = 0;
87
- const bool isSmall = SizeMap::GetSizeClass(size, &sizeClass);
88
- if (alignment <= sizeof(double)) {
89
- // all of our size classes are at least 8-byte aligned
90
- auto ptr = this->malloc(size);
91
- d_assert_msg((reinterpret_cast<uintptr_t>(ptr) % alignment) == 0, "%p(%zu) %% %zu != 0", ptr, size, alignment);
92
- return ptr;
93
- } else if (isSmall) {
94
- const auto sizeClassBytes = SizeMap::ByteSizeForClass(sizeClass);
95
- // if the alignment is for a small allocation that is less than
96
- // the page size, and the size class size in bytes is a multiple
97
- // of the alignment, just call malloc
98
- if (sizeClassBytes <= kPageSize && alignment <= sizeClassBytes && (sizeClassBytes % alignment) == 0) {
99
- auto ptr = this->malloc(size);
100
- d_assert_msg((reinterpret_cast<uintptr_t>(ptr) % alignment) == 0, "%p(%zu) %% %zu != 0", ptr, size, alignment);
101
- return ptr;
102
- }
103
- }
104
-
105
- // fall back to page-aligned allocation
106
- const size_t pageAlignment = (alignment + kPageSize - 1) / kPageSize;
107
- const size_t pageCount = PageCount(size);
108
- return _global->pageAlignedAlloc(pageAlignment, pageCount);
109
- }
110
-
111
- inline void *ATTRIBUTE_ALWAYS_INLINE realloc(void *oldPtr, size_t newSize) {
112
- if (oldPtr == nullptr) {
113
- return this->malloc(newSize);
114
- }
115
-
116
- if (newSize == 0) {
117
- this->free(oldPtr);
118
- return this->malloc(newSize);
119
- }
120
-
121
- size_t oldSize = getSize(oldPtr);
122
-
123
- // the following is directly from tcmalloc, designed to avoid
124
- // 'resizing ping pongs'
125
- const size_t lowerBoundToGrow = oldSize + oldSize / 4ul;
126
- const size_t upperBoundToShrink = oldSize / 2ul;
127
-
128
- if (newSize > oldSize || newSize < upperBoundToShrink) {
129
- void *newPtr = nullptr;
130
- if (newSize > oldSize && newSize < lowerBoundToGrow) {
131
- newPtr = this->malloc(lowerBoundToGrow);
132
- }
133
- if (newPtr == nullptr) {
134
- newPtr = this->malloc(newSize);
135
- }
136
- if (unlikely(newPtr == nullptr)) {
137
- return nullptr;
138
- }
139
- const size_t copySize = (oldSize < newSize) ? oldSize : newSize;
140
- memcpy(newPtr, oldPtr, copySize);
141
- this->free(oldPtr);
142
- return newPtr;
143
- } else {
144
- // the current allocation is good enough
145
- return oldPtr;
146
- }
147
- }
148
-
149
- inline void *ATTRIBUTE_ALWAYS_INLINE calloc(size_t count, size_t size) {
150
- if (unlikely(size && count > (size_t)-1 / size)) {
151
- errno = ENOMEM;
152
- return nullptr;
153
- }
154
-
155
- const size_t n = count * size;
156
- void *ptr = this->malloc(n);
157
-
158
- if (ptr != nullptr) {
159
- memset(ptr, 0, n);
160
- }
161
-
162
- return ptr;
163
- }
164
-
165
- inline void *ATTRIBUTE_ALWAYS_INLINE cxxNew(size_t sz) {
166
- void *ptr = this->malloc(sz);
167
- if (unlikely(ptr == NULL && sz != 0)) {
168
- throw std::bad_alloc();
169
- }
170
-
171
- return ptr;
172
- }
173
-
174
- // semiansiheap ensures we never see size == 0
175
- inline void *ATTRIBUTE_ALWAYS_INLINE malloc(size_t sz) {
176
- uint32_t sizeClass = 0;
177
-
178
- // if the size isn't in our sizemap it is a large alloc
179
- if (unlikely(!SizeMap::GetSizeClass(sz, &sizeClass))) {
180
- return _global->malloc(sz);
181
- }
182
-
183
- ShuffleVector &shuffleVector = _shuffleVector[sizeClass];
184
- if (unlikely(shuffleVector.isExhausted())) {
185
- return smallAllocSlowpath(sizeClass);
186
- }
187
-
188
- return shuffleVector.malloc();
189
- }
190
-
191
- inline void ATTRIBUTE_ALWAYS_INLINE free(void *ptr) {
192
- if (unlikely(ptr == nullptr))
193
- return;
194
-
195
- size_t startEpoch{0};
196
- auto mh = _global->miniheapForWithEpoch(ptr, startEpoch);
197
- if (likely(mh && mh->current() == _current && !mh->hasMeshed())) {
198
- ShuffleVector &shuffleVector = _shuffleVector[mh->sizeClass()];
199
- shuffleVector.free(mh, ptr);
200
- return;
201
- }
202
-
203
- _global->freeFor(mh, ptr, startEpoch);
204
- }
205
-
206
- inline void ATTRIBUTE_ALWAYS_INLINE sizedFree(void *ptr, size_t sz) {
207
- this->free(ptr);
208
- }
209
-
210
- inline size_t getSize(void *ptr) {
211
- if (unlikely(ptr == nullptr))
212
- return 0;
213
-
214
- auto mh = _global->miniheapFor(ptr);
215
- if (likely(mh && mh->current() == _current)) {
216
- ShuffleVector &shuffleVector = _shuffleVector[mh->sizeClass()];
217
- return shuffleVector.getSize();
218
- }
219
-
220
- return _global->getSize(ptr);
221
- }
222
-
223
- static ThreadLocalHeap *NewHeap(pthread_t current);
224
- static ThreadLocalHeap *GetHeapIfPresent() {
225
- #ifdef MESH_HAVE_TLS
226
- return _threadLocalHeap;
227
- #else
228
- return _tlhInitialized ? reinterpret_cast<ThreadLocalHeap *>(pthread_getspecific(_heapKey)) : nullptr;
229
- #endif
230
- }
231
-
232
- static void DeleteHeap(ThreadLocalHeap *heap);
233
-
234
- static ThreadLocalHeap *GetHeap() {
235
- auto heap = GetHeapIfPresent();
236
- if (unlikely(heap == nullptr)) {
237
- return CreateHeapIfNecessary();
238
- }
239
- return heap;
240
- }
241
-
242
- static ThreadLocalHeap *ATTRIBUTE_NEVER_INLINE CreateHeapIfNecessary();
243
-
244
- protected:
245
- ShuffleVector _shuffleVector[kNumBins] CACHELINE_ALIGNED;
246
- // this cacheline is read-mostly (only changed when creating + destroying threads)
247
- const pid_t _current CACHELINE_ALIGNED{0};
248
- GlobalHeap *_global;
249
- ThreadLocalHeap *_next{}; // protected by global heap lock
250
- ThreadLocalHeap *_prev{};
251
- const pthread_t _pthreadCurrent;
252
- MWC _prng CACHELINE_ALIGNED;
253
- const size_t _maxObjectSize;
254
- LocalHeapStats _stats{};
255
- bool _inSetSpecific{false};
256
-
257
- #ifdef MESH_HAVE_TLS
258
- static __thread ThreadLocalHeap *_threadLocalHeap CACHELINE_ALIGNED ATTR_INITIAL_EXEC;
259
- #endif
260
-
261
- static ThreadLocalHeap *_threadLocalHeaps;
262
- static bool _tlhInitialized;
263
- static pthread_key_t _heapKey;
264
- };
265
-
266
- } // namespace mesh
267
-
268
- #endif // MESH_THREAD_LOCAL_HEAP_H