22
33const 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`.
2522pub 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+
82114const log = bun .Output .scoped (.mimalloc , .hidden );
83115
84116pub fn allocator (self : Self ) std.mem.Allocator {
85- self .assertThreadOwnership ();
86117 return self .borrow ().allocator ();
87118}
88119
89120pub 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
93128pub 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
124159pub 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
129168pub 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
136178pub 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
156190fn alignedAllocSize (ptr : [* ]u8 ) usize {
@@ -159,10 +193,13 @@ fn alignedAllocSize(ptr: [*]u8) usize {
159193
160194fn 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.
189245fn 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
195254pub 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;
209268const bun = @import ("bun" );
210269const assert = bun .assert ;
211270const mimalloc = bun .mimalloc ;
271+ const Owned = bun .ptr .Owned ;
272+ const safety_checks = bun .Environment .ci_assert ;
0 commit comments