Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

resource annotations #158

Open
andrewrk opened this issue Jul 19, 2016 · 8 comments
Open

resource annotations #158

andrewrk opened this issue Jul 19, 2016 · 8 comments
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Milestone

Comments

@andrewrk
Copy link
Member

andrewrk commented Jul 19, 2016

Strap your jet pack on because we're going for a pie-in-the-sky adventure.

I propose a builtin function called @resource_acquire() which returns a u64. This is an opaque identifier which is probably just random bytes. Zig code may then store this identifier somewhere, like this:

const want_resource_tracking = @compile_var("debug_safety_on");
const ResourceIdType = if (want_resource_tracking) u64 else void;
struct HeapEntry {
    debug_id: ResourceIdType,
    // ....
}
fn malloc(inline T: type, count: usize) -> %[]T {
    if (want_resource_tracking) some_heap_entry.debug_id = @resource_acquire();
}
fn free(inline T: type, mem: []T) {
    if (want_resource_tracking) @resource_release(some_heap_entry.debug_id);
}

Now, in debug mode, Zig will add safety checks to catch the following scenarios:

  • (easy) (runtime) Releasing a resource too many times.
  • (easy) (runtime) Program reaches end with unreleased resources.
  • (hard) (compile error) The compiler was able to determine that the resource couldn't possibly have been released, or that the resource will be released twice.
  • (hard) (runtime) Track the memory location where the resource id is stored and reference count it. If references drop to 0 before it gets released, runtime error, the resource leaked. The stack trace at the location the ref count drops to 0 will be very useful.

The standard library would implement these semantics for all resources, for example file streams, network connections.

If the implementation is fast enough this could even be viable for detecting failure to flush a stream, such as stdout. When anything is written, acquire a resource, unless one is already acquired. When flush is called, release the resource, unless already released.

Depending on how advanced the safety checks are, this might not even need to be a builtin function. It could just be a standard library function.

In the event of an error we would want to print the stack trace of the resource being acquired, so we might want a @get_stack_trace() builtin function. We want that anyway. Separate issue.

@andrewrk andrewrk added the enhancement Solving this issue will likely involve adding new logic or components to the codebase. label Jul 19, 2016
@andrewrk
Copy link
Member Author

Better yet the void/u64 thing can be done by the compiler, and we can have a resource type.

Then you don't even need the if (want_resource_tracking) code, since assigning void does nothing.

@thejoshwolfe
Copy link
Contributor

The name resource seems kind of like the name data or object. Sorry to be the one who criticizes names first all the time, but I think we can do better. My best proposal is LifeCycleTracker. This doesn't say that it's a resource, because it really isn't. It's meant to accompany a resource in order to track its life cycle. And now I think it's more believable that a life cycle tracker would be void in release mode, as opposed to a resource, which you would probably still want in release mode.

I chose the name Tracker instead of Instead of Id, because I think reference counting on something called an ID would be surprising. Tracker sounds fancy enough that it's more plausible that it could be doing reference counting. And if we want to do reference counting, it might be nice to give users direct access to it via @ref(tracker), @unref(tracker), and even @set_ref_counting_enabled(tracker, false).

If the (debug) runtime is tracking a stacktrace for where the resource was acquired, how about providing access to that? @get_acquisition_stack_trace(tracker).

Anyway, those are my loop-d-loops and barrel rolls on this jetpack ride :D

@andrewrk andrewrk added this to the 0.3.0 milestone May 7, 2017
@andrewrk andrewrk modified the milestones: 0.3.0, 1.1.0 Aug 26, 2017
@kyle-github
Copy link

It seems like this is just a few steps from a built in reference count implementation. If you have it internally why not expose it so that programs can use it?

To thejoshwolfe, reference counting on anything can be useful in some circumstances. It is usually associated with memory, but can be file handles, sockets etc. It is not a panacea, but it really gets useful when you have a lot of async code or lots of threading. Really anything that makes the order in which resources could be freed complicated.

@tiehuis tiehuis added the proposal This issue suggests modifications. If it also has the "accepted" label then it is planned. label Sep 16, 2017
@lerno
Copy link

lerno commented Jun 15, 2023

Whatever happened to this feature?

@mlugg
Copy link
Member

mlugg commented Jun 15, 2023

It's a non-accepted proposal, which means it may or may not make it into the language. More to the point, the current milestone of 1.1.0 suggests that if this is eventually accepted, that might not be until after Zig 1.0.

@lerno
Copy link

lerno commented Jun 15, 2023

I'm curious whether there were any further discussion on this or if it's likely DOA

@matu3ba
Copy link
Contributor

matu3ba commented Sep 16, 2023

This sounds pretty much like valgrind except that valgrind has in-build Kernel ressource annotations, whereas this annotation is user-based (possibly inside libstd).

Open questions:

    1. usage: Are these resource annotations purely intended for Zig user code or how should Zig users handle/annotate externally linked object code or reused C code?
    1. implementation: What should the compiler then emit and track exactly? Often state is implicit, meaning that resource allocation and deallocation depends on the order of execution without explicit variables. Take defer as most prominent example.
    1. implementation: How should this work for multiple threads? Making the u64 atomic?
    1. why: What specific advantage for eliminating the problem does this provide? Mainstream Kernels already provide tracing capabilities and there is already tooling (valgrind and strace being most commonly known ones), so why should Zig users repeat this work?
    1. composability: How can the user pin ownership to specific runtime execution contexts (Thread, Process, functions etc) or does the user need to build their own redundant tooling for this from scratch?
    1. composability: This does not play very nice with swappable things (for example switching allocators) that need no free (Arena, FixedBuffer). Or does it?
    1. usage: It needs extra handling for expected panic checks and/or test behavior must annotate how the functionality should work.

From my point of view, the MVP could be done once supported and non-supported use cases have been defined with the actual advantages to reusage of current tooling including generality, composability.

@msaher
Copy link

msaher commented Aug 25, 2024

For what its worth this seems somewhat like linear types

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement Solving this issue will likely involve adding new logic or components to the codebase. proposal This issue suggests modifications. If it also has the "accepted" label then it is planned.
Projects
None yet
Development

No branches or pull requests

8 participants