Sharing only some fields of a struct between threads - multithreading

I'm writing a simulation program using two threads: one for the actual simulation and the other for rendering.
The simulation thread has a Vec of Entity structs that it uses each tick. My problem arises when the rendering thread needs to access the Vec of entities in order to render them, but some of the fields of the Entity struct do not implement the Send trait. However, these fields are not actually used for rendering.
The only solution I have thought of is to split each entity into two structs: one which contains the internal 'non-sendable' fields and the other which contains the fields necessary for rendering. I could then use two separate Vecs and wrap the 'sendable' one in a Mutex and share it with the rendering thread. However this means I will have two parts to each entity and that each struct will have to store the index of the other part to keep them tied together, but this feels like a massive headache to manage when entities are being created and deleted a lot.
Is there a standard way of doing this in Rust? It feels like a fairly common requirement for lots of programs, but I can't find an easy way to do it.

You're on the right track with the split-Entity design, but you can take it one step further with cues from the Entity-Component-System (ECS) pattern. Rather than having a Vec<SimEntity> and a Vec<RenderEntity>, in which the two kinds of Entity must keep track of each other, the ECS pattern says that an entity is simply a collection of components. In a simple model, this would be something like
struct Sim { /* ... */ }
struct Render { /* ... */ }
struct Entity {
sim_id: usize,
render_id: usize,
}
struct World {
entities: Vec<Entity>,
sims: Vec<Sim>,
renders: Arc<Mutex<Vec<Render>>>,
}
Then, a system in ECS terms is simply a function that acts on components rather than entities. This pattern parallelizes well because a given system can lock only those components which it needs, rather than locking all entity data. Here's a playground with a simple (and somewhat naive) proof-of-concept with a separate rendering thread which shares data with the simulation thread.
If you're looking for an implementation or inspiration, Rust has numerous ECS libraries. You can find extensive benchmarks of the various options in rust-gamedev/ecs-bench-suite.

Related

Is there a way to automatically register trait implementors?

I'm trying to load JSON files that refer to structs implementing a trait. When the JSON files are loaded, the struct is grabbed from a hashmap. The problem is, I'll probably have to have a lot of structs put into that hashmap all over my code. I would like to have that done automatically. To me this seems to be doable with procedural macros, something like:
#[my_proc_macro(type=ImplementedType)]
struct MyStruct {}
impl ImplementedType for MyStruct {}
fn load_implementors() {
let implementors = HashMap::new();
load_implementors!(implementors, ImplementedType);
}
Is there a way to do this?
No
There is a core issue that makes it difficult to skip manually inserting into a structure. Consider this simplified example, where we simply want to print values that are provided separately in the code-base:
my_register!(alice);
my_register!(bob);
fn main() {
my_print(); // prints "alice" and "bob"
}
In typical Rust, there is no mechanism to link the my_print() call to the multiple invocations of my_register. There is no support for declaration merging, run-time/compile-time reflection, or run-before-main execution that you might find in other languages that might make this possible (unless of course there's something I'm missing).
But Also Yes
There are third party crates built around link-time or run-time tricks that can make this possible:
ctor allows you to define functions that are executed before main(). With it, you can have my_register!() create invididual functions for alice and bob that when executed will add themselves to some global structure which can then be accessed by my_print().
linkme allows you to define a slice that is made from elements defined separately, which are combined at compile time. The my_register!() simply needs to use this crate's attributes to add an element to the slice, which my_print() can easily access.
I understand skepticism of these methods since the declarative approach is often clearer to me, but sometimes they are necessary or the ergonomic benefits outweigh the "magic".

Trying to make a graph with cycles in RUST [duplicate]

I have a data structure which can be represented as a unidirectional graph between some structs linked with link objects because links contain metadata.
It looks something like this:
struct StateMachine {
resources: Vec<Resource>,
links: Vec<Link>,
}
struct Resource {
kind: ResourceType,
// ...
}
enum LinkTarget {
ResourceList(Vec<&Resource>),
LabelSelector(HashMap<String, String>),
}
struct Link {
from: LinkTarget,
to: LinkTarget,
metadata: SomeMetadataStruct,
}
The whole structure needs to be mutable because I need to be able to add and remove links and resources at runtime. Because of this, I cannot use the normal lifetime model and bind the resources to the parent struct's lifetime.
I understand that I need to "choose my own guarantee" by picking the appropriate type, but I'm not sure what's the best way to solve this problem.
Modeling graph-like structures in Rust is not a simple problem.
Here there are two valuable discussions from Nick Cameron and Niko Matsakis (two main Rust developers at Mozilla.)
Graphs and arena allocation
Modeling Graphs in Rust Using Vector Indices
Actually, for a graph like structure, the simplest solution is to use an arena such as TypedArena.
The lifetime of the nodes will then be only dependent on the lifetime of the instance of the typed arena they were created from, which will greatly simplify resource management.
Warning: avoid a scenario where you dynamically add/remove nodes to the graph, as the nodes will NOT be removed from the arena until said arena is dropped, so the size of the arena would grow, unbounded.
If you are in a situation where you will add/remove nodes at runtime, another solution is to:
have a collection of Resources
have the edges only indirectly refer to the Resources (not owners, and not borrowers either)
Two examples:
HashMap<ResourceId, (Resource, Vec<ResourceId>)>
type R = RefCell<Resource>, Vec<Rc<R>> and Vec<(Weak<R>, Vec<Weak<R>>)>
in either case, you are responsible for cleaning up the edges when removing a resource, and forgetting may lead to a memory leak and panics (when dereferencing) but is otherwise safe.
There are, probably, infinite variations on the above.
The simplest solution for a graph-like structure is to use a library which models graphs. petgraph is a good choice:
use petgraph::Graph; // 0.5.1
use std::{collections::HashMap, rc::Rc};
struct Resource;
enum LinkTarget {
ResourceList(Vec<Rc<Resource>>),
LabelSelector(HashMap<String, String>),
}
struct SomeMetadataStruct;
fn main() {
let mut graph = Graph::new();
let n1 = graph.add_node(LinkTarget::LabelSelector(Default::default()));
let n2 = graph.add_node(LinkTarget::LabelSelector(Default::default()));
let _l2 = graph.add_edge(n1, n2, SomeMetadataStruct);
}
The guarantees that you have to choose here center around the member of ResourceList. I assume that you wish to have single-threaded shared immutable Resources.
if you need to share them across threads, use a Vec<Arc<Resource>>
if they aren't shared, you can own them — Vec<Resource>
if they need to be mutable, use a Vec<Rc<RefCell<Resource>>> (Or a Mutex or RwLock if also multithreaded)

Does Rust's borrow checker really mean that I should re-structure my program?

So I've read Why can't I store a value and a reference to that value in the same struct? and I understand why my naive approach to this was not working, but I'm still very unclear how to better handle my situation.
I have a program I wanted to structure like follows (details omitted because I can't make this compile anyway):
use std::sync::Mutex;
struct Species{
index : usize,
population : Mutex<usize>
}
struct Simulation<'a>{
species : Vec<Species>,
grid : Vec<&'a Species>
}
impl<'a> Simulation<'a>{
pub fn new() -> Self {...} //I don't think there's any way to implement this
pub fn run(&self) {...}
}
The idea is that I create a vector of Species (which won't change for the lifetime of Simulation, except in specific mutex-guarded fields) and then a grid representing which species live where, which will change freely. This implementation won't work, at least not any way I've been able to figure out. As I understand it, the issue is that pretty much however I make my new method, the moment it returns, all of the references in grid would becomine invalid as Simulation and therefor Simulation.species are moved to another location in the stack. Even if I could prove to the compiler that species and its contents would continue to exist, they actually won't be in the same place. Right?
I've looked into various ways around this, such as making species as an Arc on the heap or using usizes instead of references and implementing my own lookup function into the species vector, but these seem slower, messier or worse. What I'm starting to think is that I need to really re-structure my code to look something like this (details filled in with placeholders because now it actually runs):
use std::sync::Mutex;
struct Species{
index : usize,
population : Mutex<usize>
}
struct Simulation<'a>{
species : &'a Vec<Species>, //Now just holds a reference rather than data
grid : Vec<&'a Species>
}
impl<'a> Simulation<'a>{
pub fn new(species : &'a Vec <Species>) -> Self { //has to be given pre-created species
let grid = vec!(species.first().unwrap(); 10);
Self{species, grid}
}
pub fn run(&self) {
let mut population = self.grid[0].population.lock().unwrap();
println!("Population: {}", population);
*population += 1;
}
}
pub fn top_level(){
let species = vec![Species{index: 0, population : Mutex::new(0_)}];
let simulation = Simulation::new(&species);
simulation.run();
}
As far as I can tell this runs fine, and ticks off all the ideal boxes:
grid uses simple references with minimal boilerplate for me
these references are checked at compile time with minimal overhead for the system
Safety is guaranteed by the compiler (unlike a custom map based approach)
But, this feels very weird to me: the two-step initialization process of creating owned memory and then references can't be abstracted any way that I can see, which feels like I'm exposing an implementation detail to the calling function. top_level has to also be responsible for establishing any other functions or (scoped) threads to run the simulation, call draw/gui functions, etc. If I need multiple levels of references, I believe I will need to add additional initialization steps to that level.
So, my question is just "Am I doing this right?". While I can't exactly prove this is wrong, I feel like I'm losing a lot of near-universal abstraction of the call structure. Is there really no way to return species and simulation as a pair at the end (with some one-off update to make all references point to the "forever home" of the data).
Phrasing my problem a second way: I do not like that I cannot have a function with a signature of ()-> Simulation, when I can can have a pair of function calls that have that same effect. I want to be able to encapsulate the creation of this simulation. I feel like the fact that this approach cannot do so indicates I'm doing something wrong, and that there may be a more idiomatic approach I'm missing.
I've looked into various ways around this, such as making species as an Arc on the heap or using usizes instead of references and implementing my own lookup function into the species vector, but these seem slower, messier or worse.
Don't assume that, test it. I once had a self-referential (using ouroboros) structure much like yours, with a vector of things and a vector of references to them. I tried rewriting it to use indices instead of references, and it was faster.
Rc/Arc is also an option worth trying out — note that there is only an extra cost to the reference counting when an Arc is cloned or dropped. Arc<Species> doesn't cost any more to dereference than &Species, and you can always get an &Species from an Arc<Species>. So the reference counting only matters if and when you're changing which Species is in an element of Grid.
If you're owning a Vec of objects, then want to also keep track of references to particular objects in that Vec, a usize index is almost always the simplest design. It might feel like extra boilerplate to you now, but it's a hell of a lot better than properly dealing with keeping pointers in check in a self-referential struct (as somebody who's made this mistake in C++ more than I should have, trust me). Rust's rules are saving you from some real headaches, just not ones that are obvious to you necessarily.
If you want to get fancy and feel like a raw usize is too arbitrary, then I recommend you look at slotmap. For a simple SlotMap, internally it's not much more than an array of values, iteration is fast and storage is efficient. But it gives you generational indices (slotmap calls these "keys") to the values: each value is embellished with a "generation" and each index also internally keeps hold of a its generation, therefore you can safely remove and replace items in the Vec without your references suddenly pointing at a different object, it's really cool.

Bi-Directional References without Database

I'm trying to implement a tree structure and I would like that every node has a pointer to both its children and parent. I.e. the reference between two nodes goes both ways.
Do languages exist (e.g. python) where such a relation can be modeled nicely? What I am doing right now is:
class Node {
setParent(Node p) {
this.parent.chilren.remove(this)
p.chilren.add(this)
this.parent = p
}
// ...
}
But I would prefer an approach where I can factor out this aspect of a bidirectional reference such that I can reuse the same construct in other places. (e.g. a more declarative approach would be nice).
Look at Haskell and functional languages to simplify recursive data structures. See the article here.

Thread-safe data structure design

I have to design a data structure that is to be used in a multi-threaded environment. The basic API is simple: insert element, remove element, retrieve element, check that element exists. The structure's implementation uses implicit locking to guarantee the atomicity of a single API call. After i implemented this it became apparent, that what i really need is atomicity across several API calls. For example if a caller needs to check the existence of an element before trying to insert it he can't do that atomically even if each single API call is atomic:
if(!data_structure.exists(element)) {
data_structure.insert(element);
}
The example is somewhat awkward, but the basic point is that we can't trust the result of "exists" call anymore after we return from atomic context (the generated assembly clearly shows a minor chance of context switch between the two calls).
What i currently have in mind to solve this is exposing the lock through the data structure's public API. This way clients will have to explicitly lock things, but at least they won't have to create their own locks. Is there a better commonly-known solution to these kinds of problems? And as long as we're at it, can you advise some good literature on thread-safe design?
EDIT: I have a better example. Suppose that element retrieval returns either a reference or a pointer to the stored element and not it's copy. How can a caller be protected to safely use this pointer\reference after the call returns? If you think that not returning copies is a problem, then think about deep copies, i.e. objects that should also copy another objects they point to internally.
Thank you.
You either provide a mechanism for external locking (bad), or redesign the API, like putIfAbsent. The latter approach is for instance used for Java's concurrent data-structures.
And, when it comes to such basic collection types, you should check-out whether your language of choice already offers them in its standard library.
[edit]To clarify: external locking is bad for the user of the class, as it introduces another source of potential bugs. Yes, there are times, when performance considerations indeed make matters worse for concurrent data-structures than externally synchronized one, but those cases are rare, and then they usually can only be solved/optimized by people with far more knowledge/experience than me.
One, maybe important, performance hint is found in Will's answer below.
[/edit]
[edit2]Given your new example: Basically you should try to keep the synchronization of the collection and the of the elements separated as much as possible. If the lifetime of the elements is bound to its presence in one collection, you will run into problems; when using a GC this kind of problem actually becomes simpler. Otherwise you will have to use a kind of proxy instead of raw elements to be in the collection; in the simplest case for C++ you would go and use boost::shared_ptr, which uses an atomic ref-count. Insert usual performance disclaimer here. When you are using C++ (as I suspect as you talk about pointers and references), the combination of boost::shared_ptr and boost::make_shared should suffice for a while.
[/edit2]
Sometimes its expensive to create an element to be inserted. In these scenarios you can't really afford to routinely create objects that might already exist just in case they do.
One approach is for the insertIfAbsent() method to return a 'cursor' that is locked - it inserts a place-holder into the internal structure so that no other thread can believe it is absent, but does not insert the new object. The placeholder can contain a lock so that other threads that want to access that particular element must wait for it to be inserted.
In an RAII language like C++ you can use a smart stack class to encapsulate the returned cursor so that it automatically rolls-back if the calling code does not commit. In Java its a bit more deferred with the finalize() method, but can still work.
Another approach is for the caller to create the object that isn't present, but that to occasionally fail in the actual insertion if another thread has 'won the race'. This is how, for example, memcache updates are done. It can work very well.
What about moving the existance check into the .insert() method? A client calls it and if it returns false you know that something went wrong. Much like what malloc() does in plain old C -- return NULL if failed, set ERRNO.
Obviously you can also return an exception, or an instance of an object, and complicate your life up from there..
But please, don't rely on the user setting their own locks.
In an RAII style fashion you could create accessor/handle objects (don't know how its called, there probably exists a pattern of this), e.g. a List:
template <typename T>
class List {
friend class ListHandle<T>;
// private methods use NO locking
bool _exists( const T& e ) { ... }
void _insert( const T& e ) { ... }
void _lock();
void _unlock();
public:
// public methods use internal locking and just wrap the private methods
bool exists( const T& e ) {
raii_lock l;
return _exists( e );
}
void insert( const T& e ) {
raii_lock l;
_insert( e );
}
...
};
template <typename T>
class ListHandle {
List<T>& list;
public:
ListHandle( List<T>& l ) : list(l) {
list._lock();
}
~ListHandle() {
list._unlock();
}
bool exists( const T& e ) { return list._exists(e); }
void insert( const T& e ) { list._insert(e); }
};
List<int> list;
void foo() {
ListHandle<int> hndl( list ); // locks the list as long as it exists
if( hndl.exists(element) ) {
hndl.insert(element);
}
// list is unlocked here (ListHandle destructor)
}
You duplicate (or even triplicate) the public interface, but you give users the choice to choose between internal and safe and comfortable external locking wherever it is required.
First of all, you should really separate your concerns. You have two things to worry about:
The datastructure and its methods.
The thread synchronization.
I highly suggest you use an interface or virtual base class that represents the type of datastructure you are implementing. Create a simple implementation that does not do any locking, at all. Then, create a second implementation that wraps the first implementation and adds locking on top of it. This will allow a more performant implementation where locking isn't needed and will greatly simplify your code.
It looks like you are implementing some sort of dictionary. One thing you can do is provide methods that have semantics that are equivalent to the combined statement. For example setdefault is a reasonable function to provide that will set a value only if the corresponding key does not already exist in the dictionary.
In other words, my recommendation would be to figure out what combinations of methods are frequently used together, and simply create API methods that perform that combination of operations atomically.

Resources