1 /*
   2  * Copyright (c) 2013, 2014, 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 Public 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 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 
  26 package com.sun.webkit;
  27 
  28 import java.util.concurrent.ScheduledFuture;
  29 import java.util.concurrent.ScheduledThreadPoolExecutor;
  30 import java.util.concurrent.ThreadFactory;
  31 import java.util.concurrent.TimeUnit;
  32 import java.util.concurrent.atomic.AtomicInteger;
  33 import static java.util.logging.Level.WARNING;
  34 import java.util.logging.Logger;
  35 
  36 final class WatchdogTimer {
  37 
  38     private static final Logger logger =
  39             Logger.getLogger(WatchdogTimer.class.getName());
  40     private static final ThreadFactory threadFactory =
  41             new CustomThreadFactory();
  42 
  43 
  44     // The executor is intentionally made a non-static/instance
  45     // field (as opposed to a static/class field) in order for
  46     // fwkDestroy() to be able to orderly shutdown the executor
  47     // and wait for the outstanding runnable, if any, to complete.
  48     // Currently, there is just a single instance of this class
  49     // per process, so having a non-static executor should not
  50     // be a problem.
  51     private final ScheduledThreadPoolExecutor executor;
  52     private final Runnable runnable;
  53     private ScheduledFuture<?> future;
  54 
  55 
  56     private WatchdogTimer(final long nativePointer) {
  57         executor = new ScheduledThreadPoolExecutor(1, threadFactory);
  58         executor.setRemoveOnCancelPolicy(true);
  59 
  60         runnable = () -> {
  61             try {
  62                 twkFire(nativePointer);
  63             } catch (Throwable th) {
  64                 logger.log(WARNING, "Error firing watchdog timer", th);
  65             }
  66         };
  67     }
  68 
  69 
  70     private static WatchdogTimer fwkCreate(long nativePointer) {
  71         return new WatchdogTimer(nativePointer);
  72     }
  73 
  74     private void fwkStart(double limit) {
  75         if (future != null) {
  76             throw new IllegalStateException();
  77         }
  78         future = executor.schedule(runnable, (long) (limit * 1000) + 50,
  79                 TimeUnit.MILLISECONDS);
  80     }
  81 
  82     private void fwkStop() {
  83         if (future == null) {
  84             throw new IllegalStateException();
  85         }
  86         future.cancel(false);
  87         future = null;
  88     }
  89 
  90     private void fwkDestroy() {
  91         executor.shutdownNow();
  92         while (true) {
  93             try {
  94                 if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
  95                     break;
  96                 }
  97             } catch (InterruptedException ex) {
  98                 // continue waiting
  99             }
 100         }
 101     }
 102 
 103     private native void twkFire(long nativePointer);
 104 
 105     private static final class CustomThreadFactory implements ThreadFactory {
 106         private final ThreadGroup group;
 107         private final AtomicInteger index = new AtomicInteger(1);
 108 
 109         private CustomThreadFactory() {
 110             SecurityManager s = System.getSecurityManager();
 111             group = (s != null) ? s.getThreadGroup()
 112                     : Thread.currentThread().getThreadGroup();
 113         }
 114 
 115         @Override
 116         public Thread newThread(Runnable r) {
 117             Thread t = new Thread(group, r,
 118                     "Watchdog-Timer-" + index.getAndIncrement());
 119             t.setDaemon(true);
 120             return t;
 121         }
 122     }
 123 }