Managing the lifetimes of garbage-collected objects [closed] - garbage-collection

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 12 months ago.
The community reviewed whether to reopen this question 11 months ago and left it closed:
Original close reason(s) were not resolved
Improve this question
I am making a simplistic mark-and-compact garbage collector. Without going too much into details, the API it exposes is like this:
/// Describes the internal structure of a managed object.
pub struct Tag { ... }
/// An unmanaged pointer to a managed object.
pub type Pointer = *mut usize;
/// Mapping from old to new locations.
pub type Adjust = BTreeMap<usize, Pointer>;
/// Mark this object and anything it points to as non-garbage.
pub unsafe fn survive(ptr: Pointer);
pub struct Heap { ... }
impl Heap {
pub fn new() -> Heap;
// Allocate an object with the specified structure.
pub fn allocate(&mut self, tag: Tag) -> Pointer;
/// Move all live objects from `heap` into `self`.
pub unsafe fn reallocate(&mut self, heap: Heap) -> Adjust;
}
This API is obviously fundamentally unsafe. I would like to rework the API (without changing the internals, which are just fine!) to account for the following facts:
All Pointers to (objects allocated in) a Heap become invalid when the heap is merged into another heap.
merge returns an Adjust whose values are valid Pointers to (objects allocated in) self.
I have the following tentative solution:
// Replaces Pointer.
#[derive(Copy, Clone)]
pub struct Object<'a> {
ptr: *mut AtomicUsize,
mark: PhantomData<&'a usize>
}
impl<'a> Object<'a> {
pub fn survive(self); // Now supposed to be perfectly safe!
}
pub type Adjust<'a> = BTreeMap<usize, Object<'a>>;
pub struct Heap { ... }
pub struct Allocator<'a> { ... }
impl Heap {
fn allocator(&'a self) -> Allocator<'a>;
// The following doesn't work:
//
// fn allocate(&'a mut self) -> Object<'a>;
// fn reallocate(&'a mut self, heap: Heap) -> Adjust<'a>;
//
// Because it doesn't allow the user to allocate more
// than one `Object` at a time (!) in a `Heap`.
}
impl<'a> Allocator<'a> {
// Note that the resulting `Object`s are tied to the `Heap`,
// but not to the allocator itself.
fn allocate(&mut self, tag: Tag) -> Object<'a>;
fn reallocate(&mut self, heap: Heap) -> Adjust<'a>;
}
Is this design correct? If not, what needs to be changed?

Related

Rust views over a shared refcounted array

I am trying to implement multiple "slices" over a shared array of objects. My solution is to morally make a
struct MySlice<T>{ shared_data: Rc<[T]> , beg: usize, len: usize }
I want to be able to implement both
impl From<Vec<T>> for MySlice<T> { /* ?? */ }
and fn get_mut_slice(&mut MySlice<T>) -> &mut [T] which returns the entire underlying slice.
With the above implementation I can get the former but not the latter. I tried modifying the type into
struct MySlice1<T>{ shared_data: Rc<RefCell<[T]>> , beg: usize, len: usize }
But I couldn't get it to work. Is there an idiomatic way to get both?
The first can be done by storing a Rc<RefCell<Vec<T>>>. I don't think this can be done with a slice, unfortunately.
The second cannot exist as-is. If you have shared ownership, and you want to get a mutable reference, you have to make sure nobody else will use the value as long as the mutable reference is active. This can only be done with a guard. You can change its signature to:
pub fn get_mut_slice(&mut MySlice<T>) -> RefMut<'_, [T]>
Then:
pub fn get_mut_slice(&mut self) -> RefMut<'_, [T]> {
RefMut::map(self.shared_data.borrow_mut(), |s| {
&mut s[self.beg..][..self.len]
})
}

Is it possible to call a parent struct's methods from a child struct? [duplicate]

This question already has answers here:
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
How do I express mutually recursive data structures in safe Rust?
(4 answers)
Pass self reference to contained object's function
(2 answers)
Passing mutable self reference to method of owned object
(2 answers)
Closed 3 years ago.
I am trying to pass a reference to self down to a child struct that is a field variable
use std::cell::RefCell;
use std::rc::Rc;
pub struct BUS {
pub processor: CPU,
pub ram: [u8; 65536],
}
impl BUS {
pub fn new() -> BUS {
BUS {
processor: CPU::new(),
ram: [0; 65536],
}
}
pub fn setup(&mut self) {
self.processor.connect_bus(Rc::new(RefCell::new(self)));
self.ram.iter_mut().for_each(|x| *x = 00);
}
pub fn write(&mut self, addr: u16, data: u8) {
self.ram[addr as usize] = data;
}
pub fn read(&mut self, addr: u16, _read_only: bool) -> u8 {
self.ram[addr as usize]
}
}
pub struct CPU {
bus_ptr: Rc<RefCell<BUS>>,
}
impl CPU {
pub fn new() -> CPU {
CPU {
bus_ptr: Rc::new(RefCell::new(BUS::new())),
}
}
pub fn connect_bus(&mut self, r: Rc<RefCell<BUS>>) {
self.bus_ptr = r;
}
pub fn read_ram(&self, addr: u16, _read_only: bool) -> u8 {
(self.bus_ptr.borrow_mut().read(addr, false))
}
pub fn write_ram(&mut self, addr: u16, data: u8) {
(self.bus_ptr.borrow_mut().write(addr, data))
}
}
fn main() {
let comp = BUS::new();
comp.setup();
}
Rust Playground
This errors:
error[E0308]: mismatched types
--> src/main.rs:18:57
|
18 | self.processor.connect_bus(Rc::new(RefCell::new(self)));
| ^^^^ expected struct `BUS`, found &mut BUS
|
= note: expected type `BUS`
found type `&mut BUS`
I can't pass in self to the RefCell as it is a second mutable borrow. I got around this by moving my functions around but want to know how possible this structure is.
I achieved this in C++ by passing in this from BUS and then using *bus in connect_bus so that read_ram can be *bus->read(...).
Is it possible to call the BUS struct's read and write functions from a method on the CPU struct?
The short answer is no.
RefCell owns its inner object. This means it has the only copy of that object, so that it can fully control all access to it and not allow any other access from outside. An object can't exist in RefCell and outside of RefCell at the same time.
Your setup could take an existing Rc<RefCell<BUS>> instead and pass that around. &mut BUS without the wrapper won't do.
The borrow checker can't ensure safety of mutual parent-child relationships. It wants program data structured as trees or DAGs. Otherwise you're forced to use wrapper types or C-like raw pointers.
The borrow checker checks against interfaces, not implementation. If your setter borrows &mut self, that's exclusive borrow of the entire object, and for borrow checking that's the most restrictive and most inflexible option. You will need to peel some layers of abstraction to make this work, e.g. pass RAM down to the CPU. Alternatively, make RAM use Cell<u8> type, so that it can be mutated via shared references.

Array of structs with individual RwLocks for each block [duplicate]

This question already has answers here:
Initialize a large, fixed-size array with non-Copy types
(8 answers)
Why is the Copy trait needed for default (struct valued) array initialization?
(2 answers)
Closed 4 years ago.
I am trying to create a struct which holds multiple blocks of bytes where each block of bytes is held in an individual RwLock.
This is for a voxel engine where performance is very important. Each block of bytes will need to be read/written by multiple threads. Multiple RwLocks are needed so that only a specific block will be locked leaving the rest of the blocks free to be read/written by other threads; locking the full struct would cause locking of all threads that are performing work.
Most other structs will be assigned a specific slot, the block of bytes needs to be stack allocated.
The compiler complains that it cannot copy the RwLocks as there is no Copy trait on RwLock, which I don't want to copy but instance multiple RwLocks.
mod constants {
pub const CHUNK_BASE_SIZE: usize = 7;
pub const CHUNK_ALLOC_COUNT: usize = 11;
}
mod example {
use std::sync::RwLock;
use super::constants;
// =====================================================================
struct BU8 {
bytes: [[u8; constants::CHUNK_BASE_SIZE]; constants::CHUNK_BASE_SIZE],
}
impl BU8 {
pub fn new() -> BU8 {
BU8 {
bytes: [[0; constants::CHUNK_BASE_SIZE]; constants::CHUNK_BASE_SIZE],
}
}
}
// =====================================================================
pub struct Bytes {
block: RwLock<BU8>,
}
impl Bytes {
pub fn new() -> Bytes {
Bytes {
block: RwLock::new(BU8::new()),
}
}
pub fn read(&self, y: usize, xz: usize) -> u8 {
self.block.read().unwrap().bytes[y][xz]
}
pub fn write(&mut self, y: usize, xz: usize, value: u8) {
self.block.write().unwrap().bytes[y][xz] = value;
}
}
// =====================================================================
pub struct Stacks {
slots: [Bytes; constants::CHUNK_ALLOC_COUNT],
}
impl Stacks {
pub fn new() -> Stacks {
Stacks {
// Cannot Copy, I dont want a Copy I want to instance a fixed sized array of RwLocks<BU8> blocks
slots: [Bytes::new(); constants::CHUNK_ALLOC_COUNT],
}
}
}
}
fn main() {}
error[E0277]: the trait bound `example::Bytes: std::marker::Copy` is not satisfied
--> src/main.rs:53:24
|
53 | slots: [Bytes::new(); constants::CHUNK_ALLOC_COUNT],
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::marker::Copy` is not implemented for `example::Bytes`
|
= note: the `Copy` trait is required because the repeated element will be copied
The main thread spawns a child thread that will hold all game/core data on and will be accessible through each worker thread the child thread will spawn.
Each worker thread will be assigned workloads to read/write data on an assigned Block on the Stacks, but I don't know how to instance multiple RwLocks this way or any other way without using the collections.
I tried adding #[derive(Copy, Clone)] for each struct but the Bytes struct error is:
error[E0204]: the trait `Copy` may not be implemented for this type
--> src/main.rs:24:18
|
24 | #[derive(Copy, Clone)]
| ^^^^
25 | pub struct Bytes {
26 | block: RwLock<BU8>,
| ------------------ this field does not implement `Copy`
I'm guessing RwLock size cannot known at compile time so would a vector of pointers to each block of bytes work? If so how do I implement that safely?

Add reference to list of reference [duplicate]

This question already has answers here:
Implement graph-like data structure in Rust
(3 answers)
Why can't I store a value and a reference to that value in the same struct?
(4 answers)
Closed 5 years ago.
I'm implementing a basic data structure. I think I understand why the following code doesn't work: I can't mutably borrow and try to access (read) the value at the same time. However, I am stuck on how to implement add_ege.
The whole idea is to have Graph hold a list of Nodes in the heap. The Node itself keeps track of adjacent nodes (edges). I don't want Node to hold copies of Node, I want it to have references to any of the Nodes held in Graph.
struct Node<'a> {
value: String,
adj_nodes: Vec<&'a Node<'a>>, // refer to Graph.nodes
}
pub struct Graph<'a> {
nodes: Vec<Box<Node<'a>>>,
}
fn mk_node<'a>(value: String) -> Node<'a> {
Node {
value,
adj_nodes: vec![],
}
}
pub fn mk_graph<'a>() -> Graph<'a> {
let nodes = vec![];
Graph { nodes }
}
impl<'a> Graph<'a> {
fn add_node(&mut self, val: String) {
let node = Box::new(mk_node(val));
self.nodes.push(node);
}
fn add_edge(&mut self, from_node: &'a mut Node<'a>, to_node: &'a mut Node<'a>) {
from_node.adj_nodes.push(to_node);
// won't work because I already have from_node as mutable borrow
to_node.adj_nodes.push(from_node);
}
}
Is there a way to do this?

Can I implement a trait which adds information to an external type in Rust?

I just implemented a simple trait to keep the history of a struct property:
fn main() {
let mut weight = Weight::new(2);
weight.set(3);
weight.set(5);
println!("Current weight: {}. History: {:?}", weight.value, weight.history);
}
trait History<T: Copy> {
fn set(&mut self, value: T);
fn history(&self) -> &Vec<T>;
}
impl History<u32> for Weight {
fn set(&mut self, value: u32) {
self.history.push(self.value);
self.value = value;
}
fn history(&self) -> &Vec<u32> {
&self.history
}
}
pub struct Weight {
value: u32,
history: Vec<u32>,
}
impl Weight {
fn new(value: u32) -> Weight {
Weight {
value,
history: Vec::new(),
}
}
}
I don't expect this is possible, but could you add the History trait (or something equivalent) to something which doesn't already have a history property (like u32 or String), effectively tacking on some information about which values the variable has taken?
No. Traits cannot add data members to the existing structures. Actually, only a programmer can do that by modifying the definition of a structure. Wrapper structures or hash-tables are the ways to go.
No, traits can only contain behavior, not data. But you could make a struct.
If you could implement History for u32, you'd have to keep the entire history of every u32 object indefinitely, in case one day someone decided to call .history() on it. (Also, what would happen when you assign one u32 to another? Does its history come with it, or does the new value just get added to the list?)
Instead, you probably want to be able to mark specific u32 objects to keep a history. A wrapper struct, as red75prime's answer suggests, will work:
mod hist {
use std::mem;
pub struct History<T> {
value: T,
history: Vec<T>,
}
impl<T> History<T> {
pub fn new(value: T) -> Self {
History {
value,
history: Vec::new(),
}
}
pub fn set(&mut self, value: T) {
self.history.push(mem::replace(&mut self.value, value));
}
pub fn get(&self) -> T
where
T: Copy,
{
self.value
}
pub fn history(&self) -> &[T] {
&self.history
}
}
}
It's generic, so you can have a History<u32> or History<String> or whatever you want, but the get() method will only be implemented when the wrapped type is Copy.* Your Weight type could just be an alias for History<u32>. Here it is in the playground.
Wrapping this code in a module is a necessary part of maintaining the abstraction. That means you can't write weight.value, you have to call weight.get(). If value were marked pub, you could assign directly to weight.value (bypassing set) and then history would be inaccurate.
As a side note, you almost never want &Vec<T> when you can use &[T], so I changed the signature of history(). Another thing you might consider is returning an iterator over the previous values (perhaps in reverse order) instead of a slice.
* A better way of getting the T out of a History<T> is to implement Deref and write *foo instead of foo.get().

Resources