Live data from Hacker News

A Tour of Safe Tracing GC Designs in Rust

manishearth.github.io

51–60 of 61 posts

Re: A Tour of Safe Tracing GC Designs in Rust

#51
post #45

Earlier quoted context omitted.

You aren't reading it properly, the documentation you are reading is for the case you leave the work to the GC, you can take it yourself C++ RAII style: { using my_socket = new NetworkSocket() } // my_socket no longer exists when code arrives here Or even better if NetworkSocket is a struct, it gets stack allocated, zero GC.

NetworkComponent foo = new NetworkComponent(); { using my_socket = new NetworkSocket(); foo.socket = my_socket; } foo.do_sth_with_socket(); // oops, runtime failure, socket closed

Trying to be clever?

Here is your Rust version, enjoy.

    use std::io::{self};

    struct NetworkComponent {
      socket : NetworkSocket
    }

    impl NetworkComponent {
        fn new() -> NetworkComponent {
            println!("Creating NetworkComponent");
            NetworkComponent {
                socket : NetworkSocket{}
            }
        }
        
        fn do_sth_with_socket(&self) {
            
        }
    }

    impl Drop for NetworkComponent {
        fn drop(&mut self) {
            println!("Dropping NetworkComponent");
        }
    }    


    struct NetworkSocket {
        
    }

    impl Drop for NetworkSocket {
        fn drop(&mut self) {
            println!("Dropping NetworkSocket");
        }
    }  

    fn main() -> io::Result {
        let mut foo = NetworkComponent::new();
        
        {
            let socket = NetworkSocket{};
            foo.socket = socket;
        }
        
        foo.do_sth_with_socket(); // oops, runtime failure, socket closed
        
        Ok(())
    }
https://play.rust-lang.org/?version=stable&mode=debug&editio...

Re: A Tour of Safe Tracing GC Designs in Rust

#52
post #45
post #32

Earlier quoted context omitted.

I must be missing something. How is it possible to precisely collect a resource with tracing GC? And if you need to update counters when you make duplicates of object references, you are not using a tracing GC where the benefits are the cheap duplication of object references, cheap allocations and cheap (batched) releases, but the downside is not being able to precisely and automatically do it when the value is avail…

You aren't reading it properly, the documentation you are reading is for the case you leave the work to the GC, you can take it yourself C++ RAII style: { using my_socket = new NetworkSocket() } // my_socket no longer exists when code arrives here Or even better if NetworkSocket is a struct, it gets stack allocated, zero GC.

So how about this then:

    {
      using my_socket = new NetworkSocket();
      my_socket.write("Started");
      register_callback(() => my_socket.write("Finished"));
    }
This is the case what RC solves well and tracing GC doesn't solve at all, regardless of the number of interfaces you implement. It is easy to find yourself in this situation given how much callbacks are used in modern codebases.

Re: A Tour of Safe Tracing GC Designs in Rust

#53
post #51

Earlier quoted context omitted.

NetworkComponent foo = new NetworkComponent(); { using my_socket = new NetworkSocket(); foo.socket = my_socket; } foo.do_sth_with_socket(); // oops, runtime failure, socket closed

Trying to be clever? Here is your Rust version, enjoy. use std::io::{self}; struct NetworkComponent { socket : NetworkSocket } impl NetworkComponent { fn new() -> NetworkComponent { println!("Creating NetworkComponent"); NetworkComponent { socket : NetworkSocket{} } } fn do_sth_with_socket(&self) { } } impl Drop for NetworkComponent { fn drop(&mut self) { println!("Dropping NetworkComponent"); } } struct NetworkSocke…

And what did you try to prove here? There is no use after free and no runtime error in this rust code. The socket stays valid since the moment of its creation and for the whole lifetime of the network component. It gets moved out of nested scope properly and gets closed after leaving the outer scope, after dropping the NetworkComponent struct.

The "oops" comment is invalid in your Rust example because the socket is still valid at that point.

Which is totally different than what would happen in C#, where you'd get use-after-free bug (actually use-after-close).

Try with resources is not RAII. It is a lot weaker.

Re: A Tour of Safe Tracing GC Designs in Rust

#54

The OP does not seem to mention CactusRef https://crates.io/crates/cactusref which provides deterministic deallocation (on par with simple refcounting) of reference cycles, with minimal tracing requirements and minimal memory overhead.

Do you know of any link to a description of how it works? These claims seem to me like very vague and very hard to deliver…

Re: A Tour of Safe Tracing GC Designs in Rust

#55
post #51

Earlier quoted context omitted.

Trying to be clever? Here is your Rust version, enjoy. use std::io::{self}; struct NetworkComponent { socket : NetworkSocket } impl NetworkComponent { fn new() -> NetworkComponent { println!("Creating NetworkComponent"); NetworkComponent { socket : NetworkSocket{} } } fn do_sth_with_socket(&self) { } } impl Drop for NetworkComponent { fn drop(&mut self) { println!("Dropping NetworkComponent"); } } struct NetworkSocke…

And what did you try to prove here? There is no use after free and no runtime error in this rust code. The socket stays valid since the moment of its creation and for the whole lifetime of the network component. It gets moved out of nested scope properly and gets closed after leaving the outer scope, after dropping the NetworkComponent struct. The "oops" comment is invalid in your Rust example because the socket is s…

> foo.do_sth_with_socket(); // oops, runtime failure, socket closed

Happens just as well in Rust, why do you think I gave you a Playground link.

If you want, I can shut up the cleverness with a cargo build example instead of a dummy playground example.

Re: A Tour of Safe Tracing GC Designs in Rust

#56
post #55

Earlier quoted context omitted.

And what did you try to prove here? There is no use after free and no runtime error in this rust code. The socket stays valid since the moment of its creation and for the whole lifetime of the network component. It gets moved out of nested scope properly and gets closed after leaving the outer scope, after dropping the NetworkComponent struct. The "oops" comment is invalid in your Rust example because the socket is s…

> foo.do_sth_with_socket(); // oops, runtime failure, socket closed Happens just as well in Rust, why do you think I gave you a Playground link. If you want, I can shut up the cleverness with a cargo build example instead of a dummy playground example.

The playground link confirms the socket is closed after dropping networkComponent.

Last two lines of the output:

    Dropping NetworkComponent
    Dropping NetworkSocket
Btw: you probably fooled yourself by accidentally creating 2 sockets, and indeed the first one gets dropped immediately when you lose (overwrite) the reference to it. Use Option to avoid that.

Re: A Tour of Safe Tracing GC Designs in Rust

#57
post #55

Earlier quoted context omitted.

> foo.do_sth_with_socket(); // oops, runtime failure, socket closed Happens just as well in Rust, why do you think I gave you a Playground link. If you want, I can shut up the cleverness with a cargo build example instead of a dummy playground example.

The playground link confirms the socket is closed after dropping networkComponent. Last two lines of the output: Dropping NetworkComponent Dropping NetworkSocket Btw: you probably fooled yourself by accidentally creating 2 sockets, and indeed the first one gets dropped immediately when you lose (overwrite) the reference to it. Use Option to avoid that.

You forgot another line, it was actually:

    Creating NetworkComponent
    Dropping NetworkSocket
    Dropping NetworkComponent
    Dropping NetworkSocket
Besides, you forgot another tiny detail,

By replacing the socket now the port number is another one, and all processes that had open connections to that port will now crash, or have messages dropped without getting why.

I can also fabricate plenty of error situations with Rust if you feel so inclined.

And if you were actually serious, you would be aware that there are Roslyn analysers that validate IDispose follows proper RAII patterns, like https://github.com/DotNetAnalyzers/IDisposableAnalyzers

Remember, Rust isn't perfect, and only fixes 70% of existing error patterns, I can have plenty of inspiration with the remaining 30%.

Re: A Tour of Safe Tracing GC Designs in Rust

#58
post #57

Earlier quoted context omitted.

The playground link confirms the socket is closed after dropping networkComponent. Last two lines of the output: Dropping NetworkComponent Dropping NetworkSocket Btw: you probably fooled yourself by accidentally creating 2 sockets, and indeed the first one gets dropped immediately when you lose (overwrite) the reference to it. Use Option to avoid that.

You forgot another line, it was actually: Creating NetworkComponent Dropping NetworkSocket Dropping NetworkComponent Dropping NetworkSocket Besides, you forgot another tiny detail, By replacing the socket now the port number is another one, and all processes that had open connections to that port will now crash, or have messages dropped without getting why. I can also fabricate plenty of error situations with Rust if…

> You forgot another line, it was actually:

No I didn't. That line is totally irrelevant and does not apply to the socket that was passed to the NetworkComponent. It applies to the initial socket you've added which was not even present in the original example. You should have used Option to make your code equivalent.

Anyway, your example failed to show use-after-close in Rust.

> Remember, Rust isn't perfect, and only fixes 70% of existing error patterns

Sure, no-one here debated that. But it fixes/protects from more error patterns than C#, and use-after-close is one of them.

You stated that try-with-resources + struct types are functionally equivalent to RAII. My code proved they were not, because you can trivially make use-after-free, and it is even really easy to do that by accident. There is nothing in the language that protects from leaking a closeable reference from the `using` scope and then using that reference after the scope gets closed. And that leak can happen 10 layers below, when it is not as easily seen as in this trivial example I posted. In Rust you can do it only with explicit `unsafe`; otherwise the typesystem tracks that for you.

Re: A Tour of Safe Tracing GC Designs in Rust

#59
post #4

Wouldn't it make more sense to have garbage collectors at the level of LLVM? That way, you could have direct access to all the pointers in a program without jumping through the language's hoops. And other languages could benefit from it too.

LLVM has built in hooks for GC, used by Azul's Zing JVM JIT compiler and perhaps Safari's LLVM JS JIT. I'm not sure if the GC integration is tied to JIT support or not. I think it's mostly related to insertion of safepoints which could be useful for Rust implementation

Safari/JavaScriptCore has moved away from LLVM and now uses its own B3 backend for advanced optimizations.

Re: A Tour of Safe Tracing GC Designs in Rust

#60
post #22

Earlier quoted context omitted.

> This isn't representative of application code and there isn't even any mention of the metrics I mentioned… Yeah, that is the usual answer when benchmarks prove how much urban myth reference counting performance is actually like. > No it doesn't. I told you it didn't the last time you said this. Did you? There is more important stuff in life to store on my brain than a list of who replies to me on hacker news. Anywa…

> Yeah, that is the usual answer when benchmarks prove how much urban myth reference counting performance is actually like. CPU/wall time benchmarks are not that relevant to system performance (seriously!) because second-order effects matter more. But if you had peak memory and page demand graphs that would matter. For a network driver I don't know if it'd really look any different though. That's mostly arena allocat…

Apple's atomics as of recently are almost magically fast, though.
Post reply on HN