ruby 'async/io' and Reactor, help understand the example












1















I need some help in understanding basic 'async/io' example as given here:



require 'async/io'

def echo_server(endpoint)
Async::Reactor.run do |task|
# This is a synchronous block within the current task:
endpoint.accept do |client|
# This is an asynchronous block within the current reactor:
data = client.read(512)

# This produces out-of-order responses.
task.sleep(rand * 0.01)

client.write(data.reverse)
end
end
end

def echo_client(endpoint, data)
Async::Reactor.run do |task|
endpoint.connect do |peer|
result = peer.write(data)

message = peer.read(512)

puts "Sent #{data}, got response: #{message}"
end
end
end

Async::Reactor.run do
endpoint = Async::IO::Endpoint.tcp('0.0.0.0', 9000)

server = echo_server(endpoint)

5.times.collect do |i|
echo_client(endpoint, "Hello World #{i}")
end.each(&:wait)

server.stop
end


A reactor pattern (correct please if wrong) is basically kind of scheduler of synchronous tasks, such that upon blocking, a task is suspended and another one is launched, and so on, and in turn tasks are resumed once their operation is unblocked [source]



In the given github example first the echo_server method returning Async::Task is defined, and assigned to server variable server



Now that the variable is created, the underlying task starts listening on the socket and gets blocked by client.read(512) call. It is suspended and the flow reaches the loop part where 5 client Async::Tasks one by one write messages to the socket.



And now happens something I don't understand. The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop. However it serves all five requests and quits after that. Obviously there is something I am getting wrong, but I cannot figure it out. Any comments are highly appreciated.










share|improve this question





























    1















    I need some help in understanding basic 'async/io' example as given here:



    require 'async/io'

    def echo_server(endpoint)
    Async::Reactor.run do |task|
    # This is a synchronous block within the current task:
    endpoint.accept do |client|
    # This is an asynchronous block within the current reactor:
    data = client.read(512)

    # This produces out-of-order responses.
    task.sleep(rand * 0.01)

    client.write(data.reverse)
    end
    end
    end

    def echo_client(endpoint, data)
    Async::Reactor.run do |task|
    endpoint.connect do |peer|
    result = peer.write(data)

    message = peer.read(512)

    puts "Sent #{data}, got response: #{message}"
    end
    end
    end

    Async::Reactor.run do
    endpoint = Async::IO::Endpoint.tcp('0.0.0.0', 9000)

    server = echo_server(endpoint)

    5.times.collect do |i|
    echo_client(endpoint, "Hello World #{i}")
    end.each(&:wait)

    server.stop
    end


    A reactor pattern (correct please if wrong) is basically kind of scheduler of synchronous tasks, such that upon blocking, a task is suspended and another one is launched, and so on, and in turn tasks are resumed once their operation is unblocked [source]



    In the given github example first the echo_server method returning Async::Task is defined, and assigned to server variable server



    Now that the variable is created, the underlying task starts listening on the socket and gets blocked by client.read(512) call. It is suspended and the flow reaches the loop part where 5 client Async::Tasks one by one write messages to the socket.



    And now happens something I don't understand. The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop. However it serves all five requests and quits after that. Obviously there is something I am getting wrong, but I cannot figure it out. Any comments are highly appreciated.










    share|improve this question



























      1












      1








      1








      I need some help in understanding basic 'async/io' example as given here:



      require 'async/io'

      def echo_server(endpoint)
      Async::Reactor.run do |task|
      # This is a synchronous block within the current task:
      endpoint.accept do |client|
      # This is an asynchronous block within the current reactor:
      data = client.read(512)

      # This produces out-of-order responses.
      task.sleep(rand * 0.01)

      client.write(data.reverse)
      end
      end
      end

      def echo_client(endpoint, data)
      Async::Reactor.run do |task|
      endpoint.connect do |peer|
      result = peer.write(data)

      message = peer.read(512)

      puts "Sent #{data}, got response: #{message}"
      end
      end
      end

      Async::Reactor.run do
      endpoint = Async::IO::Endpoint.tcp('0.0.0.0', 9000)

      server = echo_server(endpoint)

      5.times.collect do |i|
      echo_client(endpoint, "Hello World #{i}")
      end.each(&:wait)

      server.stop
      end


      A reactor pattern (correct please if wrong) is basically kind of scheduler of synchronous tasks, such that upon blocking, a task is suspended and another one is launched, and so on, and in turn tasks are resumed once their operation is unblocked [source]



      In the given github example first the echo_server method returning Async::Task is defined, and assigned to server variable server



      Now that the variable is created, the underlying task starts listening on the socket and gets blocked by client.read(512) call. It is suspended and the flow reaches the loop part where 5 client Async::Tasks one by one write messages to the socket.



      And now happens something I don't understand. The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop. However it serves all five requests and quits after that. Obviously there is something I am getting wrong, but I cannot figure it out. Any comments are highly appreciated.










      share|improve this question
















      I need some help in understanding basic 'async/io' example as given here:



      require 'async/io'

      def echo_server(endpoint)
      Async::Reactor.run do |task|
      # This is a synchronous block within the current task:
      endpoint.accept do |client|
      # This is an asynchronous block within the current reactor:
      data = client.read(512)

      # This produces out-of-order responses.
      task.sleep(rand * 0.01)

      client.write(data.reverse)
      end
      end
      end

      def echo_client(endpoint, data)
      Async::Reactor.run do |task|
      endpoint.connect do |peer|
      result = peer.write(data)

      message = peer.read(512)

      puts "Sent #{data}, got response: #{message}"
      end
      end
      end

      Async::Reactor.run do
      endpoint = Async::IO::Endpoint.tcp('0.0.0.0', 9000)

      server = echo_server(endpoint)

      5.times.collect do |i|
      echo_client(endpoint, "Hello World #{i}")
      end.each(&:wait)

      server.stop
      end


      A reactor pattern (correct please if wrong) is basically kind of scheduler of synchronous tasks, such that upon blocking, a task is suspended and another one is launched, and so on, and in turn tasks are resumed once their operation is unblocked [source]



      In the given github example first the echo_server method returning Async::Task is defined, and assigned to server variable server



      Now that the variable is created, the underlying task starts listening on the socket and gets blocked by client.read(512) call. It is suspended and the flow reaches the loop part where 5 client Async::Tasks one by one write messages to the socket.



      And now happens something I don't understand. The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop. However it serves all five requests and quits after that. Obviously there is something I am getting wrong, but I cannot figure it out. Any comments are highly appreciated.







      ruby async-await reactor tcpsocket






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Jan 3 at 13:03









      ioquatix

      882922




      882922










      asked Nov 23 '18 at 11:46









      ClergymanClergyman

      1461213




      1461213
























          1 Answer
          1






          active

          oldest

          votes


















          0














          echo_client is executed 5 times as it is invoked from a loop. That function calls endpoint.connect and sends a single message and reads a single response.



          echo_server is executed 1 time and calls endpoint.accept which yields the block for each connection. The server reads one message and writes it back.




          The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop.




          endpoint.accept is implemented as a loop:



                  def accept(backlog = Socket::SOMAXCONN, &block)
          bind do |server|
          server.listen(backlog)

          server.accept_each(&block)
          end
          end


          Here is the implementation of server.accept_each:



                  def accept_each(task: Task.current)
          task.annotate "accepting connections #{self.local_address.inspect}"

          while true
          self.accept(task: task) do |io, address|
          yield io, address, task: task
          end
          end
          end


          As you can see, it binds to the socket, listens for incoming connections, and then invokes accept in a loop.






          share|improve this answer























            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            autoActivateHeartbeat: false,
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














            draft saved

            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53446123%2fruby-async-io-and-reactor-help-understand-the-example%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            1 Answer
            1






            active

            oldest

            votes








            1 Answer
            1






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes









            0














            echo_client is executed 5 times as it is invoked from a loop. That function calls endpoint.connect and sends a single message and reads a single response.



            echo_server is executed 1 time and calls endpoint.accept which yields the block for each connection. The server reads one message and writes it back.




            The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop.




            endpoint.accept is implemented as a loop:



                    def accept(backlog = Socket::SOMAXCONN, &block)
            bind do |server|
            server.listen(backlog)

            server.accept_each(&block)
            end
            end


            Here is the implementation of server.accept_each:



                    def accept_each(task: Task.current)
            task.annotate "accepting connections #{self.local_address.inspect}"

            while true
            self.accept(task: task) do |io, address|
            yield io, address, task: task
            end
            end
            end


            As you can see, it binds to the socket, listens for incoming connections, and then invokes accept in a loop.






            share|improve this answer




























              0














              echo_client is executed 5 times as it is invoked from a loop. That function calls endpoint.connect and sends a single message and reads a single response.



              echo_server is executed 1 time and calls endpoint.accept which yields the block for each connection. The server reads one message and writes it back.




              The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop.




              endpoint.accept is implemented as a loop:



                      def accept(backlog = Socket::SOMAXCONN, &block)
              bind do |server|
              server.listen(backlog)

              server.accept_each(&block)
              end
              end


              Here is the implementation of server.accept_each:



                      def accept_each(task: Task.current)
              task.annotate "accepting connections #{self.local_address.inspect}"

              while true
              self.accept(task: task) do |io, address|
              yield io, address, task: task
              end
              end
              end


              As you can see, it binds to the socket, listens for incoming connections, and then invokes accept in a loop.






              share|improve this answer


























                0












                0








                0







                echo_client is executed 5 times as it is invoked from a loop. That function calls endpoint.connect and sends a single message and reads a single response.



                echo_server is executed 1 time and calls endpoint.accept which yields the block for each connection. The server reads one message and writes it back.




                The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop.




                endpoint.accept is implemented as a loop:



                        def accept(backlog = Socket::SOMAXCONN, &block)
                bind do |server|
                server.listen(backlog)

                server.accept_each(&block)
                end
                end


                Here is the implementation of server.accept_each:



                        def accept_each(task: Task.current)
                task.annotate "accepting connections #{self.local_address.inspect}"

                while true
                self.accept(task: task) do |io, address|
                yield io, address, task: task
                end
                end
                end


                As you can see, it binds to the socket, listens for incoming connections, and then invokes accept in a loop.






                share|improve this answer













                echo_client is executed 5 times as it is invoked from a loop. That function calls endpoint.connect and sends a single message and reads a single response.



                echo_server is executed 1 time and calls endpoint.accept which yields the block for each connection. The server reads one message and writes it back.




                The server task gets unlocked and replies to the first message. After that it should quit, because there is no kind of loop.




                endpoint.accept is implemented as a loop:



                        def accept(backlog = Socket::SOMAXCONN, &block)
                bind do |server|
                server.listen(backlog)

                server.accept_each(&block)
                end
                end


                Here is the implementation of server.accept_each:



                        def accept_each(task: Task.current)
                task.annotate "accepting connections #{self.local_address.inspect}"

                while true
                self.accept(task: task) do |io, address|
                yield io, address, task: task
                end
                end
                end


                As you can see, it binds to the socket, listens for incoming connections, and then invokes accept in a loop.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Jan 3 at 9:13









                ioquatixioquatix

                882922




                882922






























                    draft saved

                    draft discarded




















































                    Thanks for contributing an answer to Stack Overflow!


                    • Please be sure to answer the question. Provide details and share your research!

                    But avoid



                    • Asking for help, clarification, or responding to other answers.

                    • Making statements based on opinion; back them up with references or personal experience.


                    To learn more, see our tips on writing great answers.




                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53446123%2fruby-async-io-and-reactor-help-understand-the-example%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    404 Error Contact Form 7 ajax form submitting

                    How to know if a Active Directory user can login interactively

                    Refactoring coordinates for Minecraft Pi buildings written in Python