Skip to content

Safe execute #139

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

Closed
wants to merge 1 commit into from
Closed

Safe execute #139

wants to merge 1 commit into from

Conversation

kelunik
Copy link
Member

@kelunik kelunik commented Jan 12, 2017

This ensures we have a safe execute context again and removes the side effect of setFactory. I think we could even allow setFactory when running now.

This has the slight disadvantage for sync applications that they have to wrap their setup code in Loop::execute, too.

@davidwdan
Copy link

@kelunik how would this work with this change?

@kelunik
Copy link
Member Author

kelunik commented Jan 12, 2017

@davidwdan It would just call getPreviousDriver instead get / getDriver.

@davidwdan
Copy link

@kelunik with the change you suggested, running this code gives warnings and throws an error:

$loop = new \WyriHaximus\React\AsyncInteropLoop\AsyncInteropLoop();

$loop->addPeriodicTimer(1, function () {
    echo 'test';
});

$loop->run();

Output:

Warning:  assert(): The Loop accessor may only be used inside a Loop::execute scope. failed in /htdocs/reactphp-async-interop-loop/vendor/async-interop/event-loop/src/Loop.php on line 195
PHP Fatal error:  Uncaught Error: Call to a member function repeat() on null in /htdocs/reactphp-async-interop-loop/vendor/async-interop/event-loop/src/Loop.php:196

Run looks like:

    public function run()
    {
        Loop::execute(
            function() {},
            Loop::getPreviousDriver()
        );
    }

@kelunik
Copy link
Member Author

kelunik commented Jan 12, 2017

@davidwdan That prooves it's a good change. You're running a loop manually, but something assumes you're using the accessor. This will accidentally result in two different loops and subtle bugs. This change prevents that form happening that easily.

@davidwdan
Copy link

That proofs it's a good change.

This change seems like a solution in search of a problem.

@trowski
Copy link
Contributor

trowski commented Jan 12, 2017

@davidwdan This change is trying to prevent people from creating watchers from outside the loop and then calling Loop::execute(), because those watchers will not be executed unless the result of Loop::get() is passed to execute(). This fails silently and is likely confusing for new users. This PR addresses this issue by asserting watchers are created within an active loop context, preventing these bugs.

Tests will need to be modified to accommodate this change, but it should have no affect on applications unless mistakes are made that will lead to bugs.

@kelunik
Copy link
Member Author

kelunik commented Jan 12, 2017

@davidwdan Sorry, I got your code wrong. I thought it'd run an interop loop via ->run(), but that's the adapter that calls Loop::execute then.

The issue is the one @trowski already described. We had a clear execute scope at the every beginning when we decided to allow stacked loops to exist. We had to loosen that strict scope for wait, but I think the way proposed now is a way better way that still allows wait, as it restores that clear scope again and prevents the issues @trowski mentioned.

Another thing this change fixes is a default driver, which had to be reset as a side effect in Loop::setFactory. That side effect bugged me from the beginning, but I didn't see a better way at that time.

@kelunik
Copy link
Member Author

kelunik commented Jan 12, 2017

@davidwdan The code you mentioned is only an issue for applications, not for libraries, right? As far as I know you don't have a accessor and rely on injection of the loop. So libraries only create watchers once the loop runs, not outside of Loop::execute? Applications setting up the loop would have to be adjusted, but they need an update to use the interop loop anyway.

@davidwdan
Copy link

@kelunik I've played around with this some more and it's not as bad as I thought it would be.

Using existing react apps before this change:

$loop = new \WyriHaximus\React\AsyncInteropLoop\AsyncInteropLoop();

$source = new React\Stream\Stream(fopen('omg.txt', 'r'), $loop);
$dest = new React\Stream\Stream(fopen('wtf.txt', 'w'), $loop);

$source->pipe($dest);

$loop->run();

After this update:

Loop::execute(function(){
    $loop = new \WyriHaximus\React\AsyncInteropLoop\AsyncInteropLoop();

    $source = new React\Stream\Stream(fopen('omg.txt', 'r'), $loop);
    $dest = new React\Stream\Stream(fopen('wtf.txt', 'w'), $loop);

    $source->pipe($dest);
});

This does change how react apps have worked in the past, which might hinder adoption.

@kelunik
Copy link
Member Author

kelunik commented Jan 13, 2017

@davidwdan That's right, it affects the bootstrap code of React applications, but it's a single line to remove and two lines to add + some indenting. I think that change is totally worth for the benefit of a clear scope we get. I think we could even remove then Loop::setFactory restriction then, as it no longer has side effects.

This does change how react apps have worked in the past, which might hinder adoption.

I don't really think this hinders adoption. That kind of code is only used for bootstrapping in applications, no? It doesn't change anything for libraries and those are the ones we really care about being interoperable.

@joshdifabio
Copy link
Contributor

What's the use case for getPreviousDriver()?

@kelunik
Copy link
Member Author

kelunik commented Jan 13, 2017

@joshdifabio It's for implementing wait which continues the previous loop.

@joshdifabio
Copy link
Contributor

@joshdifabio It's for implementing wait which continues the previous loop.

Of course, thanks.

@WyriHaximus
Copy link
Member

@kelunik @davidwdan Looking at that last example the impact is actually minimal. And when looking at how I personally usually build up async (sub)projects this is a minor change. Tbh I doubt it will hinder adaptation much 😄

@davidwdan
Copy link

I disagree. I think it will be a deterrent. React is over 90% of async php, so for all intents and purposes, is the current standard. This means that even without this change, there is little incentive for any react lib to adopt the interop loop.

@WyriHaximus
Copy link
Member

You might actually be right about that 🤐 .

@kelunik
Copy link
Member Author

kelunik commented Jan 13, 2017

@davidwdan In fact, the following code fails with the current code, which is quite surprising. It would fail with the PR merged.

// require autoloader
// setup factory

Loop::defer(function () {
    // never executed
});

Loop::execute(function () {
    // executes a new loop, the previous defer was registered on another loop
});

@kelunik kelunik mentioned this pull request Jan 31, 2017
@kelunik
Copy link
Member Author

kelunik commented Feb 21, 2017

I disagree. I think it will be a deterrent. React is over 90% of async php, so for all intents and purposes, is the current standard.

Sure, but React also doesn't have a global accessor currently, but passes the loop around.

This means that even without this change, there is little incentive for any react lib to adopt the interop loop.

For React libraries nothing will change, this PR doesn't affect libraries at all.

As React doesn't see real benefits in adopting the interop loop, they'll probably be covered using an adapter. It's up to them to provide an adapter the other way round then, any library / application using the interop loop can also use any React library by adding the adapter.

@WyriHaximus @cboden @davidwdan @mbonneau What do you think about the PR now? Could you change your reaction on the original post if your opinion changed or state a reason against this PR otherwise?

@kelunik
Copy link
Member Author

kelunik commented Apr 17, 2017

Closing, as project discontinued for now.

@kelunik kelunik closed this Apr 17, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants