<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="en">
<head>
  <title>the all-thing</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <link rel="stylesheet" href="/static/style.css" type="text/css" />
  <link rel="alternate" type="application/rss+xml" title="the all-thing RSS feed" href="/index.rss" />
  <link rel="alternate" type="text/plain" title="the all-thing in plain text" href="/index.txt" />
  <script type="text/javascript" src="/static/mootools.js"></script>
  <script type="text/javascript" src="http://music.masanjin.net:9292/waxiest.js"></script>
</head>
<body>

<div id="main">
  <div id="header">
    <h1><a  href="/">the all-thing</a></h1>
    
  </div>
  <div id="sidebar">
    <h3>Recent comments</h3>

    <ul class="sidebar-list">
    
    <li><b><a  href="/whisper-0.5#58174069c046a78e55f02ef81da81e74">Dominique Julia</a></b>
        <i><a  href="/whisper-0.5">Whisper 0.5 released</a></i>
           one week ago
    </li>
    
    <li><b><a  href="/ruby-ncurses-and-thread-blocking#8fa2a0f392d7c0562d630e4936407c11">William Morgan</a></b>
        <i><a  href="/ruby-ncurses-and-thread-blocking">Ruby, Ncurses and blocked threads</a></i>
           three months ago
    </li>
    
    <li><b><a  href="/git-wtf-bf06ab7-released#533654a7a229569e27a6d0afd716c444">William Morgan</a></b>
        <i><a  href="/git-wtf-bf06ab7-released">git wtf bf06ab7 released</a></i>
           three months ago
    </li>
    
    <li><b><a  href="/git-wtf-bf06ab7-released#b7b7a905477674eb6985b34a964a0dca">Joao Nelas</a></b>
        <i><a  href="/git-wtf-bf06ab7-released">git wtf bf06ab7 released</a></i>
           three months ago
    </li>
    
    <li><b><a  href="/ruby-ncurses-and-thread-blocking#b00001114360ac152f87d4ac2a6e0c5b">Ollivier Robert</a></b>
        <i><a  href="/ruby-ncurses-and-thread-blocking">Ruby, Ncurses and blocked threads</a></i>
           three months ago
    </li>
    
    </ul>

    <h3>Authors</h3>
    <ul class="sidebar-list">
    
      <li><a class="author" href="/by/William+Morgan/">William&nbsp;Morgan</a>&nbsp;(65) </li>
    
    </ul>

    <h3>Tags</h3>
    <ul class="sidebar-list">
    
      <li><a class="label" href="/label/releases/">releases</a>&nbsp;(15) </li>
    
      <li><a class="label" href="/label/whisper/">whisper</a>&nbsp;(13) </li>
    
      <li><a class="label" href="/label/git/">git</a>&nbsp;(9) </li>
    
      <li><a class="label" href="/label/stats/">stats</a>&nbsp;(8) </li>
    
      <li><a class="label" href="/label/trollop/">trollop</a>&nbsp;(6) </li>
    
      <li><a class="label" href="/label/ruby/">ruby</a>&nbsp;(6) </li>
    
      <li><a class="label" href="/label/sup/">sup</a>&nbsp;(6) </li>
    
      <li><a class="label" href="/label/git-wtf/">git-wtf</a>&nbsp;(4) </li>
    
      <li><a class="label" href="/label/vm/">vm</a>&nbsp;(4) </li>
    
      <li><a class="label" href="/label/mathml/">mathml</a>&nbsp;(3) </li>
    
      <li><a class="label" href="/label/continuations/">continuations</a>&nbsp;(3) </li>
    
      <li><a class="label" href="/label/ditz/">ditz</a>&nbsp;(3) </li>
    
      <li><a class="label" href="/label/proglang/">proglang</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/optimization/">optimization</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/benchmarks/">benchmarks</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/rubinius/">rubinius</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/inlining/">inlining</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/ubuntu/">ubuntu</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/fibers/">fibers</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/ritex/">ritex</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/ruby1.9/">ruby1.9</a>&nbsp;(2) </li>
    
      <li><a class="label" href="/label/ncurses/">ncurses</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/javascript/">javascript</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/media/">media</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/vim/">vim</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/classification/">classification</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/massachusetts/">massachusetts</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/greasemonkey/">greasemonkey</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/wine/">wine</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/readme/">readme</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/ancient-greek/">ancient-greek</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/web/">web</a>&nbsp;(1) </li>
    
      <li><a class="label" href="/label/current+events/">current&nbsp;events</a>&nbsp;(1) </li>
    
    </ul>

    <h3>Other formats</h3>
    <ul class="sidebar-list">
    <li><a href="/index.rss"><img src="/static/rss-badge.png"/></a></li>
    <li><a href="/index.txt">plain text version</a></li>
    </ul>

    <h3 class="waxiest.author.original">Who is this man?</h3>
    <h3 class="waxiest.author.beautiful" style="display:none">I must find out more about this beautiful creature</h3>
    <h3 class="waxiest.author.beautifulbig" style="display:none">I MUST FIND OUT MORE ABOUT THIS BEAUTIFUL CREATURE</h3>
    <h3 class="waxiest.author.originalbig" style="display:none">WHO IS THIS MAN?</h3>

    <script type="text/javascript">
      var w = waxiest();
      w.optimizeHTMLSection("author", ["original", "beautiful", "beautifulbig", "originalbig"]);
    </script>

    <a href="http://masanjin.net" onClick="w.goalReached('greeting')">William Morgan</a>
  </div>
  <div id="content">
    <h2><a  href="/ruby-ncurses-and-thread-blocking">Ruby, Ncurses and blocked threads</a></h2>

<div class="byline">
  <a  href="/by/William+Morgan/">William Morgan</a>,
  <span title="7 months ago">August  6, 2009  6:40pm</span>
</div>

  <div class="labels"><span class='label'><a  href="/label/ruby/">ruby</a></span> <span class='label'><a  href="/label/ncurses/">ncurses</a></span> <span class='label'><a  href="/label/sup/">sup</a></span> </div>


<p class='first'>If you&#8217;re writing a multithreaded Ruby program that uses ncurses,
you might be curious why program stops running when you call
<code>Ncurses.getch</code>. <a href="http://sup.rubyforge.org">Sup</a> has been plagued
by this issue since 2005. Thankfully, I think I finally understand
it.</p>
<p>The problem is that there is a bug in the Ruby ncurses library
such that using blocking input will block <strong>all</strong> Ruby threads when
it waits for user input, instead of just the calling thread. So
<code>Ncurses.getch</code> will cause everything to grind to a halt.  This is
probably due to the library not releasing the <span class="caps">GVL</span> when blocking on
stdin.</p>
<p>This bug is present in the latest rubygems version of curses,
0.9.1. It has been fixed in the latest libncurses-ruby Debian
packages (1.1-3).</p>
<p>To see if you have a buggy, blocking version of the ruby ncurses
library, run this program:</p>
<p><code><pre class="ruby"><span class="ident">require</span> <span class="punct">'</span><span class="string">rubygems</span><span class="punct">'</span>
<span class="ident">require</span> <span class="punct">'</span><span class="string">ncurses</span><span class="punct">'</span>
<span class="ident">require</span> <span class="punct">'</span><span class="string">thread</span><span class="punct">'</span>

<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">initscr</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">noecho</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">cbreak</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">curs_set</span> <span class="number">0</span>

<span class="constant">Thread</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">do</span>
  <span class="ident">sleep</span> <span class="number">0.1</span>
  <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">stdscr</span><span class="punct">.</span><span class="ident">mvaddstr</span> <span class="number">0</span><span class="punct">,</span> <span class="number">0</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">library is GOOD.</span><span class="punct">&quot;</span>
<span class="keyword">end</span>

<span class="keyword">begin</span>
  <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">stdscr</span><span class="punct">.</span><span class="ident">mvaddstr</span> <span class="number">0</span><span class="punct">,</span> <span class="number">0</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">library is BAD.</span><span class="punct">&quot;</span>
  <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">getch</span>
<span class="keyword">ensure</span>
  <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">curs_set</span> <span class="number">1</span>
  <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">endwin</span>
  <span class="ident">puts</span> <span class="punct">&quot;</span><span class="string">bye</span><span class="punct">&quot;</span>
<span class="keyword">end</span></pre></code></p>
<p>(I purposely require <code>rubygems</code> in there to load the rubygems
ncurses library if it&#8217;s present; you can drop this if you don&#8217;t
use rubygems.)</p>
<p>There are two workarounds to this problem. First, you can simply
tell ncurses to use nonblocking input:</p>
<p><code><pre class="ruby"><span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">nodelay</span> <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">stdscr</span><span class="punct">,</span> <span class="constant">true</span></pre></code></p>
<p>But if you&#8217;re writing a multithreaded app, you probably aren&#8217;t
interested in nonblocking input, unless you want a nasty polling
loop.</p>
<p>The better choice is to add a call to <code>IO.select</code> before <code>getch</code>,
which will block the calling thread until there&#8217;s an actual
keypress, and then allow <code>getch</code> to pick it up:</p>
<p><code><pre class="ruby"><span class="keyword">if</span> <span class="constant">IO</span><span class="punct">.</span><span class="ident">select</span> <span class="punct">[</span><span class="global">$stdin</span><span class="punct">],</span> <span class="constant">nil</span><span class="punct">,</span> <span class="constant">nil</span><span class="punct">,</span> <span class="number">1</span>
  <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">getch</span>
<span class="keyword">end</span></pre></code></p>
<p><code>IO.select</code> requires a delay, so you&#8217;ll have to handle the
periodic nils that generates. But the background threads should no longer block.</p>
<p>There is one further complication, which is that you won&#8217;t be able
to receive the pseudo-keypresses Ncurses emits when the terminal
size changes, since they don&#8217;t show up on <code>$stdin</code> and thus the
<code>select</code> won&#8217;t pass. The solution is to install your own signal
handler:</p>
<p><code><pre class="ruby"><span class="ident">trap</span><span class="punct">(&quot;</span><span class="string">WINCH</span><span class="punct">&quot;)</span> <span class="punct">{</span> <span class="punct">...</span> <span class="ident">handle</span> <span class="ident">sigwinch</span>  <span class="punct">...</span> <span class="punct">}</span></pre></code></p>
<p>You will still see the resize events coming from <code>getch</code>, but only
once the user presses a key. You can drop them at this point.</p>
<p>That should be enough to make any multithreaded Ruby ncurses app
able function. Of course, once everyone&#8217;s using a fixed version fo
the ncurses libraries, you can do away with the <code>select</code> and set
<code>nodelay</code> to false.</p>
<p>(One last hint for the future: I&#8217;ve found it necessary to set it
to false before every call to <code>getch</code>; otherwise a ctrl-c will
magically change it back to nonblocking mode. Not sure why.)</p>

<h4>Discussion</h4>
<a name="comments"></a>

<ul class="comment-tree">

  <li>
  
  <a name="4097c627adc344b577b2b334fcab301c"></a>
  <div class="comment-body">
    <table class="comment-byline">
      <tr><td class="comment-author">Felipe Contreras</td>
          <td class="comment-date" title="four months ago"><a href="#4097c627adc344b577b2b334fcab301c">November  2, 2009  5:43pm</a></td>
    </tr></table>
    <div class='comment-quote' title="On Mon, Nov 2, 2009 at 6:10 PM, William Morgan &lt;comments@all-thing.net&gt; wrote:">
<p class='first'>If you&#8217;re writing a multithreaded Ruby program that uses ncurses,
you might be curious why program stops running when you call
<code>Ncurses.getch</code>. <a href="http://sup.rubyforge.org">Sup</a> has been plagued
by this issue since 2005. Thankfully, I think I finally understand
it.
The problem is that there is a bug in the Ruby ncurses library
such that using blocking input will block <strong>all</strong> Ruby threads when
it waits for user input, instead of just the calling thread. So
<code>Ncurses.getch</code> will cause everything to grind to a halt.   This is
probably due to the library not releasing the <span class="caps">GVL</span> when blocking on
stdin.
This bug is present in the latest rubygems version of curses,
0.9.1. It has been fixed in the latest libncurses-ruby Debian
packages (1.1-3).
To see if you have a buggy, blocking version of the ruby ncurses
library, run this program:
<code><pre class="ruby"><span class="ident">require</span> <span class="punct">'</span><span class="string">rubygems</span><span class="punct">'</span>
<span class="ident">require</span> <span class="punct">'</span><span class="string">ncurses</span><span class="punct">'</span>
<span class="ident">require</span> <span class="punct">'</span><span class="string">thread</span><span class="punct">'</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">initscr</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">noecho</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">cbreak</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">curs_set</span> <span class="number">0</span>
<span class="constant">Thread</span><span class="punct">.</span><span class="ident">new</span> <span class="keyword">do</span>
<span class="ident">sleep</span> <span class="number">0.1</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">stdscr</span><span class="punct">.</span><span class="ident">mvaddstr</span> <span class="number">0</span><span class="punct">,</span> <span class="number">0</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">library is GOOD.</span><span class="punct">&quot;</span>
<span class="keyword">end</span>
<span class="keyword">begin</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">stdscr</span><span class="punct">.</span><span class="ident">mvaddstr</span> <span class="number">0</span><span class="punct">,</span> <span class="number">0</span><span class="punct">,</span> <span class="punct">&quot;</span><span class="string">library is BAD.</span><span class="punct">&quot;</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">getch</span>
<span class="keyword">ensure</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">curs_set</span> <span class="number">1</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">endwin</span>
<span class="ident">puts</span> <span class="punct">&quot;</span><span class="string">bye</span><span class="punct">&quot;</span>
<span class="keyword">end</span></pre></code>
(I purposely require <code>rubygems</code> in there to load the rubygems
ncurses library if it&#8217;s present; you can drop this if you don&#8217;t
use rubygems.)
There are two workarounds to this problem. First, you can simply
tell ncurses to use nonblocking input:
<code><pre class="ruby"><span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">nodelay</span> <span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">stdscr</span><span class="punct">,</span> <span class="constant">true</span></pre></code>
But if you&#8217;re writing a multithreaded app, you probably aren&#8217;t
interested in nonblocking input, unless you want a nasty polling
loop.
The better choice is to add a call to <code>IO.select</code> before <code>getch</code>,
which will block the calling thread until there&#8217;s an actual
keypress, and then allow <code>getch</code> to pick it up:
<code><pre class="ruby"><span class="keyword">if</span> <span class="constant">IO</span><span class="punct">.</span><span class="ident">select</span> <span class="punct">[</span><span class="global">$stdin</span><span class="punct">],</span> <span class="constant">nil</span><span class="punct">,</span> <span class="constant">nil</span><span class="punct">,</span> <span class="number">1</span>
<span class="constant">Ncurses</span><span class="punct">.</span><span class="ident">getch</span>
<span class="keyword">end</span></pre></code>
<code>IO.select</code> requires a delay, so you&#8217;ll have to handle the
periodic nils that generates. But the background threads should no longer block.
There is one further complication, which is that you won&#8217;t be able
to receive the pseudo-keypresses Ncurses emits when the terminal
size changes, since they don&#8217;t show up on <code>$stdin</code> and thus the
<code>select</code> won&#8217;t pass. The solution is to install your own signal
handler:
<code><pre class="ruby"><span class="ident">trap</span><span class="punct">(&quot;</span><span class="string">WINCH</span><span class="punct">&quot;)</span> <span class="punct">{</span> <span class="punct">...</span> <span class="ident">handle</span> <span class="ident">sigwinch</span>   <span class="punct">...</span> <span class="punct">}</span></pre></code>
You will still see the resize events coming from <code>getch</code>, but only
once the user presses a key. You can drop them at this point.
That should be enough to make any multithreaded Ruby ncurses app
able function. Of course, once everyone&#8217;s using a fixed version fo
the ncurses libraries, you can do away with the <code>select</code> and set
<code>nodelay</code> to false.
(One last hint for the future: I&#8217;ve found it necessary to set it
to false before every call to <code>getch</code>; otherwise a ctrl-c will
magically change it back to nonblocking mode. Not sure why.)</p>
</div><p>Shouldn&#8217;t we be pushing for the ncurses gem to be updated? Anyway, I
tried ncurses-1.2.4 and indeed your test now works correctly, but I
still feel sup very slow, specially when I launch it and it&#8217;s loading
the threads.</p>
    <div class="reply-to-outer">
      <div class="reply-to-header"><a href="#" class="reply-to-link">reply</a></div>
      <div class="reply-to-box" id="reply-to-ruby-ncurses-and-thread-blocking-4097c627adc344b577b2b334fcab301c" >
        <span class="comment-instructions">To reply, enter your email address. A copy of the comment will be sent to you via email.</span>
        <form id='comment-form-ruby-ncurses-and-thread-blocking-4097c627adc344b577b2b334fcab301c' action='/comment/ruby-ncurses-and-thread-blocking' method='post' class='comment-form'>
          <input type='text' name='email' id='textfield-email'/>
          <input type='submit' value='email me' id='submit-email me'/>
        <input type='hidden' name='comment-id' value='4097c627adc344b577b2b334fcab301c'/>
  <span class="form-result" id="form-result-ruby-ncurses-and-thread-blocking-4097c627adc344b577b2b334fcab301c"><!-- spanna --></span>
</form>

      </div>
    </div>
  </div>

  </li><li>
  
  <a name="b00001114360ac152f87d4ac2a6e0c5b"></a>
  <div class="comment-body">
    <table class="comment-byline">
      <tr><td class="comment-author">Ollivier Robert</td>
          <td class="comment-date" title="three months ago"><a href="#b00001114360ac152f87d4ac2a6e0c5b">November 20, 2009  2:04pm</a></td>
    </tr></table>
    <div class='comment-quote'>
<p>That should be enough to make any multithreaded Ruby ncurses app
able function. Of course, once everyone&#8217;s using a fixed version fo
the ncurses libraries, you can do away with the <code>select</code> and set
<code>nodelay</code> to false.</p>
</div><p>Is support for things like ff-ncurses planned?  Either the ncurses gem
has been removed (moved to gemcutter?) or there is a problem on my
side but I can&#8217;t seem to find it (as a gem).</p>
    <div class="reply-to-outer">
      <div class="reply-to-header"><a href="#" class="reply-to-link">reply</a></div>
      <div class="reply-to-box" id="reply-to-ruby-ncurses-and-thread-blocking-b00001114360ac152f87d4ac2a6e0c5b" >
        <span class="comment-instructions">To reply, enter your email address. A copy of the comment will be sent to you via email.</span>
        <form id='comment-form-ruby-ncurses-and-thread-blocking-b00001114360ac152f87d4ac2a6e0c5b' action='/comment/ruby-ncurses-and-thread-blocking' method='post' class='comment-form'>
          <input type='text' name='email' id='textfield-email'/>
          <input type='submit' value='email me' id='submit-email me'/>
        <input type='hidden' name='comment-id' value='b00001114360ac152f87d4ac2a6e0c5b'/>
  <span class="form-result" id="form-result-ruby-ncurses-and-thread-blocking-b00001114360ac152f87d4ac2a6e0c5b"><!-- spanna --></span>
</form>

      </div>
    </div>
  </div>

  <ul><li>
  
  <a name="8fa2a0f392d7c0562d630e4936407c11"></a>
  <div class="comment-body">
    <table class="comment-byline">
      <tr><td class="comment-author"><a href="http://masanjin.net/">William Morgan</a></td>
          <td class="comment-date" title="three months ago"><a href="#8fa2a0f392d7c0562d630e4936407c11">December  7, 2009  1:21pm</a></td>
    </tr></table>
    <div class='comment-quote' title="Reformatted excerpts from Ollivier Robert (on the all-thing)'s message of 2009-12-07:">
<p>Is support for things like ff-ncurses planned?</p>
</div><p>What is ff-ncurses?</p><div class='comment-quote'>
<p>Either the ncurses gem has been removed (moved to gemcutter?) or there
is a problem on my side but I can&#8217;t seem to find it (as a gem).</p>
</div><p>Works for me&#8482;.</p>
    <div class="reply-to-outer">
      <div class="reply-to-header"><a href="#" class="reply-to-link">reply</a></div>
      <div class="reply-to-box" id="reply-to-ruby-ncurses-and-thread-blocking-8fa2a0f392d7c0562d630e4936407c11" >
        <span class="comment-instructions">To reply, enter your email address. A copy of the comment will be sent to you via email.</span>
        <form id='comment-form-ruby-ncurses-and-thread-blocking-8fa2a0f392d7c0562d630e4936407c11' action='/comment/ruby-ncurses-and-thread-blocking' method='post' class='comment-form'>
          <input type='text' name='email' id='textfield-email'/>
          <input type='submit' value='email me' id='submit-email me'/>
        <input type='hidden' name='comment-id' value='8fa2a0f392d7c0562d630e4936407c11'/>
  <span class="form-result" id="form-result-ruby-ncurses-and-thread-blocking-8fa2a0f392d7c0562d630e4936407c11"><!-- spanna --></span>
</form>

      </div>
    </div>
  </div>

  </li></ul></li>
  
</ul>

<p class="comment-instructions">To leave a new comment, enter your email
address. A copy of the article will be sent to you via email.</p>
<form id='comment-form-ruby-ncurses-and-thread-blocking' action='/comment/ruby-ncurses-and-thread-blocking' method='post' class='comment-form'>
  <input type='text' name='email' id='textfield-email'/>
  <input type='submit' value='email me' id='submit-email me'/>
  <span class="form-result" id="form-result-ruby-ncurses-and-thread-blocking"><!-- spanna --></span>
</form>


<script type="text/javascript">
/* <![CDATA[ */
$$('.comment-form').addEvent('submit', function(e) {
  e.stop();
  var el = this.getElement('.form-result');
  var result = el.empty().addClass('ajax-loading');
  this.set('send', {
    method: 'post',
    onComplete: function(response) { 
      result.removeClass('ajax-loading');
      result.set('html', response);
    },
  });
  this.send();
});
/* ]]> */
</script>


<script type="text/javascript">
/* <![CDATA[ */
$$('.reply-to-link').each(function(link, i) {
  var box = link.getParent().getParent().getElement('.reply-to-box');
  var oldHeight = box.getStyle("height");
  box.setStyle("height", 0);
  box.setStyle("opacity", 0);
  link.addEvent('click', function(e) {
    e.stop();
    box.setStyle("opacity", 1);
    box.setStyle("height", oldHeight);
    box.getElement("input").focus();
  });
});
/* ]]> */
</script>

  </div>

  <div id="footer" style="margin: 0px;">
    Served up by <a href="http://masanjin.net/whisper/">Whisper</a>. Yes!
  </div>
</div>
</body>
</html>
