Repository: netty/netty
Issue: Deprecates LazyRunnable
Labels: cleanup

https://ionutbalosin.com/2023/03/jvm-performance-comparison-for-jdk-17/#typecheckbenchmark shows how bad currently JITs handle type checking of interfaces not implemented by a concrete type.

I've built an [agent](https://github.com/RedHatPerf/type-pollution-agent) that can detect such slow paths and by using it in a https://hyperfoil.io/ workload it has reported:
```
--------------------------
5:      io.vertx.core.net.impl.ConnectionBase$$Lambda$281/0x0000000800fd0fc8
Count:  3065706
Types:
        io.netty.util.concurrent.AbstractEventExecutor$LazyRunnable
Traces:
        io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java:827)
                class: io.netty.util.concurrent.AbstractEventExecutor$LazyRunnable
                count: 3065706
--------------------------
6:      io.vertx.core.http.impl.Http1xServerConnection$$Lambda$284/0x0000000800fd2330
Count:  3064004
Types:
        io.netty.util.concurrent.AbstractEventExecutor$LazyRunnable
Traces:
        io.netty.util.concurrent.SingleThreadEventExecutor.execute0(SingleThreadEventExecutor.java:827)
                class: io.netty.util.concurrent.AbstractEventExecutor$LazyRunnable
                count: 3064004
--------------------------
```
that means that event submission always receive such hit, without providing any benefit for such a common use case.
I'm sending a PR to deprecate it on Netty 4.1

@njhill Let me know if you need any more info about the issue


Issue comments:

Pull request review comments:
should this be `execute(task, false)`?
check `SingleThreadEventExecutor::lazyExecute` overridden method:

```java
    @Override
    public void lazyExecute(Runnable task) {
        lazyExecute0(task);
    }

    private void lazyExecute0(@Schedule Runnable task) {
        execute(ObjectUtil.checkNotNull(task, "task"), false);
    }
```
```suggestion
     * @deprecated override {@link SingleThreadEventExecutor#wakesUpForTask} to re-create this behaviour
```
```suggestion
     *  @deprecated override {@link SingleThreadEventExecutor#wakesUpForTask} to re-create this behaviour
```

Patch that resolved the issue:
diff --git a/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java
index ce6b1ac875f..b32978ee679 100644
--- a/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java
+++ b/common/src/main/java/io/netty/util/concurrent/AbstractEventExecutor.java
@@ -177,25 +177,19 @@ protected static void runTask(@Execute Runnable task) {
     /**
      * Like {@link #execute(Runnable)} but does not guarantee the task will be run until either
      * a non-lazy task is executed or the executor is shut down.
-     *
-     * This is equivalent to submitting a {@link AbstractEventExecutor.LazyRunnable} to
-     * {@link #execute(Runnable)} but for an arbitrary {@link Runnable}.
-     *
+     * <p>
      * The default implementation just delegates to {@link #execute(Runnable)}.
+     * </p>
      */
     @UnstableApi
     public void lazyExecute(Runnable task) {
-        lazyExecute0(task);
-    }
-
-    private void lazyExecute0(@Schedule Runnable task) {
         execute(task);
     }
 
     /**
-     * Marker interface for {@link Runnable} to indicate that it should be queued for execution
-     * but does not need to run immediately.
+     *  @deprecated override {@link SingleThreadEventExecutor#wakesUpForTask} to re-create this behaviour
+     *
      */
-    @UnstableApi
+    @Deprecated
     public interface LazyRunnable extends Runnable { }
 }
diff --git a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java
index 6c7020ee234..070b2fdfff6 100644
--- a/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java
+++ b/common/src/main/java/io/netty/util/concurrent/SingleThreadEventExecutor.java
@@ -824,7 +824,7 @@ public void lazyExecute(Runnable task) {
 
     private void execute0(@Schedule Runnable task) {
         ObjectUtil.checkNotNull(task, "task");
-        execute(task, !(task instanceof LazyRunnable) && wakesUpForTask(task));
+        execute(task, wakesUpForTask(task));
     }
 
     private void lazyExecute0(@Schedule Runnable task) {
@@ -917,7 +917,7 @@ public final ThreadProperties threadProperties() {
     }
 
     /**
-     * @deprecated use {@link AbstractEventExecutor.LazyRunnable}
+     * @deprecated override {@link SingleThreadEventExecutor#wakesUpForTask} to re-create this behaviour
      */
     @Deprecated
     protected interface NonWakeupRunnable extends LazyRunnable { }
diff --git a/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java b/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java
index 5e2acb1209f..f7136c62714 100644
--- a/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java
+++ b/transport/src/main/java/io/netty/channel/SingleThreadEventLoop.java
@@ -115,7 +115,7 @@ public final void executeAfterEventLoopIteration(Runnable task) {
             reject(task);
         }
 
-        if (!(task instanceof LazyRunnable) && wakesUpForTask(task)) {
+        if (wakesUpForTask(task)) {
             wakeup(inEventLoop());
         }
     }


Test patch:
diff --git a/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java b/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java
index 8465ea8d3ae..dc12034bcc5 100644
--- a/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java
+++ b/common/src/test/java/io/netty/util/concurrent/SingleThreadEventExecutorTest.java
@@ -17,7 +17,6 @@
 
 import org.junit.jupiter.api.Test;
 
-import io.netty.util.concurrent.AbstractEventExecutor.LazyRunnable;
 import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.api.function.Executable;
 
@@ -208,12 +207,18 @@ public void run() {
         }
     }
 
-    static class LazyLatchTask extends LatchTask implements LazyRunnable { }
+    static class LazyLatchTask extends LatchTask { }
 
     @Test
     public void testLazyExecution() throws Exception {
         final SingleThreadEventExecutor executor = new SingleThreadEventExecutor(null,
                 Executors.defaultThreadFactory(), false) {
+
+            @Override
+            protected boolean wakesUpForTask(final Runnable task) {
+                return !(task instanceof LazyLatchTask);
+            }
+
             @Override
             protected void run() {
                 while (!confirmShutdown()) {