Skip to content

Borrowing model #3

@torkleyy

Description

@torkleyy

This issue shall discuss the borrowing model of the nodes in vnodes. For those of you who don't know, these nodes will be structured in a tree like this:

├── dev
│   ├── keyboards
│   │   ├── 0
│   │   └── 1
│   └── platform
├── ecs
│   ├── get
│   ├── insert
│   ├── join
│   └── world
│       ├── BarResource
│       └── FooResource
├── io
│   ├── assets
│   │   ├── dwarven_crossbow.gltf
│   │   ├── mesh.obj
│   │   └── terrain.png
│   └── configs
│       └── display.ron
│           ├── msaa
│           ├── resolution
│           │   ├── height
│           │   └── width
│           └── vsync
└── scripts
    ├── dragons.lua
    ├── inventory.rhai
    └── weapons.rhai

Each identifier above is called a node. Note that just because it is represented as a node, it doesn't mean it has that's that internal representation. Especially for values inside configs, that would be too expensive.

Requirements of the borrowing model

Obviously it should work in parallel. What parts exactly shall work in parallel, where we make exceptions, that's part of this discussion. To find a good model, we need to know which operations are very common and which are not (and thus may be more expensive).

Common operations

  • very common: calling a node; this can be a script or an engine function that's exposed via nodes (e.g. /ecs/insert to insert a component)
  • reading a node's internal data, e.g. values from a config file, a resource
  • writing to the node when it gets called

Average

  • random writes to nodes, e.g. on an event callback

Rare operations

  • creating new nodes
  • overriding an existing node
  • removing an existing node

Selected models with their pros and cons

Static borrowing model

In the static borrowing model, the compiler controls read and write access. Nodes would be retrieved as references from the tree.

Advanges

  • very fast: checked at compile-time
  • low implementation effort

Disadvantages

  • only allows to borrow one node mutably at once
  • forces us to either make everything immutable or drop parallelism

Wrap everything with a Mutex

A Mutex can be locked to get a mutable reference. This would be done internally, and every node would be an Arc<Mutex<Node>>. Note that deadlocks aren't possible except the node triggers a callback that tries to borrow the same node (but this is an issue with every model I know of).

Advantages

  • easy for implementors because they always have mutable access
  • if the same node is barely used in parallel, this allows things to run arbitrarily in parallel

Disadvantages

  • high overhead

Make every method receive &self, let user choose how to wrap

If every method of a node (calling, setting a sub node, reading a value, ..) takes an immutable reference, there is no borrowing issue anymore. However, quite some nodes actually do need write access. For that, they would need to wrap their internal data or specific fields with a Mutex, RwLock, etc.

Advantages

  • improves on the above approach of wrapping everything by default, immutable nodes won't need to do any locking
  • atomicity and individual strategies can be used to optimize parallel access

Disadvantages

  • additional burden on implementors
  • overhead differs, largely dependent on how much the nodes are optimized

Some random ideas

  • acquiring tickets upfront, similar to system dependencies in Specs (seems difficult, esp wrt callbacks; might be solved by deferring them)
  • creating copies of the data with the risk of reading dirty (outdated) revisions
  • working with change sets, possibly STMs
  • create a topological sort of the nodes, allowing to lock specific regions (like say /dev/keyboards recursively) by an index range

That's all I can think of for now. Please add your ideas and opinions below ;)

Metadata

Metadata

Assignees

No one assigned

    Labels

    designRFC; this heavily influences API and usagehelp wantedExtra attention is neededperformance

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions