Skip to content

Thread:yield a non-event loop virtual thread has an strange semantic #19

@franz1981

Description

@franz1981

Assuming to have a virtual thread (only) which call N times Thread::yield, it will re-run itself immediately till exhausting the available yield limits (see 249b4b9).

It would be nicer if a Thread::yield from a virtual thread different from the event loop can just enable running the event loop.

In short, having something like:

   @Override
   public void execute(Runnable command) {
      if (ioEventLoop.isTerminated()) {
         throw new RejectedExecutionException("event loop is shutting down");
      }
      // The default scheduler won't shut down, but Netty's event loop can!
      Runnable eventLoopContinuation = this.eventLoopContinuation;
      if (eventLoopContinuation == null) {
         eventLoopContinuation = setEventLoopContinuation(command);
      }
      if (eventLoopContinuation == command) {
         submittedEventLoopContinuation = true;
      } else {
         // <--------------------- Thread::yield should happen in the carrier
         if (Thread.currentThread() == carrierThread && externalContinuations.isEmpty() && submittedEventLoopContinuation) {          
            // if we have an event loop to run and no other tasks...  
            externalContinuations.offer(command) 
            // let's just resume the event loop, if it needs
            submittedEventLoopContinuation = false;
            eventLoopContinuation.run();
            // here shouldn't need to wake up the event loop, but needs investigation!
            return;
            // <---------------------
         } else {
            externalContinuations.offer(command)
         }
      }
      if (!ioEventLoop.inEventLoop(Thread.currentThread())) {
         // TODO: if we have access to the scheduler brought by the continuation,
         //       we could skip the wakeup if matches with this.
         ioEventLoop.wakeup();
         LockSupport.unpark(parkedCarrierThread);
      } else if (eventLoopContinuation != command && !running.get()) {
         // since the event loop was blocked, it's fine if we try to consume some continuations, if any
         Thread.yield();
      }
   }

Clearly this is going to improve Thread::yield reactivity for a single virtual thread doing it; if we have 2 virtual threads which Thread::yield, it won't work.

And, if the event loop is operating on a resource which we expect the Thread::yield on the virtual thread to grant room to make some progress, it will fail.

In short; even with this "workaround" the problem of fairly grant progress to everyone, is not fixed.
A potentially better solution would be to give all virtual thread (including the event loop) a similar bandwidth (i.e. chances to be executed for some amount of time) - but since the event loop I/O cannot be "stopped", I'm not sure is something we could address.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions