src/jdk.incubator.httpclient/share/classes/jdk/incubator/http/internal/common/SequentialScheduler.java
author chegar
Sun, 05 Nov 2017 17:32:13 +0000
branchhttp-client-branch
changeset 55763 634d8e14c172
child 55768 8674257c75ce
permissions -rw-r--r--
http-client-branch: intial load from jdk10/sandbox
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
55763
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     1
/*
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     2
 * Copyright (c) 2016, 2017, Oracle and/or its affiliates. All rights reserved.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     3
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     4
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     5
 * This code is free software; you can redistribute it and/or modify it
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     6
 * under the terms of the GNU General Public License version 2 only, as
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     7
 * published by the Free Software Foundation.  Oracle designates this
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     8
 * particular file as subject to the "Classpath" exception as provided
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
     9
 * by Oracle in the LICENSE file that accompanied this code.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    10
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    11
 * This code is distributed in the hope that it will be useful, but WITHOUT
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    12
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    13
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    14
 * version 2 for more details (a copy is included in the LICENSE file that
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    15
 * accompanied this code).
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    16
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    17
 * You should have received a copy of the GNU General Public License version
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    18
 * 2 along with this work; if not, write to the Free Software Foundation,
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    19
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    20
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    22
 * or visit www.oracle.com if you need additional information or have any
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    23
 * questions.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    24
 */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    25
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    26
package jdk.incubator.http.internal.common;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    27
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    28
import java.util.concurrent.Executor;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    29
import java.util.concurrent.atomic.AtomicInteger;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    30
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    31
import static java.util.Objects.requireNonNull;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    32
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    33
/**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    34
 * A scheduler of ( repeatable ) tasks that MUST be run sequentially.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    35
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    36
 * <p> This class can be used as a synchronization aid that assists a number of
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    37
 * parties in running a task in a mutually exclusive fashion.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    38
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    39
 * <p> To run the task, a party invokes {@code runOrSchedule}. To permanently
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    40
 * prevent the task from subsequent runs, the party invokes {@code stop}.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    41
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    42
 * <p> The parties can, but do not have to, operate in different threads.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    43
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    44
 * <p> The task can be either synchronous ( completes when its {@code run}
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    45
 * method returns ), or asynchronous ( completed when its
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    46
 * {@code DeferredCompleter} is explicitly completed ).
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    47
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    48
 * <p> The next run of the task will not begin until the previous run has
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    49
 * finished.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    50
 *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    51
 * <p> The task may invoke {@code runOrSchedule} itself, which may be a normal
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    52
 * situation.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    53
 */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    54
public final class SequentialScheduler {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    55
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    56
    /*
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    57
       Since the task is fixed and known beforehand, no blocking synchronization
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    58
       (locks, queues, etc.) is required. The job can be done solely using
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    59
       nonblocking primitives.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    60
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    61
       The machinery below addresses two problems:
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    62
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    63
         1. Running the task in a sequential order (no concurrent runs):
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    64
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    65
                begin, end, begin, end...
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    66
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    67
         2. Avoiding indefinite recursion:
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    68
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    69
                begin
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    70
                  end
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    71
                    begin
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    72
                      end
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    73
                        ...
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    74
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    75
       Problem #1 is solved with a finite state machine with 4 states:
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    76
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    77
           BEGIN, AGAIN, END, and STOP.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    78
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    79
       Problem #2 is solved with a "state modifier" OFFLOAD.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    80
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    81
       Parties invoke `runOrSchedule()` to signal the task must run. A party
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    82
       that has invoked `runOrSchedule()` either begins the task or exploits the
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    83
       party that is either beginning the task or ending it.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    84
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    85
       The party that is trying to end the task either ends it or begins it
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    86
       again.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    87
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    88
       To avoid indefinite recursion, before re-running the task the
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    89
       TryEndDeferredCompleter sets the OFFLOAD bit, signalling to its "child"
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    90
       TryEndDeferredCompleter that this ("parent") TryEndDeferredCompleter is
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    91
       available and the "child" must offload the task on to the "parent". Then
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    92
       a race begins. Whichever invocation of TryEndDeferredCompleter.complete
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    93
       manages to unset OFFLOAD bit first does not do the work.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    94
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    95
       There is at most 1 thread that is beginning the task and at most 2
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    96
       threads that are trying to end it: "parent" and "child". In case of a
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    97
       synchronous task "parent" and "child" are the same thread.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    98
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
    99
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   100
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   101
     * An interface to signal the completion of a {@link RestartableTask}.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   102
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   103
     * <p> The invocation of {@code complete} completes the task. The invocation
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   104
     * of {@code complete} may restart the task, if an attempt has previously
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   105
     * been made to run the task while it was already running.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   106
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   107
     * @apiNote {@code DeferredCompleter} is useful when a task is not necessary
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   108
     * complete when its {@code run} method returns, but will complete at a
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   109
     * later time, and maybe in different thread. This type exists for
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   110
     * readability purposes at use-sites only.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   111
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   112
    public static abstract class DeferredCompleter {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   113
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   114
        /** Extensible from this (outer) class ONLY. */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   115
        private DeferredCompleter() { }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   116
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   117
        /** Completes the task. Must be called once, and once only. */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   118
        public abstract void complete();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   119
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   120
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   121
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   122
     * A restartable task.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   123
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   124
    @FunctionalInterface
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   125
    public interface RestartableTask {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   126
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   127
        /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   128
         * The body of the task.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   129
         *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   130
         * @param taskCompleter
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   131
         *         A completer that must be invoked once, and only once,
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   132
         *         when this task is logically finished
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   133
         */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   134
        void run(DeferredCompleter taskCompleter);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   135
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   136
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   137
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   138
     * A complete restartable task is one which is simple and self-contained.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   139
     * It completes once its {@code run} method returns.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   140
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   141
    public static abstract class CompleteRestartableTask
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   142
        implements RestartableTask
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   143
    {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   144
        @Override
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   145
        public final void run(DeferredCompleter taskCompleter) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   146
            try {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   147
                run();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   148
            } finally {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   149
                taskCompleter.complete();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   150
            }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   151
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   152
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   153
        /** The body of the task. */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   154
        protected abstract void run();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   155
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   156
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   157
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   158
     * A RestartableTask that runs its main loop within a
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   159
     * synchronized block to place a memory barrier around it.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   160
     * Because the main loop can't run concurrently in two treads,
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   161
     * then the lock shouldn't be contended and no deadlock should
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   162
     * ever be possible.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   163
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   164
    public static final class SynchronizedRestartableTask
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   165
            extends CompleteRestartableTask {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   166
        private final Runnable mainLoop;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   167
        private final Object lock = new Object();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   168
        public SynchronizedRestartableTask(Runnable mainLoop) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   169
            this.mainLoop = mainLoop;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   170
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   171
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   172
        @Override
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   173
        protected void run() {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   174
            synchronized(lock) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   175
                mainLoop.run();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   176
            }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   177
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   178
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   179
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   180
    private static final int OFFLOAD =  1;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   181
    private static final int AGAIN   =  2;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   182
    private static final int BEGIN   =  4;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   183
    private static final int STOP    =  8;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   184
    private static final int END     = 16;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   185
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   186
    private final AtomicInteger state = new AtomicInteger(END);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   187
    private final RestartableTask restartableTask;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   188
    private final DeferredCompleter completer;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   189
    private final SchedulableTask schedulableTask;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   190
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   191
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   192
     * A simple task that can be pushed on an executor to execute
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   193
     * {@code restartableTask.run(completer)}.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   194
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   195
    private final class SchedulableTask implements Runnable {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   196
        @Override
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   197
        public void run() {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   198
            restartableTask.run(completer);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   199
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   200
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   201
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   202
    public SequentialScheduler(RestartableTask restartableTask) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   203
        this.restartableTask = requireNonNull(restartableTask);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   204
        this.completer = new TryEndDeferredCompleter();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   205
        this.schedulableTask = new SchedulableTask();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   206
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   207
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   208
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   209
     * Runs or schedules the task to be run.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   210
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   211
     * @implSpec The recursion which is possible here must be bounded:
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   212
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   213
     *  <pre>{@code
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   214
     *     this.runOrSchedule()
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   215
     *         completer.complete()
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   216
     *             this.runOrSchedule()
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   217
     *                 ...
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   218
     * }</pre>
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   219
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   220
     * @implNote The recursion in this implementation has the maximum
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   221
     * depth of 1.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   222
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   223
    public void runOrSchedule() {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   224
        runOrSchedule(schedulableTask, null);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   225
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   226
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   227
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   228
     * Runs or schedules the task to be run in the provided executor.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   229
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   230
     * <p> This method can be used when potential executing from a calling
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   231
     * thread is not desirable.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   232
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   233
     * @param executor
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   234
     *         An executor in which to execute the task, if the task needs
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   235
     *         to be executed.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   236
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   237
     * @apiNote The given executor can be {@code null} in which case calling
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   238
     * {@code deferOrSchedule(null)} is strictly equivalent to calling
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   239
     * {@code runOrSchedule()}.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   240
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   241
    public void deferOrSchedule(Executor executor) { // TODO: why this name? why not runOrSchedule?
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   242
        runOrSchedule(schedulableTask, executor);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   243
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   244
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   245
    private void runOrSchedule(SchedulableTask task, Executor executor) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   246
        while (true) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   247
            int s = state.get();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   248
            if (s == END) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   249
                if (state.compareAndSet(END, BEGIN)) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   250
                    break;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   251
                }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   252
            } else if ((s & BEGIN) != 0) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   253
                // Tries to change the state to AGAIN, preserving OFFLOAD bit
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   254
                if (state.compareAndSet(s, AGAIN | (s & OFFLOAD))) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   255
                    return;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   256
                }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   257
            } else if ((s & AGAIN) != 0 || s == STOP) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   258
                /* In the case of AGAIN the scheduler does not provide
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   259
                   happens-before relationship between actions prior to
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   260
                   runOrSchedule() and actions that happen in task.run().
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   261
                   The reason is that no volatile write is done in this case,
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   262
                   and the call piggybacks on the call that has actually set
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   263
                   AGAIN state. */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   264
                return;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   265
            } else {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   266
                // Non-existent state, or the one that cannot be offloaded
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   267
                throw new InternalError(String.valueOf(s));
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   268
            }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   269
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   270
        if (executor == null) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   271
            task.run();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   272
        } else {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   273
            executor.execute(task);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   274
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   275
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   276
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   277
    /** The only concrete {@code DeferredCompleter} implementation. */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   278
    private class TryEndDeferredCompleter extends DeferredCompleter {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   279
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   280
        @Override
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   281
        public void complete() {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   282
            while (true) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   283
                int s;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   284
                while (((s = state.get()) & OFFLOAD) != 0) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   285
                    // Tries to offload ending of the task to the parent
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   286
                    if (state.compareAndSet(s, s & ~OFFLOAD)) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   287
                        return;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   288
                    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   289
                }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   290
                while (true) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   291
                    if ((s & OFFLOAD) != 0) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   292
                        /* OFFLOAD bit can never be observed here. Otherwise
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   293
                           it would mean there is another invocation of
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   294
                           "complete" that can run the task. */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   295
                        throw new InternalError(String.valueOf(s));
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   296
                    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   297
                    if (s == BEGIN) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   298
                        if (state.compareAndSet(BEGIN, END)) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   299
                            return;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   300
                        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   301
                    } else if (s == AGAIN) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   302
                        if (state.compareAndSet(AGAIN, BEGIN | OFFLOAD)) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   303
                            break;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   304
                        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   305
                    } else if (s == STOP) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   306
                        return;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   307
                    } else if (s == END) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   308
                        throw new IllegalStateException("Duplicate completion");
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   309
                    } else {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   310
                        // Non-existent state
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   311
                        throw new InternalError(String.valueOf(s));
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   312
                    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   313
                    s = state.get();
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   314
                }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   315
                restartableTask.run(completer);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   316
            }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   317
        }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   318
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   319
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   320
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   321
     * Tells whether, or not, this scheduler has been permanently stopped.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   322
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   323
     * <p> Should be used from inside the task to poll the status of the
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   324
     * scheduler, pretty much the same way as it is done for threads:
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   325
     * <pre>{@code
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   326
     *     if (!Thread.currentThread().isInterrupted()) {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   327
     *         ...
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   328
     *     }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   329
     * }</pre>
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   330
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   331
    public boolean isStopped() {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   332
        return state.get() == STOP;
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   333
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   334
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   335
    /**
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   336
     * Stops this scheduler.  Subsequent invocations of {@code runOrSchedule}
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   337
     * are effectively no-ops.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   338
     *
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   339
     * <p> If the task has already begun, this invocation will not affect it,
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   340
     * unless the task itself uses {@code isStopped()} method to check the state
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   341
     * of the handler.
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   342
     */
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   343
    public void stop() {
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   344
        state.set(STOP);
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   345
    }
634d8e14c172 http-client-branch: intial load from jdk10/sandbox
chegar
parents:
diff changeset
   346
}