1. 17
  1.  

  2. 5

    This is more a roll your own runtime wrapper, and it does a really good job of that. But to me it isn’t rolling a runtime - I had imagined it would be a small js subset in rust (mostly because I’m curious about how such a project would manage GC behavior coherently)

    1. 4

      I’ve been toying with a JS runtime (https://dmytrish.net/sljs), and tracing roots reliably is indeed a tough problem.

      Right now I do not have a good way to do that in my runtime. I’ve been thinking about two approaches:

      • keeping a vector of weak references for every external JSRef and looking at it on each garbage collection to get the refcount and updating the internal heap index as a Atomic inside.

      • keeping it as a “monad” with a lot of function wrappers that expose “handles”, something like:

      heap.with_local(|x: &Local| {
        // Use the variable as a "handle"
      })
      

      Since everything is hidden behind an opaque references with wrapper-provided lifetimes, Rust guarantees that a Local is available only inside the wrapper and cannot leak outside.

      1. 1

        If I were trying to do this in a language like Rust I would start out with precise marking and a super dumb linked list coupled with an implementation of Drop that removes self from said linked list (If I were using C++ I’d use move semantics to avoid add/remove but I’m not sure what the rust equivalent of that is). That allows you to simply return a local and avoid callback nesting hell. The question I think then becomes one of how you implement that model efficiently in rust - C++ with memory shenanigans can avoid allocations, but rust doesn’t get to randomly trample memory for some reason :D

        Only one I decided to provide a real API for the runtime would I implement conservative marking of the stack, as that’s the only reasonable way to have a C API with GC not be terrible to use.

        1. 2

          Linked lists and Rust do not go well together (it’s hard to convey to Rust what owns a node in a double-linked list), and modern hardware does not like them as well (pointer chasing, writes through multiple cache layers, etc).

          Having an intermediate data structure to gate access to internal indexes is definitely possible in safe Rust, I’m in the process of designing this “gateway” structure for external references.

          Another way is to have Atomic counters for external references in the objects themselves and avoid changing object indexes when n_ext.get() > 0. In this case, external references must have a back pointer to the Heap to be able to decrement counters on Drop, which is not pleasant, but doable with Weak<Heap> pointers.

          If I were using C++ I’d use move semantics to avoid add/remove

          – could you give more details? Rust is generally great at move semantics, but it is simpler than in C++ (no move constructors, so no way to implement the move yourself, only bit-wise copy, etc).

          1. 1

            I’d forgotten about rust’s hatred of linked lists :D

            The c++ move stuff would be purely an optimization to avoid return behaviour that consisted of pushing a new handle and initializing to the original value then releasing the old one. I only mentioned it as I don’t know the details of Drop, etc so thought it would be worth noting :)

    2. 2

      This is a great little demo.