<div>Dear Marc and Gambit community,</div><div><br></div><div>Some while ago I emailed a suggestion for improvement to IO error handling for IO-intensive realworld applications (post at <a href="https://mercure.iro.umontreal.ca/pipermail/gambit-list/2013-January/006322.html" target="_blank">https://mercure.iro.umontreal.ca/pipermail/gambit-list/2013-January/006322.html</a> , "Proposal: Addition of a parameter option for signalling IO error as <font face="courier new, monospace">#f</font>/<font face="courier new, monospace">#!eof</font> result value instead of as exception").</div>


<div><br></div><div>The update provides a way for higher IO performance and higher specificity of IO error reporting through the removal of need for up to as many <font face="courier new, monospace">with-exception-catcher</font> calls as the CPU can perform IO procedure calls i.e. up to ~25,000,000 per second.</div>


<div><br></div><div>This makes great sense as <span style="font-family:'courier new',monospace">with-exception-catcher</span> calls everywhere really clutters code, and, it's a medium-expensive procedure taking approx 0.344 seconds per million calls (test file attached).</div>


<div><div><br></div><div>Secondarily this update makes using Gambit's IO more holistic through guaranteeing that IO calls will never lead to an exception except if it's such a serious error that manual administrator or programmer intervention would reasonably be required, as for instance in the case of heap overflow.</div>


</div><div><br></div><div>(As I got it, IO exceptions are thrown in corner cases of read/write-u8/char/substring/subu8vector even while generally <font face="courier new, monospace">#!eof</font> or 0 are returned.)</div>

<div>
<br></div><div>The previous post worked at a level of concept, and I now wish to propose a specific implementation specification as to further the progress on this topic:</div><div><br></div><div><br></div><div><b>Implementation suggestion</b></div>


<div><br></div><div>Three exports are needed to make it spin:</div><div><br></div><div><font face="courier new, monospace">     ##io-error-behavior</font></div><div><font face="courier new, monospace">     ##default-io-error-behavior</font></div>


<div><font face="courier new, monospace">     ##last-io-error</font></div><div><br></div><div><font face="courier new, monospace">##io-error-behavior</font> is a parameter object whose content determines Gambit's IO procedures' behavior in case of an IO error.</div>


<div><br></div><div>It is set by default to <font face="courier new, monospace">##default-io-error-behavior</font> which is a unique object (an uninterned symbol or something).</div><div><br></div><div>When an IO error happens,</div>


<div><br></div><div> * If <font face="courier new, monospace">##io-error-behavior</font> is set to <font face="courier new, monospace">##default-io-error-behavior</font>, then the error is handled as per Gambit's current behavior (exception or <font face="courier new, monospace">#!eof</font> or 0 depending on procedure).</div>


<div><br></div><div> * If <font face="courier new, monospace">##io-error-behavior</font> is set to another value, then that value is returned.</div><div><br></div><div><font face="courier new, monospace">##last-io-error</font> is a parameter object whose value is set on IO error to the exception value or return value that Gambit produces in its default behavior. This way the caller can inspect the error further if interested.</div>


<div><br></div><div><br></div><div><b>Example use</b></div><span style="font-family:'courier new',monospace">; => boolean, #t = success.</span><br><span style="font-family:'courier new',monospace">(define (handle-request port)</span><br>


<span style="font-family:'courier new',monospace">  (parameterize ((##io-error-behavior #f))</span><br><font face="courier new, monospace"><br></font><span style="font-family:'courier new',monospace">    ; Read input data up to end of input stream</span><br>


<span style="font-family:'courier new',monospace">    (let loop ()</span><br><span style="font-family:'courier new',monospace">      (let ((c (read-u8 port)))</span><br><span style="font-family:'courier new',monospace">        (if c</span><br>


<span style="font-family:'courier new',monospace">          (begin</span><br><span style="font-family:'courier new',monospace">            ; [Handle char]</span><br><span style="font-family:'courier new',monospace">            (loop))</span><br>


<font face="courier new, monospace"><br></font><span style="font-family:'courier new',monospace">          ; End of input stream reached.</span><div><font face="courier new, monospace">          ;<br></font><div>

<span style="font-family:'courier new',monospace">          ; Acknowledge reception</span><br>
<div><span style="font-family:'courier new',monospace">          (and (display "OK\n" port) (force-output port)))))))</span><span style="font-family:'courier new',monospace">    </span></div><div>


<br></div><div>The benefit here is the saving of a <font face="courier new, monospace">with-exception-catcher</font> calls for each <font face="courier new, monospace">read-u8</font> call, and depending on how you'd do it otherwise, the saving of one or two <font face="courier new, monospace">with-exception-catcher</font> calls for the <font face="courier new, monospace">display</font> and <font face="courier new, monospace">force-output</font> calls and saving of the possibility of doing <font face="courier new, monospace">force-output</font> on port when <font face="courier new, monospace">display</font> already reported it as closed.</div>


<div><br></div><div><br></div><div><br></div><div><b>Concluding notes re implementing this</b></div><div>I may very well get into implementing a Gambit commit for providing this functionality.</div><div><br></div><div>On my end, it will make ground for doing well-deserved cleanup to Sack and the to-be-released OpenSSL SSL channels module, both IO-intensive modules for production use, that currently use version-unsafe hacks to simulate the functionality proposed above.</div>


<div><br></div><div>I do not claim any copyright on suggestions or commits I send to Gambit but leave them in public domain.</div><div><br></div><div><br></div><div><b>Bigger picture, for reference</b></div><div>What motivated me to propose this as well as recently proposing</div>


<div><br></div><div> 1) A <font face="courier new, monospace">##os-device-get-fd</font> procedure (<a href="https://mercure.iro.umontreal.ca/pipermail/gambit-list/2013-March/006477.html" target="_blank">https://mercure.iro.umontreal.ca/pipermail/gambit-list/2013-March/006477.html</a>)</div>


<div><br></div><div> 2) A <font face="courier new, monospace">##device-port-wait-for-output!</font> procedure to complement <font face="courier new, monospace">##device-port-wait-for-input</font> (<a href="https://github.com/feeley/gambit/pull/6" target="_blank">https://github.com/feeley/gambit/pull/6</a>)</div>

<div><br>
</div><div> 3) Some way to implement low-level application-level ports (<a href="https://mercure.iro.umontreal.ca/pipermail/gambit-list/2013-January/006321.html" target="_blank">https://mercure.iro.umontreal.ca/pipermail/gambit-list/2013-January/006321.html</a>)</div>


<div><br></div><div>is that I've gotten to the conclusion that Gambit's builtin IO system is strikingly good as it is and has the potential to scale for any practical usecase I can imagine with only minor-ish tweaks.</div>

<div><br></div><div><br></div><div>The usecases to tweak for that I see relevant are</div>
<div><br></div><div> a) Realworld IO-intense use (which the proposal in this email seeks to improve in a quite fundamental way through a quite trivial tweak),</div><div><br></div><div> b) Deep C integration out of the box (which 1 and 2 above regard, really trivial tweaks those two too), and</div>


<div><br></div><div> c) Delivering low-level application-level ports e.g. SSL and GZip (3 above regard this, I didn't get to any specific proposal for this til now but from reviewing <font face="courier new, monospace">_io.scm</font> it cannot be a very big deal to do)</div>


<div><br></div><div> d) Later I'd guess: Hybrid character and byte access of ports (for ISO-8859-1 encoding, I believe it's already in the box, maybe some clarification needed and possibility for support for more encodings could be looked into)</div>


<div><br></div><div>And last</div><div><br></div><div><div> e) Making the IO system's UTF-8 coder suitable for any use at all, because it's not due to a freeze-crash bug!!</div><div>     (I believe it's at <a href="http://www.iro.umontreal.ca/~gambit/bugzilla/show_bug.cgi?id=127">http://www.iro.umontreal.ca/~gambit/bugzilla/show_bug.cgi?id=127</a> though Bugzilla is down right now)</div>

</div><div><br></div><div> f) Perhaps later: Higher IO procedure call performance through lower overhead on primarily the per-byte/character calls i.e. read-byte read-char, perhaps by making them automatically inlined and/or by making their mutex use configurable.</div>

<div>   <font face="courier new, monospace">read-u8</font> currently delivers ~350KB/sec and an increase to 25MB/sec should be realistic.</div>
<div>   For now though this can easily be worked around by application-level buffers and use of read/write-subu8vector, though that does come with drawbacks i.e. doesn't integrate well with hybrid byte-character use etc.</div>


<div><br></div><div><br></div><div>These are the only practical improvements that have come to my mind in total from several years of intense use of this functionality. Zooming out a bit, this is impressively little really.</div>


<div><br></div><div><br></div><div><br></div><div>I would kindly ask you for your feedback on the suggestion regarded by this email. :)</div><div><br></div><div><br></div><div>Best regards,</div><div>Mikael</div><div><br>


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