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

Parallel transformations are run twice when one of the Futures resolves the other synchronously #367

Open
Avaq opened this issue Sep 7, 2019 · 4 comments
Labels

Comments

@Avaq
Copy link
Member

Avaq commented Sep 7, 2019

First discovered in fluture-js/fluture-hooks#6, this is one of several thinkable issues that arise once control over the resolution of a Future is taken outside of the Future constructor, for example:

let exportedRes;

// We're defeating Fluture's design and exfiltrating the res function:
const m1 = Future ((rej, res) => {
  exportedRes = res;
});

// Then we create another Future which forces the resolution of the former:
const m2 = Future ((rej, res) => {
  res (42);
  exportedRes (42);
});

// Now when we (re)combine these two Futures with Fluture, Fluture
// is going to run the effect of `both` (or whatever combinator was
// used) twice: once as a result of m2 starting a parallel process,
// and once more for m1 resolving as a result of that parallel process.
both (m1) (m2)

The example above is very similar to what fluture-hooks does to achieve its parallel applicative instance. Arguably, this is not a bug, because the user is abusing Fluture: they're manually combining m1 and m2 outside of Fluture's API for combining Futures. However, since it's possible to achieve this at all; I still want to record it as a bug and think about how Fluture should behave under these scenarios.

@Avaq Avaq added the bug label Sep 7, 2019
@Avaq Avaq changed the title Parallel transformations are run twice when one of the Futures resolves the other Parallel transformations are run twice when one of the Futures resolves the other synchronously Sep 7, 2019
@Avaq
Copy link
Member Author

Avaq commented Sep 7, 2019

It's might also be worth addressing why a user would want to exfiltrate the res function in the first place, and work towards allowing the user to substitute it with a more idiomatic pattern.

In the case of fluture-hooks, it is required because we need a mechanism for two Futures to hang until some external process has finished its thing, at which point both Futures should resolve.

@safareli
Copy link

safareli commented Jul 6, 2021

Will this issue be resolved, if rej and res functions are wrapped in a way that makes them idempotent?

@Avaq
Copy link
Member Author

Avaq commented Jul 6, 2021

Hi @safareli :)

No. Rej and res are already wrapped for idempotence:

Fluture/src/future.js

Lines 153 to 169 in f349eb4

cancel = computation(function Computation$rej(x){
cont = function Computation$rej$cont(){
open = false;
rej(x);
};
if(open){
cont();
}
}, function Computation$res(x){
cont = function Computation$res$cont(){
open = false;
res(x);
};
if(open){
cont();
}
});

The problem happens because the internal mechanics of both (or any parallel combinator), and how it combines the two parallel Futures.

@Avaq
Copy link
Member Author

Avaq commented Jul 6, 2021

It's built on the assumption that the userland of one Future doesn't trigger signals in the other. So the signals from the other future are not guarded while running the one Future.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants