Skip to content

Commit cf6cdbb

Browse files
authored
Revert "Mimalloc v3 update (#26379)" (#26783)
This reverts commit c63415c. ### What does this PR do? ### How did you verify your code works?
1 parent 89d2b1c commit cf6cdbb

File tree

4 files changed

+105
-55
lines changed

4 files changed

+105
-55
lines changed

cmake/targets/BuildMimalloc.cmake

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ register_repository(
44
REPOSITORY
55
oven-sh/mimalloc
66
COMMIT
7-
ffa38ab8ac914f9eb7af75c1f8ad457643dc14f2
7+
1beadf9651a7bfdec6b5367c380ecc3fe1c40d1a
88
)
99

1010
set(MIMALLOC_CMAKE_ARGS
@@ -14,7 +14,7 @@ set(MIMALLOC_CMAKE_ARGS
1414
-DMI_BUILD_TESTS=OFF
1515
-DMI_USE_CXX=ON
1616
-DMI_SKIP_COLLECT_ON_EXIT=ON
17-
17+
1818
# ```
1919
# ❯ mimalloc_allow_large_os_pages=0 BUN_PORT=3004 mem bun http-hello.js
2020
# Started development server: http://localhost:3004
@@ -51,7 +51,7 @@ if(ENABLE_ASAN)
5151
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_DEBUG_UBSAN=ON)
5252
elseif(APPLE OR LINUX)
5353
if(APPLE)
54-
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
54+
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OVERRIDE=OFF)
5555
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_ZONE=OFF)
5656
list(APPEND MIMALLOC_CMAKE_ARGS -DMI_OSX_INTERPOSE=OFF)
5757
else()
@@ -87,9 +87,9 @@ endif()
8787

8888
if(WIN32)
8989
if(DEBUG)
90-
set(MIMALLOC_LIBRARY mimalloc-debug)
90+
set(MIMALLOC_LIBRARY mimalloc-static-debug)
9191
else()
92-
set(MIMALLOC_LIBRARY mimalloc)
92+
set(MIMALLOC_LIBRARY mimalloc-static)
9393
endif()
9494
elseif(DEBUG)
9595
if (ENABLE_ASAN)

src/allocators/MimallocArena.zig

Lines changed: 92 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,7 @@
22

33
const Self = @This();
44

5-
const safety_checks = bun.Environment.isDebug or bun.Environment.enable_asan;
6-
7-
#heap: *mimalloc.Heap,
8-
thread_id: if (safety_checks) std.Thread.Id else void,
5+
#heap: if (safety_checks) Owned(*DebugHeap) else *mimalloc.Heap,
96

107
/// Uses the default thread-local heap. This type is zero-sized.
118
///
@@ -23,18 +20,18 @@ pub const Default = struct {
2320
///
2421
/// This type is a `GenericAllocator`; see `src/allocators.zig`.
2522
pub const Borrowed = struct {
26-
#heap: *mimalloc.Heap,
23+
#heap: BorrowedHeap,
2724

2825
pub fn allocator(self: Borrowed) std.mem.Allocator {
29-
return .{ .ptr = self.#heap, .vtable = c_allocator_vtable };
26+
return .{ .ptr = self.#heap, .vtable = &c_allocator_vtable };
3027
}
3128

3229
pub fn getDefault() Borrowed {
33-
return .{ .#heap = mimalloc.mi_heap_main() };
30+
return .{ .#heap = getThreadHeap() };
3431
}
3532

3633
pub fn gc(self: Borrowed) void {
37-
mimalloc.mi_heap_collect(self.#heap, false);
34+
mimalloc.mi_heap_collect(self.getMimallocHeap(), false);
3835
}
3936

4037
pub fn helpCatchMemoryIssues(self: Borrowed) void {
@@ -44,17 +41,30 @@ pub const Borrowed = struct {
4441
}
4542
}
4643

44+
pub fn ownsPtr(self: Borrowed, ptr: *const anyopaque) bool {
45+
return mimalloc.mi_heap_check_owned(self.getMimallocHeap(), ptr);
46+
}
47+
4748
fn fromOpaque(ptr: *anyopaque) Borrowed {
4849
return .{ .#heap = @ptrCast(@alignCast(ptr)) };
4950
}
5051

52+
fn getMimallocHeap(self: Borrowed) *mimalloc.Heap {
53+
return if (comptime safety_checks) self.#heap.inner else self.#heap;
54+
}
55+
56+
fn assertThreadLock(self: Borrowed) void {
57+
if (comptime safety_checks) self.#heap.thread_lock.assertLocked();
58+
}
59+
5160
fn alignedAlloc(self: Borrowed, len: usize, alignment: Alignment) ?[*]u8 {
5261
log("Malloc: {d}\n", .{len});
5362

63+
const heap = self.getMimallocHeap();
5464
const ptr: ?*anyopaque = if (mimalloc.mustUseAlignedAlloc(alignment))
55-
mimalloc.mi_heap_malloc_aligned(self.#heap, len, alignment.toByteUnits())
65+
mimalloc.mi_heap_malloc_aligned(heap, len, alignment.toByteUnits())
5666
else
57-
mimalloc.mi_heap_malloc(self.#heap, len);
67+
mimalloc.mi_heap_malloc(heap, len);
5868

5969
if (comptime bun.Environment.isDebug) {
6070
const usable = mimalloc.mi_malloc_usable_size(ptr);
@@ -79,17 +89,42 @@ pub const Borrowed = struct {
7989
}
8090
};
8191

92+
const BorrowedHeap = if (safety_checks) *DebugHeap else *mimalloc.Heap;
93+
94+
const DebugHeap = struct {
95+
inner: *mimalloc.Heap,
96+
thread_lock: bun.safety.ThreadLock,
97+
98+
pub const deinit = void;
99+
};
100+
101+
threadlocal var thread_heap: if (safety_checks) ?DebugHeap else void = if (safety_checks) null;
102+
103+
fn getThreadHeap() BorrowedHeap {
104+
if (comptime !safety_checks) return mimalloc.mi_heap_get_default();
105+
if (thread_heap == null) {
106+
thread_heap = .{
107+
.inner = mimalloc.mi_heap_get_default(),
108+
.thread_lock = .initLocked(),
109+
};
110+
}
111+
return &thread_heap.?;
112+
}
113+
82114
const log = bun.Output.scoped(.mimalloc, .hidden);
83115

84116
pub fn allocator(self: Self) std.mem.Allocator {
85-
self.assertThreadOwnership();
86117
return self.borrow().allocator();
87118
}
88119

89120
pub fn borrow(self: Self) Borrowed {
90-
return .{ .#heap = self.#heap };
121+
return .{ .#heap = if (comptime safety_checks) self.#heap.get() else self.#heap };
91122
}
92123

124+
/// Internally, mimalloc calls mi_heap_get_default()
125+
/// to get the default heap.
126+
/// It uses pthread_getspecific to do that.
127+
/// We can save those extra calls if we just do it once in here
93128
pub fn getThreadLocalDefault() std.mem.Allocator {
94129
if (bun.Environment.enable_asan) return bun.default_allocator;
95130
return Borrowed.getDefault().allocator();
@@ -122,15 +157,22 @@ pub fn dumpStats(_: Self) void {
122157
}
123158

124159
pub fn deinit(self: *Self) void {
125-
mimalloc.mi_heap_destroy(self.#heap);
160+
const mimalloc_heap = self.borrow().getMimallocHeap();
161+
if (comptime safety_checks) {
162+
self.#heap.deinit();
163+
}
164+
mimalloc.mi_heap_destroy(mimalloc_heap);
126165
self.* = undefined;
127166
}
128167

129168
pub fn init() Self {
130-
return .{
131-
.#heap = mimalloc.mi_heap_new() orelse bun.outOfMemory(),
132-
.thread_id = if (safety_checks) std.Thread.getCurrentId() else {},
133-
};
169+
const mimalloc_heap = mimalloc.mi_heap_new() orelse bun.outOfMemory();
170+
if (comptime !safety_checks) return .{ .#heap = mimalloc_heap };
171+
const heap: Owned(*DebugHeap) = .new(.{
172+
.inner = mimalloc_heap,
173+
.thread_lock = .initLocked(),
174+
});
175+
return .{ .#heap = heap };
134176
}
135177

136178
pub fn gc(self: Self) void {
@@ -141,16 +183,8 @@ pub fn helpCatchMemoryIssues(self: Self) void {
141183
self.borrow().helpCatchMemoryIssues();
142184
}
143185

144-
fn assertThreadOwnership(self: Self) void {
145-
if (comptime safety_checks) {
146-
const current_thread = std.Thread.getCurrentId();
147-
if (current_thread != self.thread_id) {
148-
std.debug.panic(
149-
"MimallocArena used from wrong thread: arena belongs to thread {d}, but current thread is {d}",
150-
.{ self.thread_id, current_thread },
151-
);
152-
}
153-
}
186+
pub fn ownsPtr(self: Self, ptr: *const anyopaque) bool {
187+
return self.borrow().ownsPtr(ptr);
154188
}
155189

156190
fn alignedAllocSize(ptr: [*]u8) usize {
@@ -159,10 +193,13 @@ fn alignedAllocSize(ptr: [*]u8) usize {
159193

160194
fn vtable_alloc(ptr: *anyopaque, len: usize, alignment: Alignment, _: usize) ?[*]u8 {
161195
const self: Borrowed = .fromOpaque(ptr);
196+
self.assertThreadLock();
162197
return self.alignedAlloc(len, alignment);
163198
}
164199

165-
fn vtable_resize(_: *anyopaque, buf: []u8, _: Alignment, new_len: usize, _: usize) bool {
200+
fn vtable_resize(ptr: *anyopaque, buf: []u8, _: Alignment, new_len: usize, _: usize) bool {
201+
const self: Borrowed = .fromOpaque(ptr);
202+
self.assertThreadLock();
166203
return mimalloc.mi_expand(buf.ptr, new_len) != null;
167204
}
168205

@@ -186,17 +223,39 @@ fn vtable_free(
186223
}
187224
}
188225

226+
/// Attempt to expand or shrink memory, allowing relocation.
227+
///
228+
/// `memory.len` must equal the length requested from the most recent
229+
/// successful call to `alloc`, `resize`, or `remap`. `alignment` must
230+
/// equal the same value that was passed as the `alignment` parameter to
231+
/// the original `alloc` call.
232+
///
233+
/// A non-`null` return value indicates the resize was successful. The
234+
/// allocation may have same address, or may have been relocated. In either
235+
/// case, the allocation now has size of `new_len`. A `null` return value
236+
/// indicates that the resize would be equivalent to allocating new memory,
237+
/// copying the bytes from the old memory, and then freeing the old memory.
238+
/// In such case, it is more efficient for the caller to perform the copy.
239+
///
240+
/// `new_len` must be greater than zero.
241+
///
242+
/// `ret_addr` is optionally provided as the first return address of the
243+
/// allocation call stack. If the value is `0` it means no return address
244+
/// has been provided.
189245
fn vtable_remap(ptr: *anyopaque, buf: []u8, alignment: Alignment, new_len: usize, _: usize) ?[*]u8 {
190246
const self: Borrowed = .fromOpaque(ptr);
191-
const value = mimalloc.mi_heap_realloc_aligned(self.#heap, buf.ptr, new_len, alignment.toByteUnits());
247+
self.assertThreadLock();
248+
const heap = self.getMimallocHeap();
249+
const aligned_size = alignment.toByteUnits();
250+
const value = mimalloc.mi_heap_realloc_aligned(heap, buf.ptr, new_len, aligned_size);
192251
return @ptrCast(value);
193252
}
194253

195254
pub fn isInstance(alloc: std.mem.Allocator) bool {
196-
return alloc.vtable == c_allocator_vtable;
255+
return alloc.vtable == &c_allocator_vtable;
197256
}
198257

199-
const c_allocator_vtable = &std.mem.Allocator.VTable{
258+
const c_allocator_vtable = std.mem.Allocator.VTable{
200259
.alloc = vtable_alloc,
201260
.resize = vtable_resize,
202261
.remap = vtable_remap,
@@ -209,3 +268,5 @@ const Alignment = std.mem.Alignment;
209268
const bun = @import("bun");
210269
const assert = bun.assert;
211270
const mimalloc = bun.mimalloc;
271+
const Owned = bun.ptr.Owned;
272+
const safety_checks = bun.Environment.ci_assert;

src/allocators/mimalloc.zig

Lines changed: 7 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -60,29 +60,17 @@ pub const Heap = opaque {
6060
return mi_heap_realloc(self, p, newsize);
6161
}
6262

63-
pub fn isOwned(self: *Heap, p: ?*const anyopaque) bool {
64-
return mi_heap_contains(self, p);
63+
pub fn isOwned(self: *Heap, p: ?*anyopaque) bool {
64+
return mi_heap_check_owned(self, p);
6565
}
6666
};
6767
pub extern fn mi_heap_new() ?*Heap;
6868
pub extern fn mi_heap_delete(heap: *Heap) void;
6969
pub extern fn mi_heap_destroy(heap: *Heap) void;
70+
pub extern fn mi_heap_set_default(heap: *Heap) *Heap;
71+
pub extern fn mi_heap_get_default() *Heap;
72+
pub extern fn mi_heap_get_backing() *Heap;
7073
pub extern fn mi_heap_collect(heap: *Heap, force: bool) void;
71-
pub extern fn mi_heap_main() *Heap;
72-
73-
// Thread-local heap (theap) API - new in mimalloc v3
74-
pub const THeap = opaque {};
75-
pub extern fn mi_theap_get_default() *THeap;
76-
pub extern fn mi_theap_set_default(theap: *THeap) *THeap;
77-
pub extern fn mi_theap_collect(theap: *THeap, force: bool) void;
78-
pub extern fn mi_theap_malloc(theap: *THeap, size: usize) ?*anyopaque;
79-
pub extern fn mi_theap_zalloc(theap: *THeap, size: usize) ?*anyopaque;
80-
pub extern fn mi_theap_calloc(theap: *THeap, count: usize, size: usize) ?*anyopaque;
81-
pub extern fn mi_theap_malloc_small(theap: *THeap, size: usize) ?*anyopaque;
82-
pub extern fn mi_theap_malloc_aligned(theap: *THeap, size: usize, alignment: usize) ?*anyopaque;
83-
pub extern fn mi_theap_realloc(theap: *THeap, p: ?*anyopaque, newsize: usize) ?*anyopaque;
84-
pub extern fn mi_theap_destroy(theap: *THeap) void;
85-
pub extern fn mi_heap_theap(heap: *Heap) *THeap;
8674
pub extern fn mi_heap_malloc(heap: *Heap, size: usize) ?*anyopaque;
8775
pub extern fn mi_heap_zalloc(heap: *Heap, size: usize) ?*anyopaque;
8876
pub extern fn mi_heap_calloc(heap: *Heap, count: usize, size: usize) ?*anyopaque;
@@ -114,7 +102,8 @@ pub extern fn mi_heap_rezalloc_aligned(heap: *Heap, p: ?*anyopaque, newsize: usi
114102
pub extern fn mi_heap_rezalloc_aligned_at(heap: *Heap, p: ?*anyopaque, newsize: usize, alignment: usize, offset: usize) ?*anyopaque;
115103
pub extern fn mi_heap_recalloc_aligned(heap: *Heap, p: ?*anyopaque, newcount: usize, size: usize, alignment: usize) ?*anyopaque;
116104
pub extern fn mi_heap_recalloc_aligned_at(heap: *Heap, p: ?*anyopaque, newcount: usize, size: usize, alignment: usize, offset: usize) ?*anyopaque;
117-
pub extern fn mi_heap_contains(heap: *const Heap, p: ?*const anyopaque) bool;
105+
pub extern fn mi_heap_contains_block(heap: *Heap, p: *const anyopaque) bool;
106+
pub extern fn mi_heap_check_owned(heap: *Heap, p: *const anyopaque) bool;
118107
pub extern fn mi_check_owned(p: ?*const anyopaque) bool;
119108
pub const struct_mi_heap_area_s = extern struct {
120109
blocks: ?*anyopaque,

test/js/bun/perf/static-initializers.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,6 @@ describe("static initializers", () => {
6868
expect(
6969
bunInitializers.length,
7070
`Do not add static initializers to Bun. Static initializers are called when Bun starts up, regardless of whether you use the variables or not. This makes Bun slower.`,
71-
).toBe(process.arch === "arm64" ? 2 : 3);
71+
).toBe(process.arch === "arm64" ? 1 : 2);
7272
});
7373
});

0 commit comments

Comments
 (0)