Temporary allocators and the core library

I’ve been using Odin over the last year on and off with usually small programs. Overall I think it’s probably the closest thing to a C/C++ replacement for me but there is one thing that prevents me from switching entirely. I’ll describe it in a second but the purpose of this thread is to see if I’m just paranoid or if are these actual problems.

So, my biggest problem is with the implicit context, the temporary allocator, and its usage within libraries and in particular the core libraries. Some allocations are guarded by runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD() but it feels hacky because it assumes the default temporary allocator and when that’s not the case, the guard is “ignored” and the memory is “leaked”. Some allocations are matched with a free (like with dynlib > initialize_symbols > symbol_address > _symbol_address), but if we’re using the default temp allocator, this free operation is essentially a no-op so the memory is “leaked” again. Some allocations aren’t freed (like with os > read > win32.utf8_to_wstring on windows) which I’m not sure is worse but the memory is also “leaked”.

Now, I’m using “leaked” here but only until the temporary allocator is reset so it isn’t a permanent problem if the program is aware. The thing is that most of these allocations shouldn’t leave their scopes which makes me wonder whether something better could be done.

I’ve read some of the code in core:os/os2 and it seems it’s (at least partially) being addressed there but I’d argue it is not a localized problem. Probably the root of this is trying to fit multiple types of allocators into a common allocator interface.

I came up with a few solutions while writing this to not be too hand wavy but it’s not clear whether any would be a better fit. Either way:

1 - Allocator_Mode.Temp_Begin and Allocator_Mode.Temp_End
Similar to having Allocator_Mode.Free_All. It is not implemented for the default heap allocator (nor would malloc/free implementations allow it, I think) but is implemented for arena allocators, including the default temp allocator.

2 - Have temp_allocator be a different type (i.e. TempAllocator)
This would actually break any code mixing temporary and permanent/managed allocator but would have better separation of concerns (probably the worst one).

3 - Not have a temp allocator
This is probably the most controversial but let the application have its own temp/frame allocators like in core:os/os2.

Anyway, I’m sorry if this is confusing but it’s something that’s been bugging me out since forever and I wanted to hear from others. Over the last couple years, I’ve been looking for something to replace C/C++ with and currently the only real alternative is Odin. Jai could probably be another one but I just couldn’t get into the closed beta so I can’t tell. Zig could be but is too pedantic and honestly… a << @as(u5, b). Others are either GCd (Go, V), unreadable (Hare, Haskell), crazy (Rust). So it is either C/C++ or Odin and I’m really liking Odin.

This is already too long so I’ll just cut it here. Cheers.

1 Like

We’re removing the runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD usage when os2 is ready. This is just a bodge effectively.

We are not adding any new allocator modes for this.

So that’s fundamentally all the problem is: they are bodges that will be removed entirely.

What you think would be the policy on using the temporary allocator after this cleanup? I’m asking because my preference would be to context.temp_allocator = panic_allocator at startup and manage any temporary memory separately (as with option #3 on the initial post). I think temporary allocators depend on the overall context of a codebase and while I see them being handy when used within it, I also see them being problematic when used outside the codebase boundaries, where the context could be different.

An example is the current use of runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD(). Even if it’s just bodge, it has a clear meaning inside the context of the core library which is to have some short lived memory within some scope but the problem arises because we can’t guarantee we’re using the default temporary allocator (the context of the program calling into the core library could be different).

I’ve mentioned some problems being addressed with os2 and I’m talking exactly about how it handles it’s own temporary memory with TEMP_ALLOCATOR_GUARD() and temp_allocator() rather than using context.temp_allocator and I’d still argue this is a better approach when interfacing with libraries.

Either way, I could just be overly paranoid or missing some detail. Cheers.

PS: I was thinking of writing a PR to change _load_library and _load_symbol in core:dynlib to avoid allocating at all when formatting strings. Is there a policy on using intrinsics.alloca? I could also just use a stack buffer with each system’s limits for symbol name and library path.