GithubHelp home page GithubHelp logo

bronze's Introduction

Bronze

Installation

To use Bronze, you must use the nightly Rust tools. To install them, run rustup default nightly on the command line.

Usage

Sometimes, the requirements of ownership in Rust can be challenging to address. For example, implementing a doubly-linked list in Rust is notoriously hard because each node must be referenced twice. Also, especially for people who are learning Rust, it may be easier to avoid the every variable has one owner restriction for the time being.

Bronze relaxes some of the restrictions that Rust has by introducing a new smart pointer type, GcRef, which describes a pointer to a garbage-collected heap location. When using Bronze, data located on the stack has all the usual Rust ownership requirements. However, Bronze allows moving data to the heap. If a value of type T is on the heap, Bronze allows an arbitrary number of references of type GcRef<T> to that value.

For example, without Bronze, we have to carefully manage references and lifetimes:

pub struct IntContainer {
    n: i32,
}

pub fn set(c: &mut IntContainer, n: i32) {
    c.n = n;
}

pub fn test() {
    let c1 = IntContainer{n: 42};
    let mut c2 = c1;
    
    // Can't use c1 anymore because it's been moved to c2
    set(&mut c2, 42);
}

With Bronze, types that are not Copy can be freely referenced through smart pointers, which are Copy:

// 
#[derive(Trace, Finalize)]
pub struct IntContainer {
    n: i32,
}

pub fn set(mut c: GcRef<IntContainer>, n: i32) {
    c.n = n;
}

pub fn test() {
    let c1 = GcRef::new(IntContainer{n: 42});
    let c2 = c1; 
    // Now c1 and c2 both reference the same object.
    
    set(c2, 42);
    set(c1, 43);
    // Now they both reference an object with value 43.
}

Because GcRef implements Deref, and because Rust automatically inserts * when needed, you can generally treat GcRef<T> as if it were a T directly. For example, in set() above, the body assigns to c.n. This implicitly means to dereference the pointer and assign to n inside the referenced value. If you like, you can instead call as_ref() and as_mut() to obtain a reference or mutable reference to the data in the GC heap.

To create a new GcRef<T>, call GcRef::new(e), where e is an expression whose value you want to be on the GC heap.

Advanced Usage

If you need to remove data from the GC heap, you can use a GcNullableRef rather than a GcRef. You can create one with Gc::new_nullable. GcNullableRef is like GcRef but adds a method remove. The first call to remove returns an Option populated with the data that was previously in the GC heap. Future calls to remove return None.

Experimental Implementation

This implementation is experimental. The 'main' branch has a collector, but it only works in limited cases and is not general enough to work with YOUR code. The 'API-only' branch has the collector disabled; be aware that you will eventually run out of memory. However, the present version is suitable for experimentation and prototyping.

Contributors are welcome! The stack map code (which, again, works only for certain cases) is in a fork of the Rust compiler. The current implementation assumes that all allocation of GC structures happens on a single thread; this is because of the specific stack map technique used in the compiler, which we selected for implementation simplicity. In particular, it uses the LLVM shadow stack. A practical version of Bronze would likely use a different stack map approach.

An important aspect of the experimental nature is that, for now, it is possible with Bronze to create multiple mutable references to the same data. Obviously, this is unsafe in Rust. For now, we assume that users will encapsulate their data structures appropriately and avoid this; in the future, we should explore enforcement mechanisms to make this safe in general. One approach might be akin to how RefCell keeps track of outstanding references.

bronze's People

Contributors

mcoblenz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

bronze's Issues

Multiple mutable references and memory bugs

Bronze GC allows creating simultaneous mutable references to a single object - probably by design. However, this allows users to create memory bugs in safe rust.

The example below writes into free'd memory in safe rust and does so in a very deliberate way, but I would think that allowing multiple mutable references may create undefined behavior (UB, which may cause memory bugs in surprising ways, or allow the compiler some nasty undesired mis-optimizations) in subtle and unexpected ways even with "reasonable" use of Bronze (e.g. there may be no consistent set of simple rules to make usage of Bronze actually safe, even if one wanted to take on this cost of moving away from reliably safe rust; e.g. because other libraries may make no-mutable-aliasing assumptions).

(Subjectively, it seems there is additional cost of this when Bronze is used to teach rust and proper memory handling. But that is independent of this.)

extern crate bronze_gc;
use bronze_gc::GcRef;

fn main() {
    let mut gr1 = GcRef::new(vec![1u16,2,3]);
    let mut gr2 = gr1.clone();

    let ref1: &mut Vec<u16> = gr1.as_mut();
    let ref2: &mut Vec<u16> = gr2.as_mut();
    // ref1 and ref2 now reference the same object:
    ref1.push(4);
    ref2.push(5);
    ref1.push(6);
    
    dbg!(&ref1, &ref2);

    // Evil stuff:
    let ref1elem0 = ref1.get_mut(0).unwrap();
    // Force reallocation of the underlying vec
    ref2.resize(1024, 0);
    // Now this writes to deallocated memory
    *ref1elem0 = 42;
    dbg!(&ref1elem0, ref2.get(0).unwrap()); // prints: 42, 1
}

Running valgrind target/debug/bronze_test:

[...]
==56780== Invalid write of size 2
==56780==    at 0x115A4A: bronze_test::main (main.rs:24)
[...]
==56780==  Address 0x4bfbf20 is 0 bytes inside a block of size 12 free'd
==56780==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
[...]
==56780==    by 0x115A41: bronze_test::main (main.rs:22)
==56780==  Block was alloc'd at
==56780==    at 0x483DFAF: realloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
[...]
==56780==    by 0x1155B1: bronze_test::main (main.rs:13)
[...]

Edit: There is a discussion on rust forum.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.