I have created the analog to rust-gc crate library:
with support by default Concurrent GC !!
But I have faced with the issue that when I access HaspMap through RwLock:
let mut trs = self.trs.write().unwrap();
dbg!("trs.len = {}", trs.len());
let del = trs[&tracer]; // Crashed here, no entry is found
dealloc(del.0, del.1);
trs.remove(&tracer);
What are you using for the hashmap key? It may be that the Hash and Eq implementations don't match up, or the key gets mutated while it is in the map. Both of these will break your map in weird and wonderful ways.
It is required that the keys implement the Eq and Hash traits, although this can frequently be achieved by using #[derive(PartialEq, Eq, Hash)] . If you implement these yourself, it is important that the following property holds:
k1 == k2 -> hash(k1) == hash(k2)
In other words, if two keys are equal, their hashes must be equal.
It is a logic error for a key to be modified in such a way that the key's hash, as determined by the Hash trait, or its equality, as determined by the Eq trait, changes while it is in the map. This is normally only possible through Cell, RefCell, global state, I/O, or unsafe code.
The key has type *const dyn Trace, which is a fat pointer (data pointer + vtable pointer). Unfortunately, in some circumstances, two different fat pointers to the same trait object can have different vtable pointers, and thus different hashes:
You could maybe work around this by using usize or a thin pointer type like *const () as the key in the hash map. (You can use as to cast from *const dyn Trace to *const (), discarding the vtable pointer.) This will work as long as all of your objects live at different addresses. (It could fail if some of your objects can have the same address but different concrete types, for example if one is stored in a field of another, or if they are different zero-sized types.)
Alternately, you might be able to fix this by setting incremental = false and codegen-units = 1 in the [profile] sections of your Cargo.toml, for any crate that depends (directly or indirectly) on this code.
If your objects all occupy disjoint locations in memory, then different objects will always have different pointers. The only problem is if you might have two objects occupying the same or overlapping locations. (For example, if one of your objects is a struct, and another object stored inline in one of the fields of that struct.) This would be a problem for both "thin" and "fat" pointers.
Since you are implementing a garbage collector, I would expect that each managed value lives in a separate memory allocation. If so, then you don't need to worry about this.
Yes, I think the same ...
But by some reason HashMap cannot find the key ... But when I iterate over HashMap I see this key inside of container ... very strange issue
How are you checking the key during iteration? If you are printing it out using dbg! or println! then you will only see half of the fat pointer, the "data" part. For example:
let x: *const dyn Debug = &"hello";
dbg!(x);
// prints "x = 0x000055a6d15c84c8"
// For debugging purposes only; don't do this transmute in production code.
let (data, vtable) = unsafe { transmute::<_, (*const (), *const ())>(x) };
dbg!((data, vtable));
// prints "(data, vtable) = (0x000055a6d15c84c8, 0x000055a6d15c84e0)"
The problem with comparing trait object pointers is that the vtable part might be different even for two pointers to the same object. You won't see this in normal debug output because it doesn't print the vtable part.
let mut trs = self.trs.write().unwrap();
for trc in (&*trs).into_iter() {
dbg!("trc = {}", trc.0); // Key is found
}
let del = trs[&tracer]; // Crash with panic
tracer is a "fat" pointer: It contains two pointers. But dbg! only prints out one of the two pointers, so you can't use dbg! to see whether two fat pointers are equal. See the code above for a way to print out both parts of the fat pointer.
The link above explains that different pointers to the same trait object are not guaranteed to be equal because the compiler may generate multiple copies of the vtable. This is just an unfortunate fact of how trait objects are compiled currently. See my earlier comments for how to work around this problem.
It's not clear yet whether this will be fixed. I agree that it's very surprising. If it isn't fixed, I hope it can at least be made less surprising, maybe by adding new compiler warnings: