jdk/src/java.httpclient/share/classes/java/net/http/WSSignalHandler.java
changeset 37874 02589df0999a
child 39730 196f4e25d9f5
equal deleted inserted replaced
37858:7c04fcb12bd4 37874:02589df0999a
       
     1 /*
       
     2  * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General  License version 2 only, as
       
     7  * published by the Free Software Foundation.  Oracle designates this
       
     8  * particular file as subject to the "Classpath" exception as provided
       
     9  * by Oracle in the LICENSE file that accompanied this code.
       
    10  *
       
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General  License
       
    14  * version 2 for more details (a copy is included in the LICENSE file that
       
    15  * accompanied this code).
       
    16  *
       
    17  * You should have received a copy of the GNU General  License version
       
    18  * 2 along with this work; if not, write to the Free Software Foundation,
       
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    20  *
       
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    22  * or visit www.oracle.com if you need additional information or have any
       
    23  * questions.
       
    24  */
       
    25 package java.net.http;
       
    26 
       
    27 import java.util.concurrent.Executor;
       
    28 import java.util.concurrent.RejectedExecutionException;
       
    29 import java.util.concurrent.atomic.AtomicInteger;
       
    30 
       
    31 import static java.util.Objects.requireNonNull;
       
    32 
       
    33 //
       
    34 // The problem:
       
    35 // ------------
       
    36 //   1. For every invocation of 'signal()' there must be at least
       
    37 //      1 invocation of 'handler.run()' that goes after
       
    38 //   2. There must be no more than 1 thread running the 'handler.run()'
       
    39 //      at any given time
       
    40 //
       
    41 // For example, imagine each signal increments (+1) some number. Then the
       
    42 // handler responds (eventually) the way that makes the number 0.
       
    43 //
       
    44 // For each signal there's a response. Several signals may be handled by a
       
    45 // single response.
       
    46 //
       
    47 final class WSSignalHandler {
       
    48 
       
    49     // In this state the task is neither submitted nor running.
       
    50     // No one is handling signals. If a new signal has been received, the task
       
    51     // has to be submitted to the executor in order to handle this signal.
       
    52     private static final int DONE    = 0;
       
    53 
       
    54     // In this state the task is running.
       
    55     // * If the signaller has found the task in this state it will try to change
       
    56     //   the state to RERUN in order to make the already running task to handle
       
    57     //   the new signal before exiting.
       
    58     // * If the task has found itself in this state it will exit.
       
    59     private static final int RUNNING = 1;
       
    60 
       
    61     // A signal to the task, that it must rerun on the spot (without being
       
    62     // resubmitted to the executor).
       
    63     // If the task has found itself in this state it resets the state to
       
    64     // RUNNING and repeats the pass.
       
    65     private static final int RERUN   = 2;
       
    66 
       
    67     private final AtomicInteger state = new AtomicInteger(DONE);
       
    68 
       
    69     private final Executor executor;
       
    70     private final Runnable task;
       
    71 
       
    72     WSSignalHandler(Executor executor, Runnable handler) {
       
    73         this.executor = requireNonNull(executor);
       
    74         requireNonNull(handler);
       
    75 
       
    76         task = () -> {
       
    77             while (!Thread.currentThread().isInterrupted()) {
       
    78 
       
    79                 try {
       
    80                     handler.run();
       
    81                 } catch (Exception e) {
       
    82                     // Sorry, the task won't be automatically retried;
       
    83                     // hope next signals (if any) will kick off the handling
       
    84                     state.set(DONE);
       
    85                     throw e;
       
    86                 }
       
    87 
       
    88                 int prev = state.getAndUpdate(s -> {
       
    89                     if (s == RUNNING) {
       
    90                         return DONE;
       
    91                     } else {
       
    92                         return RUNNING;
       
    93                     }
       
    94                 });
       
    95 
       
    96                 // Can't be DONE, since only the task itself may transit state
       
    97                 // into DONE (with one exception: RejectedExecution in signal();
       
    98                 // but in that case we couldn't be here at all)
       
    99                 assert prev == RUNNING || prev == RERUN;
       
   100 
       
   101                 if (prev == RUNNING) {
       
   102                     break;
       
   103                 }
       
   104             }
       
   105         };
       
   106     }
       
   107 
       
   108     // Invoked by outer code to signal
       
   109     void signal() {
       
   110 
       
   111         int prev = state.getAndUpdate(s -> {
       
   112             switch (s) {
       
   113                 case RUNNING:
       
   114                     return RERUN;
       
   115                 case DONE:
       
   116                     return RUNNING;
       
   117                 case RERUN:
       
   118                     return RERUN;
       
   119                 default:
       
   120                     throw new InternalError(String.valueOf(s));
       
   121             }
       
   122         });
       
   123 
       
   124         if (prev != DONE) {
       
   125             // Nothing to do! piggybacking on previous signal
       
   126             return;
       
   127         }
       
   128         try {
       
   129             executor.execute(task);
       
   130         } catch (RejectedExecutionException e) {
       
   131             // Sorry some signal() invocations may have been accepted, but won't
       
   132             // be done, since the 'task' couldn't be submitted
       
   133             state.set(DONE);
       
   134             throw e;
       
   135         }
       
   136     }
       
   137 }