Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extend the concept of async-init to cover asynchronous monitors #1

Closed
CMCDragonkai opened this issue Jul 15, 2022 · 7 comments
Closed
Assignees
Labels
enhancement New feature or request r&d:polykey:core activity 4 End to End Networking behind Consumer NAT Devices

Comments

@CMCDragonkai
Copy link
Member

Is your feature request related to a problem? Please describe.

In concurrent programming, a monitor is a synchronization construct that allows threads to have both mutual exclusion and the ability to wait (block) for a certain condition to become false. Monitors also have a mechanism for signaling other threads that their condition has been met. A monitor consists of a mutex (lock) object and condition variables. A condition variable essentially is a container of threads that are waiting for a certain condition. Monitors provide a mechanism for threads to temporarily give up exclusive access in order to wait for some condition to be met, before regaining exclusive access and resuming their task.

Another definition of monitor is a thread-safe class, object, or module that wraps around a mutex in order to safely allow access to a method or variable by more than one thread. The defining characteristic of a monitor is that its methods are executed with mutual exclusion: At each point in time, at most one thread may be executing any of its methods. By using one or more condition variables it can also provide the ability for threads to wait on a certain condition (thus using the above definition of a "monitor"). For the rest of this article, this sense of "monitor" will be referred to as a "thread-safe object/class/module".

Basically our usage of class decorators and method decorators for managing the asynchronous initialisation of classes is basically an implementation of the CS concept "monitor".

However the usage of locking right now only applies to asynchronous construction and asynchronous destruction.

We could generalise this library for arbitrary concurrent synchronisation, and call it js-async-monitors.

Describe the solution you'd like

Make it possible to use decorators to indicate arbitrary synchronisation requirements on class methods. I suspect that an extra decorator will be needed beyond class and method/member decorators. Probably something like function decorators, or just "hooks" similar to react hooks, that allow arbitrary synchronisation based on conditions.

It can make use of the Barrier that was recently introduced in js-async-locks.

Describe alternatives you've considered

Introducing more concurrent synchronisation can introduce subtle deadlocks. Programming models may give the possibility to break things. Better abstractions can help prevent mistakes, and I'm not sure if introducing more locking is the best solution.

At any case, a deadlock detection system should be in place to prevent races.

Additional context

@CMCDragonkai CMCDragonkai added the enhancement New feature or request label Jul 15, 2022
@CMCDragonkai CMCDragonkai added r&d:polykey:core activity 1 Secret Vault Sharing and Secret History Management r&d:polykey:core activity 2 Cross Platform Cryptography for JavaScript Platforms r&d:polykey:core activity 4 End to End Networking behind Consumer NAT Devices r&d:polykey:core activity 3 Peer to Peer Federated Hierarchy and removed r&d:polykey:core activity 1 Secret Vault Sharing and Secret History Management r&d:polykey:core activity 2 Cross Platform Cryptography for JavaScript Platforms r&d:polykey:core activity 4 End to End Networking behind Consumer NAT Devices r&d:polykey:core activity 3 Peer to Peer Federated Hierarchy labels Jul 23, 2022
@CMCDragonkai
Copy link
Member Author

Imagine something like this:

@AsyncMonitor
class C {
  @lock('resource')
  public async f() { /* ... */ }

  @lock('resource')
  public async g() { /* ... */ }
}

Doing so automatically adds mutual exclusion between f and g.

It's possible that the class itself requires a decorator to setup the utilities for mutual synchronisation, similar to how the async init class decorator is needed before method decorators.

@CMCDragonkai
Copy link
Member Author

It's like a macro, use it with great power and great responsibility.

@CMCDragonkai
Copy link
Member Author

CMCDragonkai commented Jun 4, 2023

A deadlock detection works by having a shared context. In this case the shared context can be maintained by the AsyncMonitor class decorator.

Specifically the shared context requires is the call-stack itself. That is when calling x.a(), which then calls x.b(). The second call must check if within the context the same lock has already been locked.

The method decorators can made invisible book-keeping attached to the call-stack? The only way to bookkeep certain state relative to the callstack is to attach as a parameter.

This makes it a little bit complicated since we basically need a ctx parameter similar to how we are using js-contexts. But doing so changes the type of the method.

Now it may not be a huge issue, if the ctx is always considered the last parameter, but consider methods that are arbitrary arity. Adding ctx can conflict with the outside type.

That being said, we did create decorators in js-context that force the usage of a parameter decorator that @context that ensures that this exists.

Only then can you "thread" a locking bookkeeper that knows what locks were locked in that call context. Even then this isn't foolproof because, if a method calls the other method transitively, it can forget to propagate this context.

I think a robust solution eventually somehow meta-program JS so you can get access to the meta callstack. Perhaps using something like: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/caller to get a reference to locking bookkeeper. Also see this MatrixAI/js-db#39 for general discussion about deadlock detection when you do have a context object (like in the case of js-db we have DBTransaction, whereas here this is more general).

Note that in the case of js-db, it already supports re-entrant locking. But it doesn't completely detect deadlocks. So a callstack context object is necessary for supporting re-entrant locking, but a full deadlock detector would be more robust.

@CMCDragonkai
Copy link
Member Author

This is now done in https://github.com/MatrixAI/js-async-monitor

@CMCDragonkai
Copy link
Member Author

I'm moving this there, and will close it there.

@CMCDragonkai CMCDragonkai transferred this issue from MatrixAI/js-async-init Jun 4, 2023
@CMCDragonkai CMCDragonkai self-assigned this Jun 4, 2023
@CMCDragonkai
Copy link
Member Author

I'm going to release this as 0.0.1 because this is mostly an experiment. There are still issues like the fact that there's no automatic deadlock detection which makes this utility a bit of a footgun.

@CMCDragonkai
Copy link
Member Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request r&d:polykey:core activity 4 End to End Networking behind Consumer NAT Devices
Development

No branches or pull requests

1 participant