<?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 debugging)</title><link>https://seecoresoftware.com/</link><description></description><atom:link href="https://seecoresoftware.com/blog/categories/debugging.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:32 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></channel></rss>