dart-lang / sdk

The Dart SDK, including the VM, JS and Wasm compilers, analysis, core libraries, and more.
https://dart.dev
BSD 3-Clause "New" or "Revised" License
10.23k stars 1.57k forks source link

local storage specific to async/await/futures chains (ie similar to thread local storage) #31990

Open unicomp21 opened 6 years ago

unicomp21 commented 6 years ago

We need the equivalent of thread local storage for async/await chains, which can be used w/ existing try-finally support.

When building cache's, to preserve locality of reference and avoid hitting the memory management, one needs the equivalent of thread-local-storage for the async/await chains in dart.

unicomp21 commented 6 years ago

In addition to an implied "this" pointer, could dart objects also have a "coroutine_context" pointer where a thread local storage equivalent pattern is needed?

unicomp21 commented 6 years ago

Do "Zone"s already fill this need?

anders-sandholm commented 6 years ago

@floitschG @lrhn

lrhn commented 6 years ago

A Zone can definitely support a zone-specific storage. Objects, on the other hand, have no specific zone affiliation, zones are "dynamic contexts" that are remembered across asynchronous gaps.

Maybe you can use something like

class ZoneStore<T> {
  final _key = new Object();
  T get value => Zone.current[_key];
  void update(T newValue, void valueScope()) {
    runZoned(valueScope, zoneValues: {_key: newValue});
  }
}

as an immutable zone-based storage where you can set a new value for a sub-computation:

var _myVar = new ZoneStore<int>();
... 
var old = _myVar.value;  // say 42
_myVar.update(old + 1, () {
  print(_myVar.value);  // 43
});
print(_myVar.value); // 42

If you want a mutable storage, it's harder than thread-local storage because threads are disjoint, Dart zones are nested. You need to manually insert delimiters when creating new zones to avoid them inheriting the super-zone's mutable variable. Maybe something like:

class ZoneVar<T> {
  final _mapping = new Expando<T>();
  // Used as key in zones to find expando-key for that zone, and as expando-key for the default zone.
  final _zoneKey = new Object();  
  ZoneVar(T initialValue) { 
    _mapping[_zoneKey] = initialValue; 
  }
  T get value => _mapping[Zone.current[_zoneKey] ?? _zoneKey];
  void set value(T newValue) {
    _mapping[Zone.current[_zoneKey] ?? _zoneKey] = newValue;
  }
  void newScope(T initialValue, void scope()) {
    var zoneId = new Object();
    _mapping[zoneId] = initialValue;
    runZoned(scope, zoneValues: {_zoneKey: zoneId});
  }
}  

This stores a mutable variable, and can introduce a new mutable variable in a sub-scope.

var myVar = new ZoneVar<int>(0);
print(myVar.value);  // 0
myVar.value = 1;
print(myVar.value);  // 1
myVar.newScope(42, () {
  print(myVar.value);  // 42
  myVar.value = 37;
  print(myVar.value); // 37
});
print(myVar.value);  // 1
bsutton commented 2 years ago

Comment so I have link to issue