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);
+ }
}
}