<div dir="ltr">Along these lines, I&#39;ve been trying to figure out a reliable way to tell if a call to dispatch is necessary. Suppose I have a handler that can be called alone or could be nested inside another handler. My handler is going to do something async so I need to make sure it&#39;s been dispatched. Since it&#39;s about to do something very fast and async there&#39;s no need to move that work to another thread, I just want to run it locally so **if** it needs dispatching I&#39;d call &quot;dispatch(SameThreadExecutor.INSTANCE, ::doSomeStuff)&quot;. The handler wrapping this might do the same (dispatch, but right back to the IO thread). Thus, when my handler gets called it **might** have already been dispatched but it&#39;s not sure.<div><br></div><div>Calling &quot;Exchange.isInIoThread()&quot; isn&#39;t reliable as the wrapping handler may have dispatched via SameThreadExecutor so being on the IO thread does not mean a dispatch in needed. OTOH, as I understand it (not sure, please correct me if I&#39;m wrong) &quot;isDispatched&quot; isn&#39;t generally reliable as that gets unset as soon as you&#39;ve left the IO thread so if the wrapper dispatched using any executor besides SameThreadExecutor my handler doesn&#39;t need to dispatch but isDispatched will be false.</div><div><br></div><div>So, I think what is reliable is that you need to dispatch if and only if &quot;isInIoThread() &amp;&amp; !isDispatched()&quot;. Is that correct?</div></div><br><div class="gmail_quote"><div dir="ltr">On Thu, Jan 26, 2017 at 4:10 PM Stuart Douglas &lt;<a href="mailto:sdouglas@redhat.com">sdouglas@redhat.com</a>&gt; wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex">In general there is never any real need to dispatch back to an IO<br class="gmail_msg">
thread, although if you perform any non blocking IO ownership of the<br class="gmail_msg">
exchange can end up back in the IO thread when the operation is<br class="gmail_msg">
complete.<br class="gmail_msg">
<br class="gmail_msg">
Stuart<br class="gmail_msg">
<br class="gmail_msg">
On Thu, Jan 26, 2017 at 12:53 AM, Bill O&#39;Neil &lt;<a href="mailto:bill@dartalley.com" class="gmail_msg" target="_blank">bill@dartalley.com</a>&gt; wrote:<br class="gmail_msg">
&gt; Hey Stuart, your last response reminded me of of a question I had hopefully<br class="gmail_msg">
&gt; its not unrelated. I was curious how and when you should dispatch back to<br class="gmail_msg">
&gt; the IO thread. For example lets say we have a simple handler that does a SQL<br class="gmail_msg">
&gt; query that is composed with the AccessLogHandler.<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt; AccessLogHandler -&gt; BlockingHandler -&gt; CustomSqlHandler In this approach I<br class="gmail_msg">
&gt; believe the final access log statement will be logged from the worker thread<br class="gmail_msg">
&gt; not the initial IO thread.<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt; Is it possible / recommended to kick back to an IO thread once blocking is<br class="gmail_msg">
&gt; complete?<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt; AccessLogHandler -&gt; BlockingHandler -&gt; CustomSqlHandler -&gt;<br class="gmail_msg">
&gt; DispatchBackToIOThread? This way the final logging of AccessLogHandler is<br class="gmail_msg">
&gt; handled in the IO thread.<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt; I&#39;m not very familiar with the best ways to mix the nonblocking and blocking<br class="gmail_msg">
&gt; handlers.<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt; On Wed, Jan 25, 2017 at 12:24 AM, Stuart Douglas &lt;<a href="mailto:sdouglas@redhat.com" class="gmail_msg" target="_blank">sdouglas@redhat.com</a>&gt;<br class="gmail_msg">
&gt; wrote:<br class="gmail_msg">
&gt;&gt;<br class="gmail_msg">
&gt;&gt; The handler is executed<br class="gmail_msg">
&gt;&gt; io.undertow.server.Connectors#executeRootHandler, which means that<br class="gmail_msg">
&gt;&gt; when the call stack returns the exchange will be ended.<br class="gmail_msg">
&gt;&gt;<br class="gmail_msg">
&gt;&gt; Conceptually this is similar to how dispatching to a thread pool<br class="gmail_msg">
&gt;&gt; works, when you call dispatch(HttpHandler) the exchange will be ended<br class="gmail_msg">
&gt;&gt; when the call stack returns, even though you are no longer on the IO<br class="gmail_msg">
&gt;&gt; thread (unless you call dispatch again).<br class="gmail_msg">
&gt;&gt;<br class="gmail_msg">
&gt;&gt; Stuart<br class="gmail_msg">
&gt;&gt;<br class="gmail_msg">
&gt;&gt;<br class="gmail_msg">
&gt;&gt; On Wed, Jan 25, 2017 at 3:57 PM, Oliver Dain &lt;<a href="mailto:oliver@analyticspot.com" class="gmail_msg" target="_blank">oliver@analyticspot.com</a>&gt;<br class="gmail_msg">
&gt;&gt; wrote:<br class="gmail_msg">
&gt;&gt; &gt; I have some code for handling file uploads. It uses FormDataProcessor<br class="gmail_msg">
&gt;&gt; &gt; and<br class="gmail_msg">
&gt;&gt; &gt; tries to do everything asynchronously. I call &quot;FormDataParser.parse&quot;<br class="gmail_msg">
&gt;&gt; &gt; passing<br class="gmail_msg">
&gt;&gt; &gt; in another handler. I&#39;ll call that handler the OnFormDataAvailable. The<br class="gmail_msg">
&gt;&gt; &gt; OnFormDataAvailable handler checks to see if it&#39;s on the IO thread. If<br class="gmail_msg">
&gt;&gt; &gt; it<br class="gmail_msg">
&gt;&gt; &gt; is, it calls dispatch. Either way, once we&#39;re sure we&#39;re dispatched that<br class="gmail_msg">
&gt;&gt; &gt; handler calls yet another handler.<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt; What I&#39;ve seen is that my OnFormDataAvailable handler (the one called by<br class="gmail_msg">
&gt;&gt; &gt; parse()) is not on the IO thread so it doesn&#39;t need to call dispatch.<br class="gmail_msg">
&gt;&gt; &gt; And<br class="gmail_msg">
&gt;&gt; &gt; yet, the exchange gets ended before the handler it calls is even close<br class="gmail_msg">
&gt;&gt; &gt; to<br class="gmail_msg">
&gt;&gt; &gt; complete.<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt; I&#39;ve found a fix, but I don&#39;t understand why it&#39;s necessary.<br class="gmail_msg">
&gt;&gt; &gt; Specifically,<br class="gmail_msg">
&gt;&gt; &gt; if OnFormDataAvailable is not on the IO thread when its invoked it calls<br class="gmail_msg">
&gt;&gt; &gt; the<br class="gmail_msg">
&gt;&gt; &gt; 0-argument version of &quot;HttpServerExchange.dispatch()&quot; (which you&#39;ve told<br class="gmail_msg">
&gt;&gt; &gt; me<br class="gmail_msg">
&gt;&gt; &gt; in another conversation shouldn&#39;t ever be necessary). If I do that,<br class="gmail_msg">
&gt;&gt; &gt; everything is fine.<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt; For completeness, here&#39;s the complete code:<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt; public class FormDataParsingHandler {<br class="gmail_msg">
&gt;&gt; &gt;   private static final Logger log =<br class="gmail_msg">
&gt;&gt; &gt; LoggerFactory.getLogger(FormDataParsingHandler.class);<br class="gmail_msg">
&gt;&gt; &gt;   private static final FormParserFactory formParserFactory =<br class="gmail_msg">
&gt;&gt; &gt; FormParserFactory.builder().build();<br class="gmail_msg">
&gt;&gt; &gt;   public static final AttachmentKey&lt;FormData&gt; FORM_DATA_ATTACHMENT_KEY =<br class="gmail_msg">
&gt;&gt; &gt; AttachmentKey.create(FormData.class);<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;   /**<br class="gmail_msg">
&gt;&gt; &gt;    * The only public method - this is what gets exposed as the<br class="gmail_msg">
&gt;&gt; &gt; HttpHandler.<br class="gmail_msg">
&gt;&gt; &gt;    */<br class="gmail_msg">
&gt;&gt; &gt;   public CompletableFuture&lt;FormData&gt; parseForm(HttpServerExchange<br class="gmail_msg">
&gt;&gt; &gt; exchange)<br class="gmail_msg">
&gt;&gt; &gt; {<br class="gmail_msg">
&gt;&gt; &gt;     <a href="http://log.info" rel="noreferrer" class="gmail_msg" target="_blank">log.info</a>(&quot;audio file upload request received.&quot;);<br class="gmail_msg">
&gt;&gt; &gt;     FormDataParser parser = formParserFactory.createParser(exchange);<br class="gmail_msg">
&gt;&gt; &gt;     if (parser == null) {<br class="gmail_msg">
&gt;&gt; &gt;       log.warn(&quot;No parser found that can handle this content type.<br class="gmail_msg">
&gt;&gt; &gt; Headers<br class="gmail_msg">
&gt;&gt; &gt; were: {}&quot;, exchange.getRequestHeaders());<br class="gmail_msg">
&gt;&gt; &gt;       throw new UserVisibleException(&quot;No parser for the given content<br class="gmail_msg">
&gt;&gt; &gt; type.&quot;, ResponseCodes.BAD_REQUEST);<br class="gmail_msg">
&gt;&gt; &gt;     }<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;     CompletableFuture&lt;FormData&gt; toComplete = new CompletableFuture&lt;&gt;();<br class="gmail_msg">
&gt;&gt; &gt;     try {<br class="gmail_msg">
&gt;&gt; &gt;       parser.parse(new OnFormDataAvailable(toComplete));<br class="gmail_msg">
&gt;&gt; &gt;     } catch (Exception e) {<br class="gmail_msg">
&gt;&gt; &gt;       log.error(&quot;Error parsing form data:&quot;, e);<br class="gmail_msg">
&gt;&gt; &gt;       throw wrapAsUnchecked(e);<br class="gmail_msg">
&gt;&gt; &gt;     }<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;     exchange.addExchangeCompleteListener((ex, nextListener) -&gt; {<br class="gmail_msg">
&gt;&gt; &gt;       // Must close the parser so it can free any temporary files that<br class="gmail_msg">
&gt;&gt; &gt; were<br class="gmail_msg">
&gt;&gt; &gt; created.<br class="gmail_msg">
&gt;&gt; &gt;       try {<br class="gmail_msg">
&gt;&gt; &gt;         parser.close();<br class="gmail_msg">
&gt;&gt; &gt;       } catch (IOException e) {<br class="gmail_msg">
&gt;&gt; &gt;         log.error(&quot;Error closing the FormDataParser. Request was handled<br class="gmail_msg">
&gt;&gt; &gt; successfully but temporary files may not &quot;<br class="gmail_msg">
&gt;&gt; &gt;             + &quot;have been cleaned up.&quot;, e);<br class="gmail_msg">
&gt;&gt; &gt;       }<br class="gmail_msg">
&gt;&gt; &gt;       nextListener.proceed();<br class="gmail_msg">
&gt;&gt; &gt;     });<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;     return toComplete;<br class="gmail_msg">
&gt;&gt; &gt;   }<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;   // The FormDataParser calls an HttpHandler when it&#39;s complete so we<br class="gmail_msg">
&gt;&gt; &gt; add a<br class="gmail_msg">
&gt;&gt; &gt; silly handler here that does nothing but<br class="gmail_msg">
&gt;&gt; &gt;   // complete this method&#39;s future when the form data is available.<br class="gmail_msg">
&gt;&gt; &gt;   private static class OnFormDataAvailable implements HttpHandler {<br class="gmail_msg">
&gt;&gt; &gt;     private final CompletableFuture&lt;FormData&gt; toComplete;<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;     private OnFormDataAvailable(CompletableFuture&lt;FormData&gt; toComplete)<br class="gmail_msg">
&gt;&gt; &gt; {<br class="gmail_msg">
&gt;&gt; &gt;       this.toComplete = toComplete;<br class="gmail_msg">
&gt;&gt; &gt;     }<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;     @Override<br class="gmail_msg">
&gt;&gt; &gt;     public void handleRequest(HttpServerExchange exchange) throws<br class="gmail_msg">
&gt;&gt; &gt; Exception<br class="gmail_msg">
&gt;&gt; &gt; {<br class="gmail_msg">
&gt;&gt; &gt;       // Before we complete the future we have to re-dispatch or we&#39;ll<br class="gmail_msg">
&gt;&gt; &gt; fall<br class="gmail_msg">
&gt;&gt; &gt; off the end of this method and Undertow<br class="gmail_msg">
&gt;&gt; &gt;       // will complete the exchange on our behalf.<br class="gmail_msg">
&gt;&gt; &gt;       FormData data = exchange.getAttachment(FormDataParser.FORM_DATA);<br class="gmail_msg">
&gt;&gt; &gt;       if (exchange.isInIoThread()) {<br class="gmail_msg">
&gt;&gt; &gt;         log.debug(&quot;Was on the IO thread. Re-dispatching.&quot;);<br class="gmail_msg">
&gt;&gt; &gt;         exchange.dispatch(SameThreadExecutor.INSTANCE, () -&gt;<br class="gmail_msg">
&gt;&gt; &gt; afterDistpach(data));<br class="gmail_msg">
&gt;&gt; &gt;       } else {<br class="gmail_msg">
&gt;&gt; &gt;         // THIS THE MYSTERY LINE. WHY IS THIS NEEDED?<br class="gmail_msg">
&gt;&gt; &gt;         exchange.dispatch();<br class="gmail_msg">
&gt;&gt; &gt;         afterDistpach(data);<br class="gmail_msg">
&gt;&gt; &gt;       }<br class="gmail_msg">
&gt;&gt; &gt;     }<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt;     private void afterDistpach(FormData data) {<br class="gmail_msg">
&gt;&gt; &gt;       if (data == null) {<br class="gmail_msg">
&gt;&gt; &gt;         toComplete.completeExceptionally(<br class="gmail_msg">
&gt;&gt; &gt;             new UserVisibleException(&quot;Parsing of data failed.&quot;,<br class="gmail_msg">
&gt;&gt; &gt; ResponseCodes.BAD_REQUEST));<br class="gmail_msg">
&gt;&gt; &gt;       } else {<br class="gmail_msg">
&gt;&gt; &gt;         toComplete.complete(data);<br class="gmail_msg">
&gt;&gt; &gt;       }<br class="gmail_msg">
&gt;&gt; &gt;     }<br class="gmail_msg">
&gt;&gt; &gt;   }<br class="gmail_msg">
&gt;&gt; &gt; }<br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt; --<br class="gmail_msg">
&gt;&gt; &gt; CTO, Analytic Spot<br class="gmail_msg">
&gt;&gt; &gt; 44 West Broadway #222<br class="gmail_msg">
&gt;&gt; &gt; Eugene, OR 97401<br class="gmail_msg">
&gt;&gt; &gt; <a href="http://analyticspot.com" rel="noreferrer" class="gmail_msg" target="_blank">analyticspot.com</a> • <a href="tel:(425)%20296-6556" value="+14252966556" class="gmail_msg" target="_blank">425-296-6556</a><br class="gmail_msg">
&gt;&gt; &gt; <a href="http://www.linkedin.com/in/oliverdain" rel="noreferrer" class="gmail_msg" target="_blank">www.linkedin.com/in/oliverdain</a><br class="gmail_msg">
&gt;&gt; &gt;<br class="gmail_msg">
&gt;&gt; &gt; _______________________________________________<br class="gmail_msg">
&gt;&gt; &gt; undertow-dev mailing list<br class="gmail_msg">
&gt;&gt; &gt; <a href="mailto:undertow-dev@lists.jboss.org" class="gmail_msg" target="_blank">undertow-dev@lists.jboss.org</a><br class="gmail_msg">
&gt;&gt; &gt; <a href="https://lists.jboss.org/mailman/listinfo/undertow-dev" rel="noreferrer" class="gmail_msg" target="_blank">https://lists.jboss.org/mailman/listinfo/undertow-dev</a><br class="gmail_msg">
&gt;&gt;<br class="gmail_msg">
&gt;&gt; _______________________________________________<br class="gmail_msg">
&gt;&gt; undertow-dev mailing list<br class="gmail_msg">
&gt;&gt; <a href="mailto:undertow-dev@lists.jboss.org" class="gmail_msg" target="_blank">undertow-dev@lists.jboss.org</a><br class="gmail_msg">
&gt;&gt; <a href="https://lists.jboss.org/mailman/listinfo/undertow-dev" rel="noreferrer" class="gmail_msg" target="_blank">https://lists.jboss.org/mailman/listinfo/undertow-dev</a><br class="gmail_msg">
&gt;<br class="gmail_msg">
&gt;<br class="gmail_msg">
</blockquote></div><div dir="ltr">-- <br></div><div data-smartmail="gmail_signature"><div dir="ltr"><div style="font-size:small"><div style="font-size:13px;line-height:19.5px">CTO, Analytic Spot</div><div style="font-size:13px;line-height:19.5px">44 West Broadway #222</div><div style="font-size:13px;line-height:19.5px">Eugene, OR 97401<br></div><div style="font-size:13px;line-height:19.5px"><a href="http://analyticspot.com/" style="z-index: 0;">analyticspot.com</a> <span style="color:rgb(127,127,127);font-family:&#39;helvetica neue&#39;;font-size:11px;line-height:normal">• </span>425-296-6556</div></div><div style="font-size:small"><span style="line-height:19.5px"><a href="http://www.linkedin.com/in/oliverdain" style="z-index: 0;">www.linkedin.com/in/oliverdain</a></span></div></div></div>