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.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/ext/mesh/extconf.rb +22 -4
- data/ext/mesh/mesh.tar.gz +0 -0
- data/lib/mesh/version.rb +1 -1
- data/mesh.gemspec +3 -2
- metadata +4 -120
- data/ext/mesh/mesh/.bazelrc +0 -20
- data/ext/mesh/mesh/.bazelversion +0 -1
- data/ext/mesh/mesh/.clang-format +0 -15
- data/ext/mesh/mesh/.dockerignore +0 -5
- data/ext/mesh/mesh/.editorconfig +0 -16
- data/ext/mesh/mesh/.gitattributes +0 -4
- data/ext/mesh/mesh/.github/workflows/main.yml +0 -144
- data/ext/mesh/mesh/.gitignore +0 -51
- data/ext/mesh/mesh/AUTHORS +0 -5
- data/ext/mesh/mesh/CMakeLists.txt +0 -270
- data/ext/mesh/mesh/CODE_OF_CONDUCT.md +0 -77
- data/ext/mesh/mesh/Dockerfile +0 -30
- data/ext/mesh/mesh/LICENSE +0 -201
- data/ext/mesh/mesh/Makefile +0 -81
- data/ext/mesh/mesh/README.md +0 -97
- data/ext/mesh/mesh/WORKSPACE +0 -50
- data/ext/mesh/mesh/bazel +0 -350
- data/ext/mesh/mesh/mesh-pldi19-powers.pdf +0 -0
- data/ext/mesh/mesh/src/BUILD +0 -222
- data/ext/mesh/mesh/src/CMakeLists.txt +0 -85
- data/ext/mesh/mesh/src/bitmap.h +0 -590
- data/ext/mesh/mesh/src/cheap_heap.h +0 -170
- data/ext/mesh/mesh/src/common.h +0 -377
- data/ext/mesh/mesh/src/copts.bzl +0 -31
- data/ext/mesh/mesh/src/d_assert.cc +0 -75
- data/ext/mesh/mesh/src/fixed_array.h +0 -124
- data/ext/mesh/mesh/src/global_heap.cc +0 -547
- data/ext/mesh/mesh/src/global_heap.h +0 -569
- data/ext/mesh/mesh/src/gnu_wrapper.cc +0 -75
- data/ext/mesh/mesh/src/internal.h +0 -356
- data/ext/mesh/mesh/src/libmesh.cc +0 -239
- data/ext/mesh/mesh/src/mac_wrapper.cc +0 -528
- data/ext/mesh/mesh/src/measure_rss.cc +0 -44
- data/ext/mesh/mesh/src/measure_rss.h +0 -20
- data/ext/mesh/mesh/src/meshable_arena.cc +0 -776
- data/ext/mesh/mesh/src/meshable_arena.h +0 -309
- data/ext/mesh/mesh/src/meshing.h +0 -60
- data/ext/mesh/mesh/src/mini_heap.h +0 -532
- data/ext/mesh/mesh/src/mmap_heap.h +0 -104
- data/ext/mesh/mesh/src/one_way_mmap_heap.h +0 -77
- data/ext/mesh/mesh/src/partitioned_heap.h +0 -111
- data/ext/mesh/mesh/src/plasma/mesh.h +0 -33
- data/ext/mesh/mesh/src/real.cc +0 -52
- data/ext/mesh/mesh/src/real.h +0 -36
- data/ext/mesh/mesh/src/rng/mwc.h +0 -296
- data/ext/mesh/mesh/src/rng/mwc64.h +0 -58
- data/ext/mesh/mesh/src/rpl_printf.c +0 -1991
- data/ext/mesh/mesh/src/runtime.cc +0 -393
- data/ext/mesh/mesh/src/runtime.h +0 -114
- data/ext/mesh/mesh/src/shuffle_vector.h +0 -287
- data/ext/mesh/mesh/src/size_classes.def +0 -251
- data/ext/mesh/mesh/src/static/if.h +0 -36
- data/ext/mesh/mesh/src/static/log.h +0 -43
- data/ext/mesh/mesh/src/testing/benchmark/local_refill.cc +0 -103
- data/ext/mesh/mesh/src/testing/big-alloc.c +0 -28
- data/ext/mesh/mesh/src/testing/fragmenter.cc +0 -128
- data/ext/mesh/mesh/src/testing/global-large-stress.cc +0 -25
- data/ext/mesh/mesh/src/testing/local-alloc.c +0 -16
- data/ext/mesh/mesh/src/testing/meshing_benchmark.cc +0 -189
- data/ext/mesh/mesh/src/testing/thread.cc +0 -35
- data/ext/mesh/mesh/src/testing/unit/alignment.cc +0 -56
- data/ext/mesh/mesh/src/testing/unit/bitmap_test.cc +0 -274
- data/ext/mesh/mesh/src/testing/unit/concurrent_mesh_test.cc +0 -185
- data/ext/mesh/mesh/src/testing/unit/mesh_test.cc +0 -143
- data/ext/mesh/mesh/src/testing/unit/rng_test.cc +0 -22
- data/ext/mesh/mesh/src/testing/unit/size_class_test.cc +0 -66
- data/ext/mesh/mesh/src/testing/unit/triple_mesh_test.cc +0 -285
- data/ext/mesh/mesh/src/testing/userfaultfd-kernel-copy.cc +0 -164
- data/ext/mesh/mesh/src/thread_local_heap.cc +0 -163
- data/ext/mesh/mesh/src/thread_local_heap.h +0 -268
- data/ext/mesh/mesh/src/wrapper.cc +0 -433
- data/ext/mesh/mesh/support/export_mesh.cmake +0 -28
- data/ext/mesh/mesh/support/gen-size-classes +0 -57
- data/ext/mesh/mesh/support/install_all_configs +0 -33
- data/ext/mesh/mesh/support/remove_export_mesh.cmake +0 -48
- data/ext/mesh/mesh/support/update-bazelisk +0 -8
- data/ext/mesh/mesh/theory/32m80.png +0 -0
- data/ext/mesh/mesh/theory/64m80ind.png +0 -0
- data/ext/mesh/mesh/theory/bound_comparison.py +0 -67
- data/ext/mesh/mesh/theory/bounds/impdeg+1 +0 -135
- data/ext/mesh/mesh/theory/choose.py +0 -43
- data/ext/mesh/mesh/theory/common.py +0 -42
- data/ext/mesh/mesh/theory/compute_exp_Y.py +0 -134
- data/ext/mesh/mesh/theory/createRandomString.py +0 -69
- data/ext/mesh/mesh/theory/deg_bound_check.py +0 -100
- data/ext/mesh/mesh/theory/degcheck.py +0 -47
- data/ext/mesh/mesh/theory/dumps/32,1,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,2,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,3,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,4,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,5,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,6,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,7,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,8,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/dumps/32,9,80,dumb.txt +0 -81
- data/ext/mesh/mesh/theory/experiment.py +0 -303
- data/ext/mesh/mesh/theory/experiment_raw_results/.gitignore +0 -0
- data/ext/mesh/mesh/theory/greedy_experiment.py +0 -66
- data/ext/mesh/mesh/theory/greedy_experiment_copy.py +0 -46
- data/ext/mesh/mesh/theory/greedy_experiment_q.py +0 -75
- data/ext/mesh/mesh/theory/makeGraph.py +0 -64
- data/ext/mesh/mesh/theory/manyreps.png +0 -0
- data/ext/mesh/mesh/theory/manystrings.png +0 -0
- data/ext/mesh/mesh/theory/match_vs_color_experiment.py +0 -94
- data/ext/mesh/mesh/theory/maxmatch_vs_E[Y].py +0 -162
- data/ext/mesh/mesh/theory/maxmatch_vs_greedymatch.py +0 -96
- data/ext/mesh/mesh/theory/maxvdeg+1imp++32,80.png +0 -0
- data/ext/mesh/mesh/theory/mesh_util.py +0 -322
- data/ext/mesh/mesh/theory/meshers.py +0 -452
- data/ext/mesh/mesh/theory/meshingBenchmark.py +0 -96
- data/ext/mesh/mesh/theory/occupancyComparison.py +0 -133
- data/ext/mesh/mesh/theory/randmatch_vs_greedymatch.py +0 -97
- data/ext/mesh/mesh/theory/randmatch_vs_greedymatch_q.py +0 -103
- data/ext/mesh/mesh/theory/randmatch_vs_greedymatch_time.py +0 -117
- data/ext/mesh/mesh/theory/read_mesh_dump.py +0 -82
- data/ext/mesh/mesh/theory/test.py +0 -70
- 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
|