tdlib-ruby icon indicating copy to clipboard operation
tdlib-ruby copied to clipboard

Handling updates in background

Open yi-jiayu opened this issue 6 years ago • 2 comments

Is there a way to register handlers and then loop forever while processing them?

I tried this naive way of doing so:

  while client.alive?
  end

However this pegs the CPU at 100%.

Here's something I've tried instead:

diff --git a/lib/tdlib/client.rb b/lib/tdlib/client.rb
index 54356a9..3d8279f 100644
--- a/lib/tdlib/client.rb
+++ b/lib/tdlib/client.rb
@@ -49,10 +49,14 @@ class TD::Client
       end
     end
 
-    @update_manager.run
+    @thread = @update_manager.run
     ready
   end
 
+  def wait
+    @thread.join if @thread
+  end
+
   # Sends asynchronous request to the TDLib client and returns Promise object
   # @see TD::ClientMethods List of available queries as methods
   # @see https://github.com/ruby-concurrency/concurrent-ruby/blob/master/docs-source/promises.in.md

This assigns the thread returned by UpdateManager#run to an instance variable so that it can be joined in the Client#wait method.

Then I create some signal handlers to call Client#dispose on interrupt (which causes the thread started by UpdateManager to return), and wait on the client:

diff --git a/README.md b/README.md
index 5e1ba88..b3a162c 100644
--- a/README.md
+++ b/README.md
@@ -75,7 +75,7 @@ begin
               nil
             end
   end
-  
+
   client.connect
 
   loop do
@@ -99,6 +99,25 @@ begin
     sleep 0.1
   end
 
+  client.on(TD::Types::Update) do |update|
+      puts 'Got update:'
+      puts update
+    end
+
+    Signal.trap("INT") {
+      Thread.start do
+        client.dispose
+      end
+    }
+
+    Signal.trap("TERM") {
+      Thread.start do
+        client.dispose
+      end
+    }
+
+    client.wait
+
 ensure
   client.dispose
 end

This processes updates continuously without spinning.

yi-jiayu avatar Feb 15 '19 12:02 yi-jiayu

Would this work for you?

while client.alive?
  sleep 1
end

sleep will put the current running thread to sleep and schedule the execution of another thread.

But I agree that we should think about implementing the built-in way of doing this. Joining UpdateManager thread is a good idea, but I will think a bit more about the public API.

vladislav-yashin avatar Feb 17 '19 06:02 vladislav-yashin

Cool, that works.

Did a simple comparison between the three methods:

Modified Client:

$ time timeout 5 bundle exec ruby example.rb 

real	0m5.020s
user	0m0.496s
sys	0m0.211s

While loop without sleep:

$ time timeout 5 bundle exec ruby example.rb 

real	0m5.017s
user	0m4.720s
sys	0m0.182s

While loop with sleep 1:

$ time timeout 5 bundle exec ruby example.rb 

real	0m5.025s
user	0m0.454s
sys	0m0.175s

The while loop without sleep spends almost all of its time in user mode, while both joining the UpdateManager thread and a while loop with sleep 1 perform similarly.

yi-jiayu avatar Feb 19 '19 10:02 yi-jiayu