View Javadoc
1   /*
2    * Copyright 2013 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty5.util.concurrent;
17  
18  import io.netty5.util.internal.logging.InternalLogger;
19  
20  import java.util.Arrays;
21  import java.util.EventListener;
22  
23  final class DefaultFutureListeners {
24      private Object[] listeners;
25      private int size;
26  
27      DefaultFutureListeners() {
28          listeners = new Object[4];
29      }
30  
31      public void add(Object listener, Object context) {
32          Object[] listeners = this.listeners;
33          int index = size << 1;
34          if (index == listeners.length) {
35              this.listeners = listeners = Arrays.copyOf(listeners, listeners.length << 1);
36          }
37          listeners[index] = listener;
38          listeners[index + 1] = context;
39          size++;
40      }
41  
42      public void remove(EventListener listener) {
43          final Object[] listeners = this.listeners;
44          for (int i = 0, len = listeners.length; i < len; i += 2) {
45              Object candidateListener = listeners[i]; // With associated context at listeners[i + 1]
46              if (candidateListener == listener) {
47                  int listenersToMove = len - i - 2;
48                  if (listenersToMove > 0) {
49                      System.arraycopy(listeners, i + 2, listeners, i, listenersToMove);
50                  }
51                  listeners[len - 2] = null;
52                  listeners[len - 1] = null;
53                  size--;
54                  return;
55              }
56          }
57      }
58  
59      @SuppressWarnings("unchecked")
60      public <V> void notifyListeners(DefaultPromise<V> promise, InternalLogger logger) {
61          int size = this.size;
62          Object[] listeners = this.listeners;
63          for (int i = 0, len = size << 1; i < len; i += 2) {
64              Object listener = listeners[i];
65              Object context = listeners[i + 1];
66              try {
67                  // Since a listener could in theory be both a FutureListener and a FutureContextListener,
68                  // we use the presence of a context object to determine which one was meant when the listener
69                  // was added. The context reference will never be null if the FutureContextListener was intended,
70                  // even if the context passed was null. In that case, the reference will point to the
71                  // NULL_CONTEXT, and we have to convert it back to null here.
72                  if (context != null) {
73                      FutureContextListener<Object, V> fcl = (FutureContextListener<Object, V>) listener;
74                      fcl.operationComplete(context == DefaultPromise.NULL_CONTEXT ? null : context, promise);
75                  } else if (listener instanceof FutureListener) {
76                      FutureListener<V> fl = (FutureListener<V>) listener;
77                      fl.operationComplete(promise);
78                  } else if (listener != null) {
79                      logger.warn("Unknown future listener type: {} of type {}", listener, listener.getClass());
80                  } else {
81                      break; // Listeners are always densely packed in the array, so finding a null means we're done.
82                  }
83              } catch (Throwable t) {
84                  if (logger.isWarnEnabled()) {
85                      String className = listener.getClass().getName();
86                      logger.warn("An exception was thrown by " + className + ".operationComplete()", t);
87                  }
88              }
89          }
90      }
91  }