Implementation of a watchdog to monitor entryDepth and take corrective action.


git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@10613 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/impl/Impl.java b/user/src/com/google/gwt/core/client/impl/Impl.java
index 8a90a7f..bdd4e00 100644
--- a/user/src/com/google/gwt/core/client/impl/Impl.java
+++ b/user/src/com/google/gwt/core/client/impl/Impl.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.core.client.impl;
 
+import com.google.gwt.core.client.Duration;
 import com.google.gwt.core.client.GWT;
 import com.google.gwt.core.client.JavaScriptObject;
 
@@ -24,6 +25,8 @@
  */
 public final class Impl {
 
+  private static final int WATCHDOG_ENTRY_DEPTH_CHECK_INTERVAL_MS = 2000;
+
   /**
    * Used by {@link #entry0(Object, Object)} to handle reentrancy.
    */
@@ -31,6 +34,16 @@
   private static int sNextHashId = 0;
 
   /**
+   * TimeStamp indicating last scheduling of the entry depth watchdog.
+   */
+  private static double watchdogEntryDepthLastScheduled;
+
+  /**
+   * Timer id of the entry depth watchdog. -1 if not scheduled.
+   */
+  private static int watchdogEntryDepthTimerId = -1;
+
+  /**
    * This method should be used whenever GWT code is entered from a JS context
    * and there is no GWT code in the same module on the call stack. Examples
    * include event handlers, exported methods, and module initialization.
@@ -182,6 +195,14 @@
   private static boolean enter() {
     assert entryDepth >= 0 : "Negative entryDepth value at entry " + entryDepth;
 
+    if (entryDepth != 0) {
+      double now = Duration.currentTimeMillis();
+      if (now - watchdogEntryDepthLastScheduled > WATCHDOG_ENTRY_DEPTH_CHECK_INTERVAL_MS) {
+        watchdogEntryDepthLastScheduled = now;
+        watchdogEntryDepthTimerId = watchdogEntryDepthSchedule();
+      }
+    }
+
     // We want to disable some actions in the reentrant case
     if (entryDepth++ == 0) {
       SchedulerImpl.INSTANCE.flushEntryCommands();
@@ -242,6 +263,10 @@
     assert entryDepth >= 0 : "Negative entryDepth value at exit " + entryDepth;
     if (initialEntry) {
       assert entryDepth == 0 : "Depth not 0" + entryDepth;
+      if (watchdogEntryDepthTimerId != -1) {
+        watchdogEntryDepthCancel(watchdogEntryDepthTimerId);
+        watchdogEntryDepthTimerId = -1;
+      }
     }
   }
 
@@ -259,4 +284,29 @@
     // Intentionally not returning a value
     return;
   }-*/;
+
+  private static native void watchdogEntryDepthCancel(int timerId) /*-{
+    $wnd.clearTimeout(timerId);
+  }-*/;
+
+  private static void watchdogEntryDepthRun() {
+    // Note: this must NEVER be called nested in a $entry() call.
+    // This method is call from a "setTimeout": entryDepth should be set to 0.
+    if (entryDepth != 0) {
+      int oldDepth = entryDepth;
+      entryDepth = 0;
+      if (GWT.getUncaughtExceptionHandler() != null) {
+        // Report the problem.
+        GWT.getUncaughtExceptionHandler().onUncaughtException(
+            new IllegalStateException("Invalid entryDepth value " + oldDepth));
+      }
+    }
+    watchdogEntryDepthTimerId = -1;  // Timer has run.
+  }
+
+  private static native int watchdogEntryDepthSchedule() /*-{
+    return $wnd.setTimeout(function() {
+      @com.google.gwt.core.client.impl.Impl::watchdogEntryDepthRun()();
+    }, 10);
+  }-*/;
 }