<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="/assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Jason Madden (Posts about hub)</title><link>https://seecoresoftware.com/</link><description></description><atom:link href="https://seecoresoftware.com/blog/categories/hub.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><copyright>Contents © 2021 &lt;a href="mailto:jason@seecoresoftware.com"&gt;Jason Madden&lt;/a&gt; </copyright><lastBuildDate>Sat, 04 Dec 2021 18:43:33 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>Blocking gevent's Hub Part 2: Finding Blocking</title><link>https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html"&gt;Last time&lt;/a&gt; we looked at the
causes and consequences of greenlets that block and avoid switching to
the hub. Many times such greenlets (really, the code they run) are
obvious, but some times they are not.&lt;/p&gt;
&lt;p&gt;This post will look at some new features that gevent 1.3 added to make
finding greenlets that block easier. These can be used both during
development and in production.&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#what-it-does" id="id5"&gt;What It does&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#testing-and-asserting" id="id6"&gt;Testing And Asserting&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#how-it-works" id="id7"&gt;How It Works&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#performance" id="id8"&gt;Performance&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="what-it-does"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id5"&gt;What It does&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;gevent 1.3 &lt;a class="reference external" href="http://www.gevent.org/monitoring.html#blocking"&gt;includes a couple features&lt;/a&gt; to help find code that's
blocking. One of those we can turn on using just environment
variables, no code changes needed (although you can configure it in
code as well). This makes it especially handy for debugging on the
fly.&lt;/p&gt;
&lt;p&gt;Let's take a look at one of our blocking examples from last time:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-7"&gt;&lt;/a&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_f35e8e9a7c744e75945228e895396c3b-13"&gt;&lt;/a&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Elapsed time: &lt;/span&gt;&lt;span class="si"&gt;%1.1f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Now we'll tell gevent to let us know if it detects any greenlets
blocking the hub and run the example (the duration of time that a
greenlet is allowed to block before it gets reported is configurable;
we'll go with the defaults here):&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; &lt;span class="nv"&gt;GEVENT_MONITOR_THREAD_ENABLE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt; python /tmp/test.py
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;================================================================================&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-4"&gt;&lt;/a&gt;&lt;span class="go"&gt;2018-06-22T19:20:10Z : Greenlet &amp;lt;Greenlet "Greenlet-0" at 0x1.: f&amp;gt; appears to be blocked&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-5"&gt;&lt;/a&gt;&lt;span class="go"&gt;    Reported by &amp;lt;gevent.__tracer.GreenletTracer object at 0x...&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-6"&gt;&lt;/a&gt;&lt;span class="go"&gt;Blocked Stack (for thread id 0x...):&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-7"&gt;&lt;/a&gt;&lt;span class="go"&gt;  File "/tmp/test.py", line 5, in f&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-8"&gt;&lt;/a&gt;&lt;span class="go"&gt;    time.sleep(3)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-9"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-10"&gt;&lt;/a&gt;&lt;span class="go"&gt;Info:&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-11"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-12"&gt;&lt;/a&gt;&lt;span class="go"&gt;* Threads&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-13"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-14"&gt;&lt;/a&gt;&lt;span class="go"&gt;Thread 0x2 (MainThread)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-15"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;  File "/tmp/test.py", line 5, in f&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-17"&gt;&lt;/a&gt;&lt;span class="go"&gt;    time.sleep(3)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-18"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-19"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-20"&gt;&lt;/a&gt;&lt;span class="go"&gt;* Greenlets&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-21"&gt;&lt;/a&gt;&lt;span class="go"&gt;********************************************************************************&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-22"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;greenlet.greenlet object at 0x3&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-23"&gt;&lt;/a&gt;&lt;span class="go"&gt; :    Parent: None&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-24"&gt;&lt;/a&gt;&lt;span class="go"&gt; :    Monitoring Thread:&amp;lt;PeriodicMonitoringThread &amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-25"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;lt;greenlet.greenlet object at 0x4&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-26"&gt;&lt;/a&gt;&lt;span class="go"&gt; :    Parent: None&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-27"&gt;&lt;/a&gt;&lt;span class="go"&gt; +--- &amp;lt;Greenlet "Greenlet-0" at 0x1: f&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-28"&gt;&lt;/a&gt;&lt;span class="go"&gt; :          Parent: &amp;lt;Hub '' at 0x5 select default pending=0 ref=1 thread_ident=0x2&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-29"&gt;&lt;/a&gt;&lt;span class="go"&gt; :          Spawned at:&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-30"&gt;&lt;/a&gt;&lt;span class="go"&gt; :            File "/tmp/test.py", line 8, in &amp;lt;module&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-31"&gt;&lt;/a&gt;&lt;span class="go"&gt; :              greenlet = gevent.spawn(f)&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-32"&gt;&lt;/a&gt;&lt;span class="go"&gt; +--- &amp;lt;Hub '' at 0x5 select default pending=0 ref=1 thread_ident=0x2&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-33"&gt;&lt;/a&gt;&lt;span class="go"&gt;            Parent: &amp;lt;greenlet.greenlet object at 0x4&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-34"&gt;&lt;/a&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-35"&gt;&lt;/a&gt;&lt;span class="go"&gt; .&lt;/span&gt;
&lt;a name="rest_code_3aad8e5308154ca18b039ec68cef772f-36"&gt;&lt;/a&gt;&lt;span class="go"&gt; Elapsed time: 3.0&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;This time, gevent detected the hold up and printed a report about
what the problem was. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id3" id="id1"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;div class="section" id="testing-and-asserting"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id6"&gt;Testing And Asserting&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Either in unittests, from particularly latency sensitive parts of your
application, or when trying to further narrow down the cause of
blocking, gevent provides the &lt;a class="reference external" href="http://www.gevent.org/api/gevent.util.html#gevent.util.assert_switches"&gt;assert_switches()&lt;/a&gt; context manager.
Wrap this manager around some code to be sure that it switches to
another greenlet or back to the hub, optionally within a set amount of
time. If nothing switches, then an exception will be raised with the
same sort of information as above.&lt;/p&gt;
&lt;pre class="code pycon"&gt;&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;gevent.util&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;assert_switches&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-2"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c1"&gt;# This will always raise an exception: nothing switches&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-3"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;assert_switches&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-4"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-5"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-6"&gt;&lt;/a&gt;&lt;span class="gt"&gt;Traceback (most recent call last):&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-7"&gt;&lt;/a&gt;  File &lt;span class="nb"&gt;"&amp;lt;stdin&amp;gt;"&lt;/span&gt;, line &lt;span class="m"&gt;2&lt;/span&gt;, in &lt;span class="n"&gt;&amp;lt;module&amp;gt;&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-8"&gt;&lt;/a&gt;  File &lt;span class="nb"&gt;"//gevent/util.py"&lt;/span&gt;, line &lt;span class="m"&gt;592&lt;/span&gt;, in &lt;span class="n"&gt;__exit__&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-9"&gt;&lt;/a&gt;    &lt;span class="k"&gt;raise&lt;/span&gt; &lt;span class="n"&gt;_FailedToSwitch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;report_lines&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-10"&gt;&lt;/a&gt;&lt;span class="gr"&gt;gevent.util._FailedToSwitch&lt;/span&gt;: &lt;span class="n"&gt;...&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-11"&gt;&lt;/a&gt;&lt;span class="gp"&gt;&amp;gt;&amp;gt;&amp;gt; &lt;/span&gt;&lt;span class="c1"&gt;# This will never raise an exception; nothing switched,&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-12"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="c1"&gt;# but it happened very fast&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-13"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;&lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;assert_switches&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;max_blocking_time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mf"&gt;1.0&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-14"&gt;&lt;/a&gt;&lt;span class="gp"&gt;... &lt;/span&gt;    &lt;span class="k"&gt;pass&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-15"&gt;&lt;/a&gt;&lt;span class="gp"&gt;...&lt;/span&gt;
&lt;a name="rest_code_b97e4b1450b84117947fcb7e53ca0314-16"&gt;&lt;/a&gt;&lt;span class="go"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="how-it-works"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id7"&gt;How It Works&lt;/a&gt;&lt;/h2&gt;
&lt;div class="admonition admonition-credit"&gt;
&lt;p class="first admonition-title"&gt;Credit&lt;/p&gt;
&lt;p class="last"&gt;This technique did not originate with gevent. I've seen it in &lt;a class="reference external" href="http://www.rfk.id.au/blog/entry/detect-gevent-blocking-with-greenlet-settrace/"&gt;this
blog post from 2012 by Ryan Kelly&lt;/a&gt;
and I've seen it in &lt;a class="reference external" href="https://github.com/mozilla-services/mozservices/blob/master/mozsvc/gunicorn_worker.py"&gt;this code&lt;/a&gt;
from Mozilla. gevent 1.3 just generalized it, made it safe for
multiple threads and hubs, and incorporated it into the core of
gevent.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;The main question we want to be able to answer is: did the hub get to
run during a certain time period? Running the hub means switching into
its greenlet, so what we really want to know is: was the greenlet hub
switched into?&lt;/p&gt;
&lt;p&gt;The greenlet library provides a hook well suited for this purpose:
&lt;a class="reference external" href="https://greenlet.readthedocs.io/en/latest/#tracing-support"&gt;greenlet.settrace()&lt;/a&gt;. This functions similarly to &lt;a class="reference external" href="https://docs.python.org/3/library/sys.html#sys.settrace"&gt;sys.settrace()&lt;/a&gt;
that's part of the core interpreter. The later calls a function for
every "interesting" interpreter event such as a function call, and the
former calls a function for every interesting event involving
greenlets.&lt;/p&gt;
&lt;p&gt;Since greenlets can really only do two interesting things,
&lt;tt class="docutils literal"&gt;'switch'&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;'throw'&lt;/tt&gt; are the only two defined events—for
our purposes they are equivalent as they both involve a transfer of
control from one greenlet to another, and both events tell us which
greenlet is going to be taking control (the "target").&lt;/p&gt;
&lt;p&gt;So to answer our question, we only need to keep track of whether or
not the greenlet hub was the target of an event and thus assumes
control. For the decorator &lt;tt class="docutils literal"&gt;assert_switches&lt;/tt&gt;, which has a
well-defined scope and only needs to answer that question at the exact
time it is exited, that's enough &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id4" id="id2"&gt;[2]&lt;/a&gt;, but for the general monitoring
function that lets you know if (when) it &lt;em&gt;ever&lt;/em&gt; found the hub blocked
for longer than a certain period, we need to do a little more. To be
able to answer that question meaningfully, we also need to know the
timestamp of the last event entering the hub so we can compare it to
the current timestamp: if it's been "too long," then we can raise an
alert.&lt;/p&gt;
&lt;div class="section" id="performance"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id8"&gt;Performance&lt;/a&gt;&lt;/h3&gt;
&lt;div class="sidebar"&gt;
&lt;p class="first sidebar-title"&gt;Implementation Details Alert&lt;/p&gt;
&lt;p class="last"&gt;This post discusses implementation details of gevent 1.3. These
details generally should not be relied upon other than as
&lt;a class="reference external" href="http://gevent.org"&gt;documented&lt;/a&gt;, as they may change in the future.&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;gevent is a performance critical library, so monitoring features that
are too perfomance sucking to enable are a non-starter. For that
reason, the implementation of these features was carefully optimized
and a few different tradeoffs are made. Here are some examples.&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;Getting the current time (&lt;tt class="docutils literal"&gt;time.time()&lt;/tt&gt;) is relatively expensive,
so we try to avoid doing that too often. For example, instead of
checking if we've exceeded the blocking threshold on every greenlet
switch, we only do so periodically in a background thread.&lt;/li&gt;
&lt;li&gt;We use some heuristics to detect if we switched into the hub instead
of absolute timestamps.&lt;/li&gt;
&lt;li&gt;The greenlet trace functions are compiled with Cython and strongly
typed. This makes the relatively complex work that the blocking
monitor trace function needs to do &lt;a class="reference external" href="https://github.com/gevent/gevent/pull/1190"&gt;faster than the most trivial
trace function implemented in Python&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;As always, if you find bugs or can think of enhancements or additions
to gevent, please feel free to &lt;a class="reference external" href="https://github.com/gevent/gevent/issues"&gt;open an issue&lt;/a&gt; or &lt;a class="reference external" href="https://github.com/gevent/gevent/pulls"&gt;create a pull request&lt;/a&gt;!&lt;/p&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="id3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;I admit I cheated here in the output a little bit. I trimmed
it: it detected, and reported, the same blockage several
times. It should probably recognize that and just print
something about "same blockage". I should fix that. Also, I fixed up the spawning
line. Things spawned at the module level do not always get the
right traceback.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="id4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;As long as you don't use the &lt;tt class="docutils literal"&gt;max_blocking_time&lt;/tt&gt; parameter.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>1.3</category><category>blocking</category><category>debugging</category><category>gevent</category><category>greenlet</category><category>hub</category><category>implementation</category><category>python</category><category>series</category><category>tracing</category><guid>https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html</guid><pubDate>Wed, 27 Jun 2018 14:36:00 GMT</pubDate></item><item><title>Blocking gevent's Hub Part 1: Understanding Blocking</title><link>https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;div&gt;&lt;p&gt;&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html"&gt;In the beginning&lt;/a&gt; we talked about gevent's
hub and how greenlets switch in and out of it to implement IO.
&lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/05/implementing-gevent-locks.html"&gt;Following that&lt;/a&gt; we showed
how locks in gevent are implemented much the same way, by "parking" a
waiting greenlet, switching to the hub to let other greenlets run or
do IO, and eventually switching back to the parked greenlet.&lt;/p&gt;
&lt;p&gt;That's a lot of switching. What does it mean if that switching doesn't
happen? What should a programmer know about switching and its
opposite, blocking? (There's also &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html"&gt;part 2&lt;/a&gt;.)&lt;/p&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#cooperation" id="id5"&gt;Cooperation&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#examples" id="id6"&gt;Examples&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#synergism-matters" id="id7"&gt;Synergism Matters&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#avoiding-blocking" id="id8"&gt;Avoiding Blocking&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#diagnosing-blocking" id="id9"&gt;Diagnosing Blocking&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="cooperation"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id5"&gt;Cooperation&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;gevent (and greenlets in general) is a &lt;a class="reference external" href="http://www.gevent.org/intro.html#cooperative-multitasking"&gt;cooperative multitasking&lt;/a&gt;
system, meaning that any given greenlet runs until it chooses to give
up control. Under gevent, choosing to give up control is usually
automatic, and usually happens when the greenlet wants to handle IO,
such as reading or writing a socket to handle an incoming HTTP request
or HTTP response. A greenlet can also give up control automatically by
waiting for a lock that another greenlet owns, or it can explicitly
give up control by using the &lt;a class="reference external" href="http://www.gevent.org/api/gevent.html#gevent.sleep"&gt;gevent.sleep()&lt;/a&gt; API. In all those
cases, control passes to the hub (recall that the hub is the greenlet
running the event loop); this is referred to as &lt;em&gt;yielding&lt;/em&gt; to the hub.
When a greenlet yields to the hub, another greenlet gets a chance to
run or IO gets serviced by the event loop. A greenlet that yields to
the hub is called &lt;em&gt;cooperative&lt;/em&gt; (or sometimes &lt;em&gt;green&lt;/em&gt;). When a
greenlet doesn't yield, it's &lt;em&gt;non-cooperative&lt;/em&gt;.&lt;/p&gt;
&lt;div class="section" id="examples"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id6"&gt;Examples&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Of course, it's not actually the greenlet itself that's yielding, it's
the code that the greenlet is running that yields. We can thus say
that any given piece of code is &lt;em&gt;cooperative&lt;/em&gt; or &lt;em&gt;green&lt;/em&gt; or not. Here
are some examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;gevent.sleep()&lt;/tt&gt; is cooperative&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-6"&gt;&lt;/a&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-7"&gt;&lt;/a&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-9"&gt;&lt;/a&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-10"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-11"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_6a7deb6d8824499c92e9fa555775a298-13"&gt;&lt;/a&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Elapsed time: &lt;/span&gt;&lt;span class="si"&gt;%1.0f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;On my system, that prints three dots, indicating that the greenlets
are yielding to the hub and switching back and forth:&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_529a6ccfa7f841b0a9e0886cea79d6d0-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_529a6ccfa7f841b0a9e0886cea79d6d0-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;...&lt;/span&gt;
&lt;a name="rest_code_529a6ccfa7f841b0a9e0886cea79d6d0-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 3.0&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;I will omit the timing part of the main greenlet in the rest of the
examples unless it changes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;tt class="docutils literal"&gt;time.sleep()&lt;/tt&gt; is &lt;strong&gt;not&lt;/strong&gt; cooperative&lt;/p&gt;
&lt;p&gt;Here's that same example, with the inner greenlet using
&lt;a class="reference external" href="https://docs.python.org/3/library/time.html#time.sleep"&gt;time.sleep()&lt;/a&gt; from the standard library.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_271c60c003414780b5b546ca9efa3b8b-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;pre class="code console"&gt;&lt;a name="rest_code_450f9391b13945818ad6f5f16dafc97c-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_450f9391b13945818ad6f5f16dafc97c-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.&lt;/span&gt;
&lt;a name="rest_code_450f9391b13945818ad6f5f16dafc97c-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 3.0&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;We get only one dot, indicating that as soon as the main greenlet
yielded to the hub, which in turn handed control over to the
greenlet running &lt;tt class="docutils literal"&gt;f&lt;/tt&gt;, that greenlet failed to yield again until it
was completely finished.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;ul id="cpu-bound"&gt;
&lt;li&gt;&lt;p class="first"&gt;Code that is CPU-bound is &lt;strong&gt;not&lt;/strong&gt; cooperative&lt;/p&gt;
&lt;p&gt;If a function is in a tight computation loop, there is no opportunity for it
to yield control.&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-3"&gt;&lt;/a&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-4"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-5"&gt;&lt;/a&gt;    &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-6"&gt;&lt;/a&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt; &lt;span class="o"&gt;**&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_c839e70401954c55b609cc37b6ef96be-7"&gt;&lt;/a&gt;        &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
&lt;/pre&gt;&lt;pre class="code console"&gt;&lt;a name="rest_code_3f843718c61649cba37f46a3ec101225-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_3f843718c61649cba37f46a3ec101225-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.&lt;/span&gt;
&lt;a name="rest_code_3f843718c61649cba37f46a3ec101225-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 28.2&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;If that had been cooperative, we would have expected to see about 28
dots, since that's how long the inner greenlet ran. We only got one.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Socket IO using &lt;a class="reference external" href="http://www.gevent.org/api/gevent.socket.html#module-gevent.socket"&gt;gevent.socket&lt;/a&gt; is cooperative&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent.socket&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostbyname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'www.python.org'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-7"&gt;&lt;/a&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;now&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-9"&gt;&lt;/a&gt;&lt;span class="n"&gt;greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-10"&gt;&lt;/a&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;dead&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-11"&gt;&lt;/a&gt;    &lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-12"&gt;&lt;/a&gt;    &lt;span class="n"&gt;gevent&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.001&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# NOTE: We switch to a smaller sleep duration&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-13"&gt;&lt;/a&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_12e38b1111d549749f49987410ae30a2-14"&gt;&lt;/a&gt;&lt;span class="k"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;Elapsed time: &lt;/span&gt;&lt;span class="si"&gt;%1.1f&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;%&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;now&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Lots of dots here.&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_ad7db88b07014ae9a1bf441e63b2cc8d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_ad7db88b07014ae9a1bf441e63b2cc8d-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.....................&lt;/span&gt;
&lt;a name="rest_code_ad7db88b07014ae9a1bf441e63b2cc8d-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 0.0&lt;/span&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Socket IO using the standard &lt;a class="reference external" href="https://docs.python.org/3/library/socket.html#module-socket"&gt;socket&lt;/a&gt; is &lt;strong&gt;not&lt;/strong&gt; cooperative &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id3" id="id1"&gt;[1]&lt;/a&gt;&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;time&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;gevent&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;socket&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-4"&gt;&lt;/a&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;f&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
&lt;a name="rest_code_b25787bbc23d4aa7a34af949303cc339-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;gethostbyname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'www.python.org'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;Again, only one dot, meaning no yielding.&lt;/p&gt;
&lt;pre class="code console"&gt;&lt;a name="rest_code_f0b7ea5beb6f42e08f26950b24f01a9d-1"&gt;&lt;/a&gt;&lt;span class="gp"&gt;$&lt;/span&gt; python test.py
&lt;a name="rest_code_f0b7ea5beb6f42e08f26950b24f01a9d-2"&gt;&lt;/a&gt;&lt;span class="go"&gt;.&lt;/span&gt;
&lt;a name="rest_code_f0b7ea5beb6f42e08f26950b24f01a9d-3"&gt;&lt;/a&gt;&lt;span class="go"&gt;Elapsed time: 0.0&lt;/span&gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;C extension libraries &lt;strong&gt;may&lt;/strong&gt; or &lt;strong&gt;may not&lt;/strong&gt; be cooperative&lt;/p&gt;
&lt;p&gt;This is a tricky area. C extensions are so common in Python (much of
the standard library is implemented in C) that you may not even
realize you're using one. Commonly, C extensions are used for two
major purposes.&lt;/p&gt;
&lt;p&gt;The first is to accelerate CPU intensive code (for example,
&lt;a class="reference external" href="http://www.numpy.org"&gt;numpy&lt;/a&gt; to speed up linear algebra). These are probably no more or
less cooperative than a counterpart written in Python. (I'll explain
what I mean by that in a minute.)&lt;/p&gt;
&lt;p&gt;The second is to integrate with other existing native (C) libraries,
such as &lt;a class="reference external" href="https://pillow.readthedocs.io/en/5.1.x/"&gt;imaging libraries&lt;/a&gt; or &lt;a class="reference external" href="https://pypi.org/project/mysqlclient/"&gt;database clients&lt;/a&gt;. Some of these
are essentially CPU accelerators, so they basically fall into the
first category. But libraries that want to do IO, especially talking
to remote systems, particularly databases, are likely to want to
handle IO their own way and, without special hooks, are unlikely to
be cooperative. (The PostgreSQL driver and its Python C extension
&lt;a class="reference external" href="https://pypi.org/project/psycopg2/"&gt;psycopg2&lt;/a&gt; is an example of a database driver that provides the
necessary hooks to be made cooperative using &lt;a class="reference external" href="https://pypi.org/project/psycogreen/"&gt;psycogreen&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Back to that "no more or less cooperative than a counterpart written
in Python" statement. If the C extension calls from C &lt;em&gt;back&lt;/em&gt; into
Python code, and that Python code &lt;em&gt;itself&lt;/em&gt; is cooperative, than
thanks to the magic of greenlet being able to switch C call stacks,
the whole thing winds up cooperative. The call from C into Python
could either be explicit, or implicit (for example, sorting or
hashing values can call arbitrary Python code in an object's
&lt;tt class="docutils literal"&gt;__eq__&lt;/tt&gt; and &lt;tt class="docutils literal"&gt;__hash__&lt;/tt&gt; methods).&lt;/p&gt;
&lt;p&gt;A few good examples of CPU accelerator extensions that can operate
cooperatively are the &lt;a class="reference external" href="https://btrees.readthedocs.io/en/latest/"&gt;BTrees&lt;/a&gt; (sorted dictionaries) and
&lt;a class="reference external" href="https://zopeinterface.readthedocs.io/en/latest/"&gt;zope.interface&lt;/a&gt; (fast adapter and component registration and
lookup) libraries. These libraries need to sort or hash objects.
They are commonly used with objects that are &lt;a class="reference external" href="https://persistent.readthedocs.io/en/latest/"&gt;persistent&lt;/a&gt; and
stored in a &lt;a class="reference external" href="http://www.zodb.org/en/latest/"&gt;ZODB&lt;/a&gt; database, so sorting or hashing may need to
transparently fetch the object from the database. ZODB is
implemented in pure Python, so the original C operation winds up
being cooperative. &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id4" id="id2"&gt;[2]&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="synergism-matters"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id7"&gt;Synergism Matters&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The point of having one greenlet periodically print dots in those
examples while the other greenlet either cooperated or not was to show
why cooperative code matters. If one greenlet is not cooperating, no
other greenlet can make any forward progress (print dots). This might
stop other greenlets for a noticeably long time (like the CPU-bound
example of counting to 268,435,456, or it might be so quick it's
difficult for a human to catch (like resolving a hostname). In either
case, it can be a problem, especially if you're trying to scale a
program such as a server to handle more connections. Every yield lost
potentially decreases overall velocity.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="avoiding-blocking"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id8"&gt;Avoiding Blocking&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Above, we saw some examples of non-cooperative code. There were some
clear transformations to go through to make code cooperative:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p class="first"&gt;Switch from standard library API to gevent API.&lt;/p&gt;
&lt;p&gt;Change imports to import from gevent. E.g., &lt;tt class="docutils literal"&gt;import time&lt;/tt&gt;⟹&lt;tt class="docutils literal"&gt;from gevent import time&lt;/tt&gt; or &lt;tt class="docutils literal"&gt;import socket&lt;/tt&gt;⟹&lt;tt class="docutils literal"&gt;from gevent
import socket&lt;/tt&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;&lt;a class="reference external" href="http://www.gevent.org/intro.html#monkey-patching"&gt;Monkey patch&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;In code that's designed to be portable to a non-gevent system, you
can use the standard library API and have gevent automatically make
it cooperative by monkey patching. Many third-party libraries such
as &lt;a class="reference external" href="http://requests.readthedocs.io/en/master/"&gt;requests&lt;/a&gt; that
use only the Python standard API can become cooperative this way.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p class="first"&gt;Break up CPU intensive loops by explicitly yielding&lt;/p&gt;
&lt;p&gt;If you know you'll have a CPU intensive loop, like in the example,
you can periodically explicitly yield by calling &lt;tt class="docutils literal"&gt;gevent.sleep()&lt;/tt&gt;
in the loop. Beginning in gevent 1.3, this attempts to automatically
balance the amount of time CPU intensive tasks get while still
allowing other greenlets to perform IO.&lt;/p&gt;
&lt;p&gt;gevent also provides a &lt;a class="reference external" href="http://www.gevent.org/api/gevent.hub.html#gevent.hub.Hub.threadpool"&gt;threadpool&lt;/a&gt; you can use to offload CPU
intensive tasks.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="section" id="diagnosing-blocking"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id9"&gt;Diagnosing Blocking&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;You may have done all of that and are still not sure if you're
blocking the hub or not: maybe a colleague added a new C extension to
your project and you don't know its internals, or maybe there's an
inner loop somewhere that &lt;em&gt;might&lt;/em&gt; be CPU intensive and you're just not
sure.&lt;/p&gt;
&lt;p&gt;That's where &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-tracing.html"&gt;my next post&lt;/a&gt;
comes in. We'll discuss how to use gevent's monitoring facilities to
find greenlets that aren't cooperating.&lt;/p&gt;
&lt;!-- the blocking problem
how to know if blocking happens?
    tracing
        what it is
        how we use it
previous support
  github comment and links
implementation in gevent 1.3 --&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="id3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;Unless of course you &lt;a class="reference external" href="http://www.gevent.org/intro.html#monkey-patching"&gt;monkey patch&lt;/a&gt;, but that's another
post. Or at least another section of this post.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="id4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;At least when the system is monkey patched. But that's another
post. Or at least another section of this post.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>1.3</category><category>blocking</category><category>debugging</category><category>gevent</category><category>greenlet</category><category>hub</category><category>implementation</category><category>python</category><category>series</category><category>tracing</category><guid>https://seecoresoftware.com/blog/2018/06/gevent-blocking-greenlets.html</guid><pubDate>Tue, 26 Jun 2018 14:36:00 GMT</pubDate></item><item><title>gevent's hub</title><link>https://seecoresoftware.com/blog/2018/05/gevent-hub.html</link><dc:creator>Jason Madden</dc:creator><description>&lt;figure&gt;&lt;img src="https://c1.staticflickr.com/9/8665/16605819942_8fb5626c93_n.jpg"&gt;&lt;/figure&gt; &lt;div&gt;&lt;p&gt;The &lt;em&gt;hub&lt;/em&gt; is an important part of &lt;a class="reference external" href="https://seecoresoftware.com/blog/categories/gevent.html"&gt;gevent&lt;/a&gt;, but
aside from a few sentences in the high-level description &lt;a class="reference external" href="http://www.gevent.org/intro.html#event-loop"&gt;of the event
loop&lt;/a&gt;, and the &lt;a class="reference external" href="http://www.gevent.org/api/gevent.hub.html#module-gevent.hub"&gt;API
documentation&lt;/a&gt;, not a
lot has been written about the hub, its purpose, or how it does what
it does. This post aims to change that and answer questions like:&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;What is the hub?&lt;/li&gt;
&lt;li&gt;How does the hub run the event loop?&lt;/li&gt;
&lt;li&gt;How do greenlets do IO? That is, how do they let the event loop run,
while letting other greenlets run, and then pick right back up where
they blocked?&lt;/li&gt;
&lt;/ul&gt;
&lt;!-- TEASER_END --&gt;
&lt;div class="contents topic" id="contents"&gt;
&lt;p class="topic-title first"&gt;Contents&lt;/p&gt;
&lt;ul class="simple"&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#what-is-a-hub" id="id6"&gt;What is a hub?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#the-hub-is-a-greenlet" id="id7"&gt;The hub is a greenlet&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#the-hub-runs-the-event-loop" id="id8"&gt;The hub runs the event loop&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#getting-out-of-the-hub" id="id9"&gt;Getting out of the hub&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#wait-how-did-we-get-in-the-hub-in-the-first-place" id="id10"&gt;Wait, how did we get in the hub in the first place?&lt;/a&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#some-code" id="id11"&gt;Some code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a class="reference internal" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#a-few-loose-threads-get-it" id="id12"&gt;A few loose threads (get it?)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/div&gt;
&lt;div class="sidebar"&gt;
&lt;p class="first sidebar-title"&gt;Implementation Details Alert&lt;/p&gt;
&lt;p class="last"&gt;This post discusses implementation details of gevent 1.3. These
details generally should not be relied upon other than as
&lt;a class="reference external" href="http://gevent.org"&gt;documented&lt;/a&gt;, as they may change in the future.&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="what-is-a-hub"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id6"&gt;What is a hub?&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let's begin with what gevent's official documentation has to say about
the hub:&lt;/p&gt;
&lt;blockquote&gt;
When a function from gevent's API wants to
block, it obtains the &lt;tt class="docutils literal"&gt;gevent.hub.Hub&lt;/tt&gt; instance—a special
greenlet that runs the event loop—and switches to it (it is said
that the greenlet &lt;em&gt;yielded&lt;/em&gt; control to the Hub). If there's no
&lt;tt class="docutils literal"&gt;gevent.hub.Hub&lt;/tt&gt; instance yet, one is automatically created.&lt;/blockquote&gt;
&lt;p&gt;Not exactly self explanatory. The API documentation doesn't add much
to that.&lt;/p&gt;
&lt;p&gt;To try to understand what that means, let's break it into chunks.&lt;/p&gt;
&lt;div class="section" id="the-hub-is-a-greenlet"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id7"&gt;The hub is a greenlet&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The hub is a greenlet. &lt;a class="reference external" href="https://greenlet.readthedocs.io"&gt;greenlets&lt;/a&gt;
are one implementation of &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Green_threads"&gt;green threads&lt;/a&gt;. They're like a
"normal" operating system &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Thread_(computing)"&gt;thread&lt;/a&gt; in that each
greenlet has a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Call_stack"&gt;call stack&lt;/a&gt; (the C call stack of the
Python interpreter, plus the Python call stack) and represents one flow
of control through a program. They are different in that many
greenlets can be associated with a single operating system thread.
&lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#f1" id="id1"&gt;[1]&lt;/a&gt; This has the important effect that only one greenlet attached
to a thread is ever actually executing at any given time.&lt;/p&gt;
&lt;p&gt;Another way in which greenlets differ from typical operating system
threads is that they are &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Cooperative_multitasking"&gt;cooperatively scheduled&lt;/a&gt;, instead of
preemptively scheduled. That is, in order for another greenlet to be
able to run, the greenlet that is currently running must &lt;em&gt;choose&lt;/em&gt; to
give up control to it; this is called "switching". Moreover, it must
actually &lt;em&gt;choose&lt;/em&gt; the greenlet it wants to run next! &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#f2" id="id2"&gt;[2]&lt;/a&gt; To do so,
the current greenlet invokes the &lt;a class="reference external" href="https://greenlet.readthedocs.io/en/latest/#switching"&gt;switch method of the destination
greenlet&lt;/a&gt; When
a greenlet switch occurs, the call stack of the current greenlet is
saved away, the call stack of the destination greenlet is put in
place, and execution resumes where the destination greenlet left off.
&lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#f3" id="id3"&gt;[3]&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;
&lt;div class="section" id="the-hub-runs-the-event-loop"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id8"&gt;The hub runs the event loop&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Ok, so the hub is a greenlet—a cooperatively scheduled call stack or
thread of execution. That thread of execution is running the event
loop.&lt;/p&gt;
&lt;p&gt;Here's how gevent's documentation describes an event loop:&lt;/p&gt;
&lt;blockquote&gt;
Instead of blocking and waiting for socket operations to complete
(a technique known as polling), gevent arranges for the operating
system to deliver an event letting it know when, for example, data
has arrived to be read from the socket. Having done that, gevent
can move on to running another greenlet, perhaps one that itself
now has an event ready for it. This repeated process of
registering for events and reacting to them as they arrive is the
event loop.&lt;/blockquote&gt;
&lt;p&gt;In a visual sense, an event loop looks something like this (this is
a very high-level overview of libuv's event loop; don't worry too much
about the specific details or terminology &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#f4" id="id4"&gt;[4]&lt;/a&gt;):&lt;/p&gt;
&lt;img alt="https://docs.libuv.org/en/v1.x/_images/loop_iteration.png" src="https://docs.libuv.org/en/v1.x/_images/loop_iteration.png"&gt;
&lt;p&gt;"Running the event loop" means that the hub enters the top of that
loop and cycles through it &lt;em&gt;forever&lt;/em&gt;. The event loops that gevent
supports are implemented in C, so entering the top of the loop means
making a C function call &lt;a class="footnote-reference" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#f5" id="id5"&gt;[5]&lt;/a&gt;. That C function call is not expected to
return (because it's an infinite loop).&lt;/p&gt;
&lt;p&gt;"Registering for events" is done with objects called "watchers". Each
watcher object is looking for one particular event to happen.&lt;/p&gt;
&lt;p&gt;How, then, do other greenlets—the ones that actually read and write
socket data and otherwise make up your application—get to run? This
is where it gets interesting.&lt;/p&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="getting-out-of-the-hub"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id9"&gt;Getting out of the hub&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Look at that diagram again. See those blocks that start with "Call",
like "Call pending callbacks", or "Run", like "Run due timers"? Those
blocks are our loophole. That's when the event loop—while still
inside its single C function—gives gevent a chance to do something,
such as respond to an IO event or handle a scheduled event (timer).&lt;/p&gt;
&lt;p&gt;For each event that gevent might want to handle, it has created a
watcher and given it to the event loop. Each watcher is associated
with a &lt;a class="reference external" href="https://en.wikipedia.org/wiki/Callback_(computer_programming)"&gt;callback&lt;/a&gt;: a
function that the event loop will call when the desired event occurs.
(Giving a watcher to the event loop and associating it with a callback
is referred to as "starting" the watcher.) That function can do just
about anything. If that function happens to invoke
&lt;tt class="docutils literal"&gt;destination_greenlet.switch()&lt;/tt&gt;, then whoosh, just like that, the
hub and the event loop it's somewhere in the middle of running is
paused (its C call stack is saved away) and some other greenlet takes
off running.&lt;/p&gt;
&lt;div class="section" id="wait-how-did-we-get-in-the-hub-in-the-first-place"&gt;
&lt;h3&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id10"&gt;Wait, how did we get in the hub in the first place?&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We just saw how we can get &lt;em&gt;out&lt;/em&gt; of the hub when its busy running the
event loop: invoke a callback that calls &lt;tt class="docutils literal"&gt;greenlet.switch&lt;/tt&gt;. But how
did we start running the event loop in the hub in the first place?&lt;/p&gt;
&lt;p&gt;Let's look back to the quoted description of the hub:&lt;/p&gt;
&lt;blockquote&gt;
When a function from gevent's API wants to
block, it obtains the &lt;tt class="docutils literal"&gt;gevent.hub.Hub&lt;/tt&gt; instance—a special
greenlet that runs the event loop—and switches to it. If there's no
&lt;tt class="docutils literal"&gt;gevent.hub.Hub&lt;/tt&gt; instance yet, one is automatically created.&lt;/blockquote&gt;
&lt;p&gt;So any gevent blocking function (such as
&lt;tt class="docutils literal"&gt;gevent.socket.socket.read()&lt;/tt&gt;) is going to go switch into the hub.
If it's the first time the hub has been entered, the hub will start up
the event loop. Otherwise, if the hub was already running the event
loop, it will pick up where &lt;em&gt;it&lt;/em&gt; last left off and either respond to
the next event (IO, timer) by calling another callback, or let the
event loop wait for the next event.&lt;/p&gt;
&lt;p&gt;You can imagine it something like a game of ping pong, with the hub on
one side of the table, and all the other greenlets on the other side.
The hub hits the ball over the net to a greenlet who (eventually) hits
it back, and then the hub hits the ball to another greenlet, and so
on, &lt;em&gt;ad infinitum&lt;/em&gt;.&lt;/p&gt;
&lt;div class="section" id="some-code"&gt;
&lt;h4&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id11"&gt;Some code&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Now we know enough to look at some code and put the pieces together to
understand how it works under the covers.&lt;/p&gt;
&lt;p&gt;Most blocking functions end up implementing their half of the game
by calling &lt;a class="reference external" href="http://www.gevent.org/api/gevent.hub.html#gevent.hub.Hub.wait"&gt;Hub.wait()&lt;/a&gt;. For
example, here's basically what &lt;tt class="docutils literal"&gt;gevent.socket.socket.recv()&lt;/tt&gt; looks
like:&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-2"&gt;&lt;/a&gt;    &lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-3"&gt;&lt;/a&gt;        &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-4"&gt;&lt;/a&gt;            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;socket&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;recv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_sock&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-5"&gt;&lt;/a&gt;        &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-6"&gt;&lt;/a&gt;            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ex&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;!=&lt;/span&gt; &lt;span class="n"&gt;EWOULDBLOCK&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-7"&gt;&lt;/a&gt;                &lt;span class="k"&gt;raise&lt;/span&gt;
&lt;a name="rest_code_184cb0d99d47410f89cf203bc56dc251-8"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;hub&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;_read_event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;p&gt;That code is looping until we can actually read some data. If we try
to read and we fail because there's nothing to read, hand things over
to the event loop using &lt;tt class="docutils literal"&gt;hub.wait()&lt;/tt&gt;, which will switch into the
hub, make sure the event loop is watching this socket, and then carry
on.&lt;/p&gt;
&lt;p&gt;&lt;tt class="docutils literal"&gt;Hub.wait&lt;/tt&gt;, in turn, is implemented something like this (this is a
dramatically simplified example; the real thing is safer and uses a &lt;a class="reference external" href="http://www.gevent.org/api/gevent.hub.html#gevent.hub.Waiter"&gt;Waiter&lt;/a&gt;):&lt;/p&gt;
&lt;pre class="code python"&gt;&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-1"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;watcher&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="c1"&gt;# Hub.wait()&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-2"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# `watcher` is an event-loop object with a callback. When the&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-3"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# event it's waiting for happens, its callback gets called.&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-4"&gt;&lt;/a&gt;    &lt;span class="n"&gt;current_greenlet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;getcurrent&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-5"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# The callback for this event will switch back to&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-6"&gt;&lt;/a&gt;    &lt;span class="c1"&gt;# this current greenlet&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-7"&gt;&lt;/a&gt;    &lt;span class="n"&gt;watcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_greenlet&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Ask the event loop to watch this&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-8"&gt;&lt;/a&gt;    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-9"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# Start running the hub.&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-10"&gt;&lt;/a&gt;        &lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-11"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# Once we get here, it's because the watcher's callback&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-12"&gt;&lt;/a&gt;        &lt;span class="c1"&gt;# fired and this greenlet got switched into.&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-13"&gt;&lt;/a&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c1"&gt;# Let the blocking code continue, its event is ready&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-14"&gt;&lt;/a&gt;    &lt;span class="k"&gt;finally&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;a name="rest_code_4ce8b24e00c847f6ae993eed3f56f8c9-15"&gt;&lt;/a&gt;        &lt;span class="n"&gt;watcher&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;div class="section" id="a-few-loose-threads-get-it"&gt;
&lt;h2&gt;&lt;a class="toc-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id12"&gt;A few loose threads (get it?)&lt;/a&gt;&lt;/h2&gt;
&lt;dl class="docutils"&gt;
&lt;dt&gt;What happens when a greenlet finishes execution?&lt;/dt&gt;
&lt;dd&gt;Control is returned to its parent greenlet. gevent arranges for
the hub to be the parent of every greenlet it runs, so when a
greenlet dies, whether through successful completion or an
uncaught exception, the hub gets a chance to run the event loop.&lt;/dd&gt;
&lt;dt&gt;What happens when we want to start a new greenlet?&lt;/dt&gt;
&lt;dd&gt;&lt;a class="reference external" href="http://www.gevent.org/api/gevent.html#gevent.spawn"&gt;gevent.spawn()&lt;/a&gt; creates a
new greenlet and schedules it to start running in the next
iteration of the event loop. It does this by, you guessed it,
setting up an event watcher with a callback that's
&lt;tt class="docutils literal"&gt;new_greenlet.switch()&lt;/tt&gt;. That event watcher is a "prepare"
watcher, a type of event that becomes available at the start of
each iteration of the loop.&lt;/dd&gt;
&lt;dt&gt;How are gevent locks and timeouts implemented?&lt;/dt&gt;
&lt;dd&gt;You'll have to wait for &lt;a class="reference external" href="https://seecoresoftware.com/blog/2018/05/implementing-gevent-locks.html"&gt;another post&lt;/a&gt; for that! But if you want to
look into the implementation, this post should provide most of the
background you need.&lt;/dd&gt;
&lt;/dl&gt;
&lt;p class="rubric"&gt;Footnotes&lt;/p&gt;
&lt;table class="docutils footnote" frame="void" id="f1" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id1"&gt;[1]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;For simplicity's sake, we'll assume there's only one
operating system thread in the process.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f2" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id2"&gt;[2]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In most cases. When a greenlet finishes, control is
automatically handed back to its parent greenlet.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f3" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id3"&gt;[3]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;This is quite a fascinating technical accomplishment and
involves &lt;a class="reference external" href="https://github.com/python-greenlet/greenlet/tree/master/platform"&gt;assembly code for each supported platform&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f4" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id4"&gt;[4]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In particular, do not worry about the difference between
"run" and "call." Neither libuv nor libev &lt;em&gt;really&lt;/em&gt; run their
event loop in exactly this fashion. The important point is
that it is a loop that does the same things over and over, in the
same order each time.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;table class="docutils footnote" frame="void" id="f5" rules="none"&gt;
&lt;colgroup&gt;&lt;col class="label"&gt;&lt;col&gt;&lt;/colgroup&gt;
&lt;tbody valign="top"&gt;
&lt;tr&gt;&lt;td class="label"&gt;&lt;a class="fn-backref" href="https://seecoresoftware.com/blog/2018/05/gevent-hub.html#id5"&gt;[5]&lt;/a&gt;&lt;/td&gt;&lt;td&gt;In the case of libuv, that's &lt;a class="reference external" href="http://docs.libuv.org/en/v1.x/loop.html#c.uv_run"&gt;uv_run()&lt;/a&gt;.&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;/div&gt;&lt;/div&gt;</description><category>blocking</category><category>details</category><category>event loop</category><category>gevent</category><category>greenlet</category><category>hub</category><category>implementation</category><category>python</category><guid>https://seecoresoftware.com/blog/2018/05/gevent-hub.html</guid><pubDate>Fri, 18 May 2018 18:40:00 GMT</pubDate></item></channel></rss>