On Thu, May 15, 2025 at 7:34 PM Christopher Schultz
<ch...@christopherschultz.net> wrote:
>
> Rémy,
>
> On 5/14/25 6:31 PM, Rémy Maucherat wrote:
> > On Wed, May 14, 2025 at 8:52 PM Christopher Schultz
> > <ch...@christopherschultz.net> wrote:
> >>
> >> Mark,
> >>
> >> On 5/13/25 11:20 AM, Mark Thomas wrote:
> >>> All,
> >>>
> >>> This was mentioned briefly before (I think on a BZ issue) but needs a
> >>> wider discussion before taking action - if we do anything.
> >>>
> >>> It has been suggested that there isn't much benefit to maintaining the
> >>> NIO2 connector and that we could simplify maintenance by removing it
> >>> (deprecating in 11.0.x, removing in 12.0.x). The current code structure
> >>> would be retained should there ever be an NIO3 or similar that we wanted
> >>> to add.
> >>>
> >>> NIO and NIO2 performance is very similar.
> >>>
> >>> The NIO2 code is arguably more complicated. I've always found it harder
> >>> to get me head around but that could just be me.
> >>>
> >>> There is the odd bug with NIO2 we haven't been able to track down.
> >>>
> >>> A similar case might be made for dropping NIO and only maintaining NIO2
> >>> although, as stated above, I have a preference for NIO over NIO2.
> >>>
> >>> What do folks think?
> >>
> >> +1 for NIO2 removal, though I don't have much of a technical voice on
> >> this topic.
> >>
> >> I know this proposal actually makes the Tomcat code MORE complicated in
> >> order to support both NIO and this, but ... I would love for Tomcat 13
> >> (?) to drop NIO and go back to BIO with 100% virtual threads.
> >
> > This does not make sense to me, for almost the same reason that NIO2
> > stopped being better than NIO in Tomcat, once NIO got the fixes and
> > refactorings.
> >
> > The problems are:
> > - If implementing BIO in Tomcat, you're not actually doing real BIO.
>
> Understood. The gain is that Tomcat code becomes simpler while pushing
> the complexity down into the JVM to handle the non-blockingness.
>
> > You have to do an async IO style impl on top of BIO. It's not actually
> > that simple. I started writing code and it instantly looked like a big
> > mess.
> > - The BIO API that has to be used is beyond antiquated. You have to do
> > the item above on top of InputStream and OutputStream.
>
> Also understood.
>
> Doing this for the BIO-oriented servlet APIs is of course trivial. It's
> the async APIs which get weird if you want to try to implement them on
> top of BIO semantics.
>
> Servlet async was introduced to solve the problem of BIO with limited
> resources (threads), and developers who saw those benefits likely spent
> a lot of time re-writing the most performance-sensitive portions of
> their applications to use it. But it's terribly error-prone from an
> application-developer standpoint and the code Tomcat has to manage to
> make it all work is equally error-prone. I suspect that our resident IO
> experts (you and markt) are better than the average application
> developer and so I trust the Tomcat code very much.

Yes, ok async IO is more complex in many cases. Not always though, for
example NIO2 can be really simple for some client tasks, and in others
the completion handler feels very natural to use.

NIO2 could have been a little bit more feature rich. It would have
addressed a lot of the complexity. Unfortunately it didn't evolve
after its very impressive initial release.

In SocketWrapperBase, I tried to add:
- Knowing if the IO call completed without being async or if it is going async.
- Asking for complete writes or reads to avoid pointless completion
handler invocation loops.
- Allow options for handling invalid concurrent IO attempts, and
allowing mixing blocking and async IO.
The funny thing is that I found out eventually that this expanded NIO2
worked better on top of NIO than NIO2 (it is really the reason why
NIO2 the connector is being removed).

> > Of course, feel free to try. Maybe I tried the wrong way or something
> > (blocking NIO2 is still on top of async IO, so not good; I think
> > blocking NIO is the same kind of thing too). Even if everything works
> > out just fine, I would not plan on removing NIO as early as the next
> > iteration (if ever).
>
> Wouldn't it be great if we could just rewind time to before Servlet
> async was added and use BIO-on-VT for everything? This is why I said
> that Tomcat would become /more/ complicated. My hand-wavy proposal goes
> something like this:
>
> For applications that do not use servlet async APIs, use BIO-on-VT for
> everything and move on with your life. For applications relying on
> servlet async APIs, continue to use the NIO implementation.
>
> It remains to be seen whether BIO-on-VT has the same performance
> characteristics as (application) hand-coded async code using Tomcat's
> NIO connector, but I suspect that most application developers would
> welcome the trade-off for *significantly* more straightforward code.

Yes, of course BIO is easy to do if you think you can avoid decoupling
the IO in the application from the actual blocking IO. Unfortunately,
in addition to all the fancier IO APIs that were introduced in the
Servlet API, you also have to dump HTTP/2 and be HTTP/1.1 only. And
accept some additional limitations (timeouts are set on the socket -
but I suppose at this point nobody cares).

This sounds to me a lot like Tomcat 5.5.

> >> The speed of progress on VT coming from OpenJDK has been fairly fast.
> >> Evidently, they have already (provisionally) resolved the issue of
> >> synchronized-code pinning VTs to platform threads (they don't in Java
> >> 24, I believe), and the remaining issue is that of native code. My sense
> >> is that dipping into native code and getting a platform thread pinned is
> >> okay, since the remaining use-case for native code in Tomcat (minus
> >> applications) is cryptography, which runs pretty quickly and "comes up
> >> for air" all the time.
> >
> > Also there's QUIC, if done it would be really all FFM at its code so
> > not VT compatible.
>
> My argument is that the points at which control is handed over to native
> code are "fast" and should return quickly, back into the Java realm
> where VT benefits are back on the table.

With QUIC a lot more would be native code and very asynchronous. The
Java layer on top would appear to be simpler, associating one
SocketWrapper to one HTTP/3 stream (unlike HTTP/2 which has multiple
HTTP/2 streams for one SocketWrapper). As you can imagine a ton of
complexity then goes to that native QUIC implementation.

Rémy

> -chris
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: dev-h...@tomcat.apache.org
>

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to