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

Thread Scheduler for light weight concurrency. #1870

Closed
wants to merge 20 commits into from

Conversation

@ioquatix
Copy link
Member

@ioquatix ioquatix commented May 4, 2018

Ruby concurrency can be greatly enhanced by concurrent, deterministic IO.

Fibers have been shown many times to be a great abstraction for this purpose. They retain normal code flow and don't require any kind of Thread synchronisation. They are enjoyable to write code with because you don't have to concern yourself with thread synchronisation or other complicated issues.

The basic idea is that if some operation would block, it yields the Fiber, and other Fibers within the thread can continue to execute.

There are a number of ways to implement this. Here is a proof of concept to amend the existing rb_io_wait_readable/rb_io_wait_writable.

This design minimally affects the Ruby implementation and allows flexibility for selector implementation. With a small amount of work, we can support EventMachine (65 million downloads), NIO4r (21 million downloads). It would be trivial to back port.

This PR isn't complete but I am seeking feedback. If it's a good idea, I will do my best to see it through to completion, including support for EventMachine and NIO4r.

https://bugs.ruby-lang.org/issues/14736

@ioquatix ioquatix force-pushed the ioquatix:thread-selector branch 3 times, most recently from 62ffa82 to a2d3b45 May 4, 2018
@ioquatix ioquatix force-pushed the ioquatix:thread-selector branch 2 times, most recently from 86287d1 to 9d83559 May 4, 2018
io.c Outdated Show resolved Hide resolved
@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Jun 2, 2018

Okay I will resume work on this issue.

@ioquatix ioquatix force-pushed the ioquatix:thread-selector branch from 9d83559 to 1fe81b8 Jun 4, 2018
@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Jun 4, 2018

@hkdnet I fixed that issue. Thanks :)

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Jul 5, 2018

@funny-falcon here is updated example using transfer rather than yield/resume.

As you can see it require no change to underlying C since the scheduler is implemented in pure Ruby.

@ioquatix ioquatix force-pushed the ioquatix:thread-selector branch from 72bbaf8 to 9ba7045 Jul 8, 2018
@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Jul 8, 2018

I have rebased on trunk so that tests will run.

@senid231
Copy link

@senid231 senid231 commented Mar 19, 2019

What's the status of this feature? Will it be merged?

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 4, 2019

I would like to think we can merge this sooner rather than later. It's minimal surface area and the potential benefit is huge.

That being said, I think that the action of merging this, gently tugs on the rudder controlling the direction of Ruby, and that part might require the input of the captain. So, it's not really my place to merge it, but requires further discussion with Matz et al.

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 4, 2019

@headius @eregon what do you think of the feasibility of implementing this in JRuby/TruffleRuby?

@eregon
Copy link
Member

@eregon eregon commented Apr 5, 2019

@ioquatix This looks very simple and not invasive for the implementation, so that's definitely a very nice aspect of it.

I'm not super familiar with selectors, but is having hooks in rb_io_wait_readable/rb_io_wait_writable enough? For instance, IO#read will use the read(2) syscall and that blocks by default if the fd is not set as nonblock (which is the default in Ruby AFAIK). Or is the idea the library using these selectors sets every IO it cares about in nonblock mode using e.g., io/nonblock?

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 5, 2019

@eregon yes it's correct, users need to explicitly opt into io.nonblock = true for it to invoke rb_io_wait_*, which I think is acceptable.

@eregon
Copy link
Member

@eregon eregon commented Apr 5, 2019

@ioquatix What about things like File.read(path) where the fd is not even passed to the Ruby side? This would block, and wouldn't that block the "event loop" making it unresponsive?

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 5, 2019

File.read cannot be made non-blocking as the underling OS normally doesn't support it, so while that's a good point, it's largely irrelevant right now.

A better solution would be to make all IO non-blocking in the context of a Thread with a selector defined, but I think I just wanted to keep this POC simple.

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 5, 2019

Keep in mind, with liburing, apparently some file operations on Linux will become non-blocking, which is interesting.

https://lwn.net/Articles/776428/

@hakusaro
Copy link

@hakusaro hakusaro commented Apr 5, 2019

So, it's not really my place to merge it, but requires further discussion with Matz et al.

Are you going to RubyKaigi? That could be a good venue to engage Matz in.

I think this addition would jive quite well with Ruby 3x3. There are a lot of us glued to MRI. Working with MRI changes is much easier than switching to JRuby for quite a number of people.

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 5, 2019

Yes, I am going to RubyKaigi to discuss it with Matz.

The design goal of this PR is to be simple enough that the majority of Ruby platforms can support it.

@hakusaro
Copy link

@hakusaro hakusaro commented Apr 24, 2019

@ioquatix how'd it go?

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 24, 2019

I got buy in from JRuby and TruffleRuby so we are going to push forward on those fronts to start with, to prove the concept.

@ioquatix ioquatix force-pushed the ioquatix:thread-selector branch from b5d0135 to 41cb0d1 Apr 12, 2020
@headius
Copy link
Contributor

@headius headius commented Apr 14, 2020

I got buy in from JRuby

I don't know if I'd say that 😁

Without a design document that we can iterate on I will not commit to anything. We need to see and discuss the design before moving forward.

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 14, 2020

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 14, 2020

We discussed it years ago and you said you were keen to try it out :)

@ioquatix ioquatix closed this Apr 14, 2020
@ioquatix ioquatix deleted the ioquatix:thread-selector branch Apr 14, 2020
@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 14, 2020

Sorry, stuffed up the PR, here is the link: #3032

@MSP-Greg
Copy link
Contributor

@MSP-Greg MSP-Greg commented Apr 14, 2020

@ioquatix

Re the MinGW SSL errors, MSYS2's OpenSSL uses a path relative to the exe file for the location of DEFAULT_CERT_FILE, DEFAULT_CERT_DIR, and DEFAULT_CONFIG_FILE. That isn't set up in the CI.

Hence, using OpenSSL::SSL::VERIFY_NONE may solve the issue. I haven't tested. Note that msvc/mswin OpenSSL builds typically use an absolute path for those locations.

@ioquatix
Copy link
Member Author

@ioquatix ioquatix commented Apr 14, 2020

@MSP-Greg thanks for the quick reply. I'll try using VERIFY_NONE. For testing purposes it's fine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment