Add safepoint callback for gc

This change adds a safepoint callback registration mechanism.
On a per-thread (or all-thread) basis, you pass in a function to be
called at the next safe point by the target thread.  That if that
function returns 0, it will be automatically disarmed.  If not,
the callback will stay in effect and the function will be called
on all subsequent safe points.

GC is the expected customer of this feature.

Change-Id: Icd3b93128b1fd547e142d047a12df7ae8ee646e3
diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c
index 7ad3096..ab50169 100644
--- a/vm/interp/Interp.c
+++ b/vm/interp/Interp.c
@@ -1552,6 +1552,35 @@
 }
 
 /*
+ * Arm a safepoint callback for a thread.  If funct is null,
+ * clear any pending callback.
+ * TODO: only gc is currently using this feature, and will have
+ * at most a single outstanding callback request.  Until we need
+ * something more capable and flexible, enforce this limit.
+ */
+void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
+                             void* arg)
+{
+    dvmLockMutex(&thread->callbackMutex);
+    if ((funct == NULL) || (thread->callback == NULL)) {
+        thread->callback = funct;
+        thread->callbackArg = arg;
+        dvmUpdateInterpBreak(thread, kInterpSafePointCallback,
+                             kSubModeNormal, (funct != NULL));
+    } else {
+        // Already armed.  Different?
+        if ((funct != thread->callback) ||
+            (arg != thread->callbackArg)) {
+            // Yes - report failure and die
+            LOGE("ArmSafePointCallback failed, thread %d", thread->threadId);
+            dvmUnlockMutex(&thread->callbackMutex);
+            dvmAbort();
+        }
+    }
+    dvmUnlockMutex(&thread->callbackMutex);
+}
+
+/*
  * One-time initialization at thread creation.  Here we initialize
  * useful constants.
  */
@@ -1604,7 +1633,7 @@
  * count profiling, JIT trace building, etc.  Dalvik PC has been exported
  * prior to call, but Thread copy of dPC & fp are not current.
  */
-void dvmCheckBefore(const u2 *pc, const u4 *fp, Thread* self)
+void dvmCheckBefore(const u2 *pc, u4 *fp, Thread* self)
 {
     const Method* method = self->interpSave.method;
     assert(self->interpBreak.ctl.breakFlags != 0);
@@ -1637,14 +1666,38 @@
     }
 #endif
 
-    /* Suspend pending? */
-    if (self->interpBreak.ctl.suspendCount) {
+    /* Safe point handling */
+    if (self->interpBreak.ctl.suspendCount ||
+        (self->interpBreak.ctl.breakFlags & kInterpSafePointCallback)) {
         // Are we are a safe point?
         int flags;
         flags = dexGetFlagsFromOpcode(dexOpcodeFromCodeUnit(*pc));
         if (flags & VERIFY_GC_INST_MASK) {
-            dvmExportPC(pc, fp);
-            dvmCheckSuspendPending(self);
+            // Yes, at a safe point.  Pending callback?
+            if (self->interpBreak.ctl.breakFlags & kInterpSafePointCallback) {
+                SafePointCallback callback;
+                void* arg;
+                // Get consistent funct/arg pair
+                dvmLockMutex(&self->callbackMutex);
+                callback = self->callback;
+                arg = self->callbackArg;
+                dvmUnlockMutex(&self->callbackMutex);
+                // Update Thread structure
+                self->interpSave.pc = pc;
+                self->interpSave.fp = fp;
+                if (callback != NULL) {
+                    // Do the callback
+                    if (!callback(self,arg)) {
+                        // disarm
+                        dvmArmSafePointCallback(self, NULL, NULL);
+                    }
+                }
+            }
+            // Need to suspend?
+            if (self->interpBreak.ctl.suspendCount) {
+                dvmExportPC(pc, fp);
+                dvmCheckSuspendPending(self);
+            }
         }
     }
 
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
index 9e059f9..b6919af 100644
--- a/vm/interp/Interp.h
+++ b/vm/interp/Interp.h
@@ -79,7 +79,7 @@
  */
 void dvmUpdateDebugger(const Method* method, const u2* pc, const u4* fp,
                        bool methodEntry, Thread* self);
-void dvmCheckBefore(const u2 *dPC, const u4 *fp, Thread* self);
+void dvmCheckBefore(const u2 *dPC, u4 *fp, Thread* self);
 void dvmReportExceptionThrow(Thread* self, const Method* curMethod,
                              const u2* pc, void* fp);
 void dvmReportPreNativeInvoke(const u2* pc, Thread* self,
@@ -101,6 +101,20 @@
  */
 void dvmUpdateAllInterpBreak(int newBreak, int newMode, bool enable);
 
+/*
+ * Register a callback to occur at the next safe point for a single thread.
+ * If funct is NULL, the previous registration is cancelled.
+ *
+ * The callback prototype is:
+ *        bool funct(Thread* thread, void* arg)
+ *
+ *  If funct returns false, the callback will be disarmed.  If true,
+ *  it will stay in effect.
+ */
+void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
+                             void* arg);
+
+
 #ifndef DVM_NO_ASM_INTERP
 extern void* dvmAsmInstructionStart[];
 extern void* dvmAsmAltInstructionStart[];
diff --git a/vm/interp/InterpState.h b/vm/interp/InterpState.h
index 2294a81..6f86476 100644
--- a/vm/interp/InterpState.h
+++ b/vm/interp/InterpState.h
@@ -71,11 +71,14 @@
     kInterpDebugBreak         = 0x04,
     kInterpEmulatorTraceBreak = 0x08,
     kInterpSingleStep         = 0x10,
+    kInterpSafePointCallback  = 0x20,
 #if defined(WITH_JIT)
-    kInterpJitBreak           = 0x20,
+    kInterpJitBreak           = 0x40,
 #endif
 } InterpBreakFlags;
 
+typedef bool (*SafePointCallback)(struct Thread* thread, void* arg);
+
 /*
  * Identify which break and submode flags should be local
  * to an interpreter activation.