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

Fix: some specs rely on Fiber.yield behavior #6953

Merged

Conversation

ysbaddaden
Copy link
Contributor

Some specs are relying on the Fiber.yield behavior, that's actually the event loop behavior (libevent2). They start failing whenever the Fiber.yield algorithm changes —for example to push the current fiber to the runnables queue, instead of adding a resume event.

This patch proposes changes to make the fiber synchronization expectations explicit. Either by looping until somethig is ready, or using Channel::Unbuffered's sync ability, or with an explicit enqueue/resume for specs testing Channel itself.

Some specs are relying on the `Fiber.yield` behavior is actually the
event loop behavior (libevent2). They start failing whenever the
`Fiber.yield` algorithm changes —for example to push the current
fiber to the runnables queue, instead of adding a resume event.

This patch proposes changes to make the fiber synchronization
expectations explicit. Either by looping until somethig is ready, or
using Channel::Unbuffered's sync ability, or with an explicit
enqueue/resume for specs testing Channel itself.
@ysbaddaden ysbaddaden force-pushed the fix-specs-rely-on-fiber-yield-behavior branch from f5ccf66 to d623176 Compare October 17, 2018 10:26
main = Fiber.current

sender = Fiber.new do
ch.send(nil)
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Related to #6948 : I'd expect ch.send to raise here since it couldn't deliver the value.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented in the issue. I think raising only in the second send is the right thing.

I would leave the rescue block to catch upon the second send.

@straight-shoota
Copy link
Member

The behaviour provided by the spec helpers yield_to and wait_for is actually pretty generically usable. I'd suggest to include them into the stdlib API.

Maybe Fiber#resume should even enqueue the current fiber by default. If you don't want that, use Crystal::Scheduler.resume directly.

Copy link
Contributor

@RX14 RX14 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be nice if we could remove Fiber.yield entirely.

four = true
two = false
three = false
four = false
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we eventually assert that they'll be true, so I wanted to also make sure the variables were changed —meaning that the fibers have run. We could even initialize them to nil.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess because the assertions at the end are should be_true so the test might have passed before if none of the spawns were executed.

@ysbaddaden
Copy link
Contributor Author

ysbaddaden commented Oct 17, 2018

Fiber synchronization should be achieved through synchronous channels, as we do for the future and lazy specs; yield_to is only there to test Channel itself. We could abstract wait_for, it's a spin-lock with a timeout (we can't use synchronization for these cases), but this isn't the goal of this PR.

Fiber#resume and Crystal::Scheduler.resume(fiber) having different semantics would only bring confusion. Maybe Fiber#resume will be removed, maybe not. Again, this isn't the topic of this PR.

spec/std/channel_spec.cr Show resolved Hide resolved
spec/std/channel_spec.cr Show resolved Hide resolved
main = Fiber.current

sender = Fiber.new do
ch.send(nil)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commented in the issue. I think raising only in the second send is the right thing.

I would leave the rescue block to catch upon the second send.

spec/std/channel_spec.cr Show resolved Hide resolved
spec/std/channel_spec.cr Show resolved Hide resolved
spec/std/channel_spec.cr Show resolved Hide resolved
@@ -34,13 +34,12 @@ describe Concurrent::Future do

describe "future" do
it "computes a value" do
chan = Channel(Int32).new(1)
chan = Channel::Unbuffered(Int32).new
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is changing the spec to run with buffered channel to an unbuffered.

Is there any reason for that change other than buffered channel of size 1 seems to be an unbuffered channel?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An unbuffered channel is synchronous, so it forces main/fiber to sync as we expect it. Unlike Buffered that are async.

Unrelated to this PR, but I think both buffered/unbuffered should have both (a)synchronous modes —in fact, I would have just buffered channels with a blocking argument to select sync/async.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, it's true the behavior is different. And that change does not affect the spec.

I think is more clear to have the async/sync nature in the type and not in the type argument.

spec/std/concurrent/future_spec.cr Show resolved Hide resolved
four = true
two = false
three = false
four = false
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess because the assertions at the end are should be_true so the test might have passed before if none of the spawns were executed.

spec/std/openssl/ssl/server_spec.cr Show resolved Hide resolved
Copy link
Member

@bcardiff bcardiff left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GTG!

@RX14 RX14 merged commit bc06cc2 into crystal-lang:master Oct 17, 2018
@RX14 RX14 added this to the 0.27.0 milestone Oct 17, 2018
@ysbaddaden ysbaddaden deleted the fix-specs-rely-on-fiber-yield-behavior branch October 17, 2018 22:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants