Skip to content

Commit c53abc4

Browse files
author
Jeff Brown
committed
Run with scissors.
Add a useful (if somewhat dangerous) method which will help replace similarly dangerous code patterns in a few different places. Change-Id: If1295f7ab9652c906ce718d94eb7914d143e1939
1 parent 2c577f1 commit c53abc4

File tree

1 file changed

+81
-0
lines changed

1 file changed

+81
-0
lines changed

core/java/android/os/Handler.java

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,50 @@ public final boolean postAtFrontOfQueue(Runnable r)
411411
return sendMessageAtFrontOfQueue(getPostMessage(r));
412412
}
413413

414+
/**
415+
* Runs the specified task synchronously.
416+
*
417+
* If the current thread is the same as the handler thread, then the runnable
418+
* runs immediately without being enqueued. Otherwise, posts the runnable
419+
* to the handler and waits for it to complete before returning.
420+
*
421+
* This method is dangerous! Improper use can result in deadlocks.
422+
* Never call this method while any locks are held or use it in a
423+
* possibly re-entrant manner.
424+
*
425+
* This method is occasionally useful in situations where a background thread
426+
* must synchronously await completion of a task that must run on the
427+
* handler's thread. However, this problem is often a symptom of bad design.
428+
* Consider improving the design (if possible) before resorting to this method.
429+
*
430+
* One example of where you might want to use this method is when you just
431+
* set up a Handler thread and need to perform some initialization steps on
432+
* it before continuing execution.
433+
*
434+
* @param r The Runnable that will be executed synchronously.
435+
*
436+
* @return Returns true if the Runnable was successfully executed.
437+
* Returns false on failure, usually because the
438+
* looper processing the message queue is exiting.
439+
*
440+
* @hide This method is prone to abuse and should probably not be in the API.
441+
* If we ever do make it part of the API, we might want to rename it to something
442+
* less funny like runUnsafe().
443+
*/
444+
public final boolean runWithScissors(final Runnable r) {
445+
if (r == null) {
446+
throw new IllegalArgumentException("runnable must not be null");
447+
}
448+
449+
if (Looper.myLooper() == mLooper) {
450+
r.run();
451+
return true;
452+
}
453+
454+
BlockingRunnable br = new BlockingRunnable(r);
455+
return br.postAndWait(this);
456+
}
457+
414458
/**
415459
* Remove any pending posts of Runnable r that are in the message queue.
416460
*/
@@ -678,4 +722,41 @@ private static void handleCallback(Message message) {
678722
final Callback mCallback;
679723
final boolean mAsynchronous;
680724
IMessenger mMessenger;
725+
726+
private static final class BlockingRunnable implements Runnable {
727+
private final Runnable mTask;
728+
private boolean mDone;
729+
730+
public BlockingRunnable(Runnable task) {
731+
mTask = task;
732+
}
733+
734+
@Override
735+
public void run() {
736+
try {
737+
mTask.run();
738+
} finally {
739+
synchronized (this) {
740+
mDone = true;
741+
notifyAll();
742+
}
743+
}
744+
}
745+
746+
public boolean postAndWait(Handler handler) {
747+
if (!handler.post(this)) {
748+
return false;
749+
}
750+
751+
synchronized (this) {
752+
while (!mDone) {
753+
try {
754+
wait();
755+
} catch (InterruptedException ex) {
756+
}
757+
}
758+
}
759+
return true;
760+
}
761+
}
681762
}

0 commit comments

Comments
 (0)