1 /*
   2  * Copyright 2010 Google, Inc.  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 Public License version 2 only, as
   7  * published by the Free Software Foundation.  Sun designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Sun 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 Public 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 Public 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 import java.util.LinkedList;
  26 import sun.misc.Continuation;
  27 
  28 /*
  29  * This class demonstrates the continuation feature by implementing a
  30  * user-thread-like object.
  31  */
  32 public class ContinuationTest9 {
  33     private static int ITER = 10000;
  34 
  35     public static void test() throws Exception {
  36         Runnable r1 = new Runnable() {
  37             public void run() {
  38                 while (true) {
  39                     System.out.println("t1: " + counter++);
  40                     UserThread.yield();
  41                     if (counter > ITER) {
  42                         return;
  43                     }
  44                 }
  45             }};
  46         Runnable r2 = new Runnable() {
  47         public void run() {
  48             while (true) {
  49                 System.out.println("t2: " + counter++);
  50                 UserThread.yield();
  51                 if (counter > ITER) {
  52                     return;
  53                 }
  54             }
  55         }};
  56         UserThread t1 = new UserThread(r1);
  57         UserThread t2 = new UserThread(r2);
  58         t1.start();
  59         t2.start();
  60         UserThread.join();
  61     }
  62 
  63     private static int counter = 0;
  64 
  65     public static void main(String[] args) throws Exception {
  66       test();
  67       System.exit(0);
  68     }
  69 }
  70 
  71 class UserThread {
  72     // The schedule queue
  73     private static final LinkedList<UserThread> queue = new LinkedList<UserThread>();
  74     // The currently scheduled UserThread
  75     private static UserThread currentThread;
  76     // Used to synchronize between the internal thread and the outside world
  77     // This guards queue, nthreads and currentThread
  78     private static final Object lock = new Object();
  79     // The number of live UserThreads
  80     private static int nthreads = 0;
  81     // Lock for join
  82     private static final Object awaitLock = new Object();
  83 
  84     // The backing Java thread that executes
  85     // all UserThreads
  86     private static final Thread jthread =
  87         new Thread(new Runnable() {
  88             public void run() {
  89                 while (true) {
  90                     try {
  91                         UserThread ut_to_run;
  92                         synchronized (lock) {
  93                             if (queue.size() == 0) {
  94                                 // Wait if there's no user thread to schedule
  95                                 lock.wait();
  96                             }
  97                             // Get the user thread to be scheduled next
  98                             ut_to_run = queue.remove();
  99                             currentThread = ut_to_run;
 100                         }
 101                         final UserThread ut = ut_to_run;
 102                         final Continuation c = ut.continuation;
 103                         // If it's scheduled for the first time,
 104                         if (c == null) {
 105                             // simply execute the given Runnable in a scope
 106                             Continuation.enter(ut.runnable, null);
 107                         } else {
 108                             // Otherwise, resume the saved continuation
 109                             Runnable r = new Runnable() {
 110                                 public void run() {
 111                                     c.resume(null);
 112                                 }};
 113                             Continuation.enter(r, null);
 114                         }
 115                     } catch (Throwable t) {
 116                         throw new RuntimeException(t);
 117                     }
 118                 }
 119             }
 120           });
 121 
 122     static {
 123         jthread.start();
 124     }
 125 
 126     private final Runnable runnable;
 127     private Continuation continuation;
 128 
 129     public UserThread(final Runnable r) {
 130         runnable = new Runnable() {
 131             public void run() {
 132                 r.run();
 133                 UserThread.exit();
 134             }
 135           };
 136     }
 137 
 138     public void start() {
 139         synchronized(lock) {
 140             nthreads++;
 141             // Add the given user thread to the schedule queue
 142             queue.add(this);
 143             // Notify the Java thread if it's waiting
 144             lock.notify();
 145         }
 146     }
 147 
 148     public static void yield() {
 149         // Create a new continuation for each yield
 150         final Continuation c = new Continuation();
 151         synchronized (lock) {
 152             // Save it in the user thread
 153             currentThread.continuation = c;
 154             // Put the user thread back to the schedule queue
 155             queue.add(currentThread);
 156         }
 157         // Save the current scope into it. It will cause
 158         // the enter at the bottom of the scope to return.
 159         c.save();
 160     }
 161 
 162     private static void exit() {
 163         int new_nthreads;
 164         synchronized (lock) {
 165             nthreads--;
 166             new_nthreads = nthreads;
 167             if (new_nthreads == 0) {
 168                 synchronized (awaitLock) {
 169                     awaitLock.notify();
 170                 }
 171             }
 172         }
 173     }
 174 
 175     public static void join() throws InterruptedException {
 176         synchronized (awaitLock) {
 177             awaitLock.wait();
 178         }
 179     }
 180 }