View Javadoc

1   //
2   //  ========================================================================
3   //  Copyright (c) 1995-2013 Mort Bay Consulting Pty. Ltd.
4   //  ------------------------------------------------------------------------
5   //  All rights reserved. This program and the accompanying materials
6   //  are made available under the terms of the Eclipse Public License v1.0
7   //  and Apache License v2.0 which accompanies this distribution.
8   //
9   //      The Eclipse Public License is available at
10  //      http://www.eclipse.org/legal/epl-v10.html
11  //
12  //      The Apache License v2.0 is available at
13  //      http://www.opensource.org/licenses/apache2.0.php
14  //
15  //  You may elect to redistribute this code under either of these licenses.
16  //  ========================================================================
17  //
18  
19  package org.eclipse.jetty.util;
20  
21  /**
22   * Utility class that splits calls to {@link #invoke(Object)} into calls to {@link #fork(Object)} or {@link #call(Object)}
23   * depending on the max number of reentrant calls to {@link #invoke(Object)}.
24   * <p/>
25   * This class prevents {@link StackOverflowError}s in case of methods that end up invoking themselves,
26   * such is common for {@link Callback#succeeded()}.
27   * <p/>
28   * Typical use case is:
29   * <pre>
30   * public void reentrantMethod(Object param)
31   * {
32   *     if (condition || tooManyReenters)
33   *         fork(param)
34   *     else
35   *         call(param)
36   * }
37   * </pre>
38   * Calculating {@code tooManyReenters} usually involves using a {@link ThreadLocal} and algebra on the
39   * number of reentrant invocations, which is factored out in this class for convenience.
40   * <p />
41   * The same code using this class becomes:
42   * <pre>
43   * private final ForkInvoker invoker = ...;
44   *
45   * public void reentrantMethod(Object param)
46   * {
47   *     invoker.invoke(param);
48   * }
49   * </pre>
50   *
51   */
52  public abstract class ForkInvoker<T>
53  {
54      private static final ThreadLocal<Integer> __invocations = new ThreadLocal<Integer>()
55      {
56          @Override
57          protected Integer initialValue()
58          {
59              return 0;
60          }
61      };
62      private final int _maxInvocations;
63  
64      /**
65       * Creates an instance with the given max number of reentrant calls to {@link #invoke(Object)}
66       * <p/>
67       * If {@code maxInvocations} is zero or negative, it is interpreted
68       * as if the max number of reentrant calls is infinite.
69       *
70       * @param maxInvocations the max number of reentrant calls to {@link #invoke(Object)}
71       */
72      public ForkInvoker(int maxInvocations)
73      {
74          _maxInvocations = maxInvocations;
75      }
76  
77      /**
78       * Invokes either {@link #fork(Object)} or {@link #call(Object)}.
79       * If {@link #condition()} returns true, {@link #fork(Object)} is invoked.
80       * Otherwise, if the max number of reentrant calls is positive and the
81       * actual number of reentrant invocations exceeds it, {@link #fork(Object)} is invoked.
82       * Otherwise, {@link #call(Object)} is invoked.
83       * @param arg TODO
84       *
85       * @return true if {@link #fork(Object)} has been called, false otherwise
86       */
87      public boolean invoke(T arg)
88      {
89          boolean countInvocations = _maxInvocations > 0;
90          int invocations = __invocations.get();
91          if (condition() || countInvocations && invocations > _maxInvocations)
92          {
93              fork(arg);
94              return true;
95          }
96          else
97          {
98              if (countInvocations)
99                  __invocations.set(invocations + 1);
100             try
101             {
102                 call(arg);
103                 return false;
104             }
105             finally
106             {
107                 if (countInvocations)
108                     __invocations.set(invocations);
109             }
110         }
111     }
112 
113     /**
114      * Subclasses should override this method returning true if they want
115      * {@link #invoke(Object)} to call {@link #fork(Object)}.
116      *
117      * @return true if {@link #invoke(Object)} should call {@link #fork(Object)}, false otherwise
118      */
119     protected boolean condition()
120     {
121         return false;
122     }
123 
124     /**
125      * Executes the forked invocation
126      * @param arg TODO
127      */
128     public abstract void fork(T arg);
129 
130     /**
131      * Executes the direct, non-forked, invocation
132      * @param arg TODO
133      */
134     public abstract void call(T arg);
135 }