tags/programmingNico Schotteliushttps://www.nico.schottelius.org//tags/programming/Nico Schotteliusikiwiki2016-02-25T13:34:32ZWhat went wrong, if accept(2) returns 0?https://www.nico.schottelius.org//blog/accept-returns-0/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<p>Today I continued working on the
<a href="https://www.nico.schottelius.org//blog/ceofhack-ui-support-1/">user interface support</a>
for <span class="createlink">ceofhack</span> and created an interesting
bug, which lead to other interesting "features":</p>
<p>The second time a user interface connected to
<a href="http://git.schottelius.org/?p=EOF/ceofhack;a=commit;h=a1a4b17fae050faf3f049b15ee20985c1684f46d">ceofhack-0.5.4-2-ga1a4b17</a>
it hung, and at the same time ceofhack got the input from cmd_ui on
stdin:</p>
<pre><code>Ignoring text (2100) (later versions send this to all peers in channel)
</code></pre>
<p>Digging a bit into the source, I found out that the accept() call in ui_read
returns 0:</p>
<pre><code>ui_handle.c:35:while((nsock = accept(fds[HP_READ], NULL, NULL)) != -1) {
</code></pre>
<p>I've never seen accept returning 0 before. As 0 is the value of
STDIN_FILENO, this explained the strange behaviour, because
<strong>helper_check_input()</strong> found the old stdin handler to be responsable
for that socket (via <strong>helper_find_by_fd()</strong>).</p>
<p>After digging a bit deeper, I found the reason for all the confusion:
<strong>helper_disable()</strong> closed all <strong>four</strong> helper file descriptors, of
which only <strong>two</strong> (HP_READ and HP_WRITE) were initialised by
<strong>helper_fdonly()</strong>. The other two contained the value 0, because
the code is compiled and linked with the gcc debugging option <strong>-g</strong>.</p>
<p>As only those two are used in ceofhack, <strong>helper_disable()</strong> was fixed to
close only those two.</p>
<p>Interesting, which ways debugging may take, isn't it?</p>
Solution proposal for the io select/poll problemhttps://www.nico.schottelius.org//blog/solution-proposal-for-the-io-select-poll-problem/2016-02-25T13:34:32Z2015-02-03T14:47:26Z
<h2>The situation</h2>
<p>If you have used select(2) or poll(2) more than once, you may have noticed
the regular pattern that comes up again and again:</p>
<ul>
<li>The main task of a program is to listen and react on multiple input/output connections.</li>
<li>For each i/o connection (file descriptor), you have</li>
<li>a function that opens the file descriptor (Let's call it <strong><em>conn_open</em></strong>.) and</li>
<li>another function to be executed if an event happens on the file descriptor
(<strong><em>conn_handle</em></strong>).</li>
<li>After <strong><em>conn_handle</em></strong> has been called, the number of connections may have changed:
<strong><em>conn_handle</em></strong> may</li>
<li>add (think of accept(2))</li>
<li>or remove (think of close(2)) connections.</li>
<li><strong><em>conn_open</em></strong> and <strong><em>conn_handle</em></strong> are closely related and belong to the
same "object" or code (<strong><em>conn_object</em></strong>).</li>
</ul>
<h2>The problem</h2>
<p>Each and every time this situation occurs, you have to (re-)write
code to handle that case. I have seen it in some applications I have
been writing, for instance <span class="createlink">ceofhack</span> or <a href="https://www.nico.schottelius.org//software/fui/">fui</a>.</p>
<h2>The solution proposal</h2>
<p>Write a solution to the problem
<a href="http://c2.com/xp/OnceAndOnlyOnce.html">once and only once</a>,
so you and me
<a href="http://en.wikipedia.org/wiki/Don%27t_repeat_yourself">don't reapeat ourselves</a>.</p>
<p>First of all, begin with the obvious part:</p>
<h3>What do we have?</h3>
<p>We have to bring together <strong>n times</strong></p>
<ul>
<li>open functions</li>
<li>file descriptors on which the following events can happen:</li>
<li>data is ready to be read from the file descriptor</li>
<li>data can be written to the file descriptor</li>
<li>an error occured on the file descriptor</li>
<li>handle functions</li>
</ul>
<p>Furthermore, we have</p>
<ul>
<li><strong>one</strong> main loop that listens for events</li>
</ul>
<h3>How to connect them properly?</h3>
<p>I assume that every <strong><em>conn_object</em></strong> knows best, which function to use for
opening and handling and which type of event is interesting.</p>
<p>Thus if we create a special function <strong><em>conn_open</em></strong>
for every <strong><em>conn_object</em></strong> that</p>
<ul>
<li>returns this information to the caller, the caller can create</li>
<li>a list containing the needed information and</li>
<li>loop over the event list and call the corresponding handler.</li>
</ul>
<p>The <strong><em>conn_open</em></strong> function may look like this:</p>
<pre><code>connection_entry = conn_open();
</code></pre>
<p>Where <strong><em>connection_list</em></strong> is a list of <strong><em>connection_entries</em></strong> like this:</p>
<pre><code>struct connection_list {
struct connection_entry *next;
} list;
enum type {
IN,
OUT,
ERR
};
struct connection_entry {
int fd;
void (*handler)(struct connection_list *);
int type;
};
</code></pre>
<p>The <strong><em>conn_open</em></strong> function can add or remove entries from the list.
Whether the list is an array, linked list, hash or whatever may be implementation specific.</p>
<h3>The main loop</h3>
<p>Before launching the main listener loop, we need to initialise
the list and run the <strong>conn_open</strong> function of every <strong>conn_object</strong>:</p>
<pre><code>struct connection_list list = init_connection_list();
connection_add(&list, a_conn_open());
connection_add(&list, b_conn_open());
</code></pre>
<p>Having done this, our main loop now looks pretty simple, doesn't it?</p>
<pre><code>while(1) {
/* create poll or select list from connection list, whatever you prefer */
poll_or_select_struct = connection_list_topoll_or_select(&connection_list);
changed_events = poll_or_select(&poll_or_select_struct);
for(event = changed_events; event != NULL; event = event->next) {
exec_handler(event->fd, &connection_list);
}
}
</code></pre>
<p>The function <strong><em>exec_handler</em></strong> would search for the registered handler
of the changed file descriptor, which could look like this:</p>
<pre><code>conn_handle(fd, &connection_list)
</code></pre>
<p>Firstly we pass the <strong>fd</strong>, because one handler may be registered for more
than one connection.
Secondly we add the <strong><em>connection_list</em></strong>, because the handler may add
more connections or remove itself.</p>
<p>Although I'm mainly speaking about <strong><em>poll</em></strong> and <strong><em>select</em></strong>, the idea also
applies to <a href="http://people.freebsd.org/~jlemon/papers/kqueue.pdf">kqueue</a>,
<a href="http://www.kernel.org/doc/man-pages/online/pages/man4/epoll.4.html">epoll</a>
and co.</p>
<h2>Further thoughts</h2>
<h3>Combine handle and open functions</h3>
<p>For a second I thought the functions <strong><em>conn_handle</em></strong> and
<strong><em>conn_open</em></strong> could be merged into a single function,
which would get a negative file descriptor, if called the first
time.
But as this means the <strong><em>conn_handle</em></strong> would need to check every
time it is called whether the to call the open part or not,
this is probably not a good idea.</p>
<h3>Creating an implementation</h3>
<p>I am currently actively (!) working on <a href="https://www.nico.schottelius.org//software/fui/">fui</a> and
think about creating an implementation in ruby and if it works
fine, another one in C for <span class="createlink">ceofhack</span>.</p>
<h3>Getting feedback</h3>
<p>I would appreciate any feedback regading this idea, whether the
problem is no problem at all, has been solved before or the idea
may be a solution for your problem, too: You can contact me for
this special post at
<strong>nico-io-poll-select-idea</strong> (near) <strong>schottelius.org</strong>.</p>