From 81201a176ea2e61e76130a4ce35125e6e17b24eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Burgos=20Maci=C3=A1?= Date: Tue, 11 Jun 2019 17:06:25 -0400 Subject: [PATCH 1/2] Updates docs, removes refs. to IO.select() [ci skip] --- lib/puma/client.rb | 7 ++++--- lib/puma/reactor.rb | 32 ++++++++++++++++---------------- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/lib/puma/client.rb b/lib/puma/client.rb index 5084e325..0aa4425e 100644 --- a/lib/puma/client.rb +++ b/lib/puma/client.rb @@ -27,9 +27,10 @@ module Puma # For example a web request from a browser or from CURL. This # # An instance of `Puma::Client` can be used as if it were an IO object - # for example it is passed into `IO.select` inside of the `Puma::Reactor`. - # This is accomplished by the `to_io` method which gets called on any - # non-IO objects being used with the IO api such as `IO.select. + # by the reactor, that's because the latter is expected to call `#to_io` + # on any non-IO objects it polls. For example nio4r internally calls + # `IO::try_convert` (which may call `#to_io`) when a new socket is + # registered. # # Instances of this class are responsible for knowing if # the header and body are fully buffered via the `try_to_finish` method. diff --git a/lib/puma/reactor.rb b/lib/puma/reactor.rb index f4ec92ee..a6245f52 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -20,10 +20,11 @@ module Puma # # ## Reactor Flow # - # A request comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance. - # The reactor stores the request in an array and calls `IO.select` on the array in a loop. + # A connection comes into a `Puma::Server` instance, it is then passed to a `Puma::Reactor` instance, + # which stores it in an array and waits for any of the connections to be ready for reading. # - # When the request is written to by the client then the `IO.select` will "wake up" and + # The waiting/wake up is performed with nio4r, which will use the apropriate backend (libev, Java NIO or + # just plain IO#select). The call to `NIO::Selector#select` (hereinafter `select()`) will "wake up" and # return the references to any objects that caused it to "wake". The reactor # then loops through each of these request objects, and sees if they're complete. If they # have a full header and body then the reactor passes the request to a thread pool. @@ -69,19 +70,18 @@ module Puma private - # Until a request is added via the `add` method this method will internally # loop, waiting on the `sockets` array objects. The only object in this # array at first is the `@ready` IO object, which is the read end of a pipe # connected to `@trigger` object. When `@trigger` is written to, then the loop - # will break on `IO.select` and return an array. + # will break on `select()` and return an array. # # ## When a request is added: # # When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array. # Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`. # - # When that happens, the internal loop stops blocking at `IO.select` and returns a reference + # When that happens, the internal loop stops blocking at `select()` and returns a reference # to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`. # When `@trigger` is written-to, the loop "wakes" and the `ready` # variable returns an array of arrays that looks like `[[#], [], []]` where the @@ -97,7 +97,7 @@ module Puma # to the `@ready` IO object. For example: `[#, #]`. # # Since the `Puma::Client` in this example has data that has not been read yet, - # the `IO.select` is immediately able to "wake" and read from the `Puma::Client`. At this point the + # the `select()` is immediately able to "wake" and read from the `Puma::Client`. At this point the # `ready` output looks like this: `[[#], [], []]`. # # Each element in the first entry is iterated over. The `Puma::Client` object is not @@ -109,12 +109,12 @@ module Puma # # If the request body is not present then nothing will happen, and the loop will iterate # again. When the client sends more data to the socket the `Puma::Client` object will - # wake up the `IO.select` and it can again be checked to see if it's ready to be + # wake up the `select()` and it can again be checked to see if it's ready to be # passed to the thread pool. # # ## Time Out Case # - # In addition to being woken via a write to one of the sockets the `IO.select` will + # In addition to being woken via a write to one of the sockets the `select()` will # periodically "time out" of the sleep. One of the functions of this is to check for # any requests that have "timed out". At the end of the loop it's checked to see if # the first element in the `@timeout` array has exceed its allowed time. If so, @@ -124,7 +124,7 @@ module Puma # # This behavior loops until all the objects that have timed out have been removed. # - # Once all the timeouts have been processed, the next duration of the `IO.select` sleep + # Once all the timeouts have been processed, the next duration of the `select()` sleep # will be set to be equal to the amount of time it will take for the next timeout to occur. # This calculation happens in `calculate_sleep`. def run_internal @@ -320,7 +320,7 @@ module Puma end end - # The `calculate_sleep` sets the value that the `IO.select` will + # The `calculate_sleep` sets the value that the `select()` will # sleep for in the main reactor loop when no sockets are being written to. # # The values kept in `@timeouts` are sorted so that the first timeout @@ -351,18 +351,18 @@ module Puma # object. # # The main body of the reactor loop is in `run_internal` and it - # will sleep on `IO.select`. When a new connection is added to the + # will sleep on `select()`. When a new connection is added to the # reactor it cannot be added directly to the `sockets` array, because - # the `IO.select` will not be watching for it yet. + # the `select()` will not be watching for it yet. # - # Instead what needs to happen is that `IO.select` needs to be woken up, + # Instead what needs to happen is that `select()` needs to be woken up, # the contents of `@input` added to the `sockets` array, and then - # another call to `IO.select` needs to happen. Since the `Puma::Client` + # another call to `select()` needs to happen. Since the `Puma::Client` # object can be read immediately, it does not block, but instead returns # right away. # # This behavior is accomplished by writing to `@trigger` which wakes up - # the `IO.select` and then there is logic to detect the value of `*`, + # the `select()` and then there is logic to detect the value of `*`, # pull the contents from `@input` and add them to the sockets array. # # If the object passed in has a timeout value in `timeout_at` then From 7b0c13486acc17a9c52c25a461f857d67c96af80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jes=C3=BAs=20Burgos=20Maci=C3=A1?= Date: Tue, 11 Jun 2019 18:54:32 -0400 Subject: [PATCH 2/2] Explicitly say that we use NIO's `#select` method [ci skip] --- lib/puma/reactor.rb | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/puma/reactor.rb b/lib/puma/reactor.rb index a6245f52..0d419cfd 100644 --- a/lib/puma/reactor.rb +++ b/lib/puma/reactor.rb @@ -24,7 +24,7 @@ module Puma # which stores it in an array and waits for any of the connections to be ready for reading. # # The waiting/wake up is performed with nio4r, which will use the apropriate backend (libev, Java NIO or - # just plain IO#select). The call to `NIO::Selector#select` (hereinafter `select()`) will "wake up" and + # just plain IO#select). The call to `NIO::Selector#select` will "wake up" and # return the references to any objects that caused it to "wake". The reactor # then loops through each of these request objects, and sees if they're complete. If they # have a full header and body then the reactor passes the request to a thread pool. @@ -74,14 +74,14 @@ module Puma # loop, waiting on the `sockets` array objects. The only object in this # array at first is the `@ready` IO object, which is the read end of a pipe # connected to `@trigger` object. When `@trigger` is written to, then the loop - # will break on `select()` and return an array. + # will break on `NIO::Selector#select` and return an array. # # ## When a request is added: # # When the `add` method is called, an instance of `Puma::Client` is added to the `@input` array. # Next the `@ready` pipe is "woken" by writing a string of `"*"` to `@trigger`. # - # When that happens, the internal loop stops blocking at `select()` and returns a reference + # When that happens, the internal loop stops blocking at `NIO::Selector#select` and returns a reference # to whatever "woke" it up. On the very first loop, the only thing in `sockets` is `@ready`. # When `@trigger` is written-to, the loop "wakes" and the `ready` # variable returns an array of arrays that looks like `[[#], [], []]` where the @@ -97,7 +97,7 @@ module Puma # to the `@ready` IO object. For example: `[#, #]`. # # Since the `Puma::Client` in this example has data that has not been read yet, - # the `select()` is immediately able to "wake" and read from the `Puma::Client`. At this point the + # the `NIO::Selector#select` is immediately able to "wake" and read from the `Puma::Client`. At this point the # `ready` output looks like this: `[[#], [], []]`. # # Each element in the first entry is iterated over. The `Puma::Client` object is not @@ -109,12 +109,12 @@ module Puma # # If the request body is not present then nothing will happen, and the loop will iterate # again. When the client sends more data to the socket the `Puma::Client` object will - # wake up the `select()` and it can again be checked to see if it's ready to be + # wake up the `NIO::Selector#select` and it can again be checked to see if it's ready to be # passed to the thread pool. # # ## Time Out Case # - # In addition to being woken via a write to one of the sockets the `select()` will + # In addition to being woken via a write to one of the sockets the `NIO::Selector#select` will # periodically "time out" of the sleep. One of the functions of this is to check for # any requests that have "timed out". At the end of the loop it's checked to see if # the first element in the `@timeout` array has exceed its allowed time. If so, @@ -124,7 +124,7 @@ module Puma # # This behavior loops until all the objects that have timed out have been removed. # - # Once all the timeouts have been processed, the next duration of the `select()` sleep + # Once all the timeouts have been processed, the next duration of the `NIO::Selector#select` sleep # will be set to be equal to the amount of time it will take for the next timeout to occur. # This calculation happens in `calculate_sleep`. def run_internal @@ -320,7 +320,7 @@ module Puma end end - # The `calculate_sleep` sets the value that the `select()` will + # The `calculate_sleep` sets the value that the `NIO::Selector#select` will # sleep for in the main reactor loop when no sockets are being written to. # # The values kept in `@timeouts` are sorted so that the first timeout @@ -351,18 +351,18 @@ module Puma # object. # # The main body of the reactor loop is in `run_internal` and it - # will sleep on `select()`. When a new connection is added to the + # will sleep on `NIO::Selector#select`. When a new connection is added to the # reactor it cannot be added directly to the `sockets` array, because - # the `select()` will not be watching for it yet. + # the `NIO::Selector#select` will not be watching for it yet. # - # Instead what needs to happen is that `select()` needs to be woken up, + # Instead what needs to happen is that `NIO::Selector#select` needs to be woken up, # the contents of `@input` added to the `sockets` array, and then - # another call to `select()` needs to happen. Since the `Puma::Client` + # another call to `NIO::Selector#select` needs to happen. Since the `Puma::Client` # object can be read immediately, it does not block, but instead returns # right away. # # This behavior is accomplished by writing to `@trigger` which wakes up - # the `select()` and then there is logic to detect the value of `*`, + # the `NIO::Selector#select` and then there is logic to detect the value of `*`, # pull the contents from `@input` and add them to the sockets array. # # If the object passed in has a timeout value in `timeout_at` then