Index: media/base/bind_to_current_loop_unittest.cc |
diff --git a/media/base/bind_to_current_loop_unittest.cc b/media/base/bind_to_current_loop_unittest.cc |
index 23030856675d69052dc3f37aab8eacd08c8614b1..2330df7f540f78782b06996e3786cc82b1a17696 100644 |
--- a/media/base/bind_to_current_loop_unittest.cc |
+++ b/media/base/bind_to_current_loop_unittest.cc |
@@ -4,8 +4,11 @@ |
#include "media/base/bind_to_current_loop.h" |
+#include "base/barrier_closure.h" |
#include "base/message_loop/message_loop.h" |
+#include "base/run_loop.h" |
#include "base/synchronization/waitable_event.h" |
+#include "base/threading/thread.h" |
#include "testing/gtest/include/gtest/gtest.h" |
namespace media { |
@@ -165,4 +168,124 @@ TEST_F(BindToCurrentLoopTest, Integers) { |
EXPECT_EQ(b, -1); |
} |
+namespace { |
+ |
+void ExpectRunOn(scoped_refptr<base::SingleThreadTaskRunner> loop, |
+ const base::Closure& done_closure) { |
+ EXPECT_TRUE(loop->BelongsToCurrentThread()); |
+ done_closure.Run(); |
+} |
+ |
+} // namespace |
+ |
+TEST_F(BindToCurrentLoopTest, CalledOnOriginalThread) { |
+ scoped_refptr<base::SingleThreadTaskRunner> origin_loop = |
+ base::ThreadTaskRunnerHandle::Get(); |
+ ASSERT_TRUE(origin_loop->BelongsToCurrentThread()); |
+ |
+ base::Thread thread("Other thread"); |
+ ASSERT_TRUE(thread.Start()); |
+ scoped_refptr<base::SingleThreadTaskRunner> thread_loop = |
+ thread.task_runner(); |
+ ASSERT_FALSE(thread_loop->BelongsToCurrentThread()); |
+ |
+ base::RunLoop run_loop; |
+ base::Closure barrier_closure = |
+ base::BarrierClosure(2, BindToCurrentLoop(run_loop.QuitClosure())); |
+ |
+ base::Closure thread_cb = base::Bind( |
+ &ExpectRunOn, thread_loop, barrier_closure); |
+ base::Closure origin_cb = BindToCurrentLoop(base::Bind( |
+ &ExpectRunOn, origin_loop, barrier_closure)); |
+ |
+ thread_loop->PostTask(FROM_HERE, thread_cb); |
+ thread_loop->PostTask(FROM_HERE, origin_cb); |
+ |
+ run_loop.Run(); |
+ |
+ thread.Stop(); |
+} |
+ |
+namespace { |
+ |
+class ExpectDeletedOnOrigin { |
+ public: |
+ ExpectDeletedOnOrigin( |
+ scoped_refptr<base::SingleThreadTaskRunner> origin_loop, |
+ const base::Closure& deleted_closure) |
+ : origin_loop_(origin_loop), |
+ deleted_closure_(deleted_closure) {} |
+ |
+ ~ExpectDeletedOnOrigin() { |
+ EXPECT_TRUE(origin_loop_->BelongsToCurrentThread()); |
+ deleted_closure_.Run(); |
+ } |
+ |
+ scoped_refptr<base::SingleThreadTaskRunner> origin_loop_; |
+ base::Closure deleted_closure_; |
+}; |
+ |
+class RefCountedExpectDeletedOnOrigin |
+ : public ExpectDeletedOnOrigin, |
+ public base::RefCountedThreadSafe<RefCountedExpectDeletedOnOrigin> { |
+ public: |
+ RefCountedExpectDeletedOnOrigin( |
+ scoped_refptr<base::SingleThreadTaskRunner> origin_loop, |
+ const base::Closure& deleted_closure) |
+ : ExpectDeletedOnOrigin(origin_loop, deleted_closure) {} |
+ |
+ private: |
+ friend class base::RefCountedThreadSafe<RefCountedExpectDeletedOnOrigin>; |
+ ~RefCountedExpectDeletedOnOrigin() {} |
+}; |
+ |
+void CallbackWithParamsToDelete( |
+ ExpectDeletedOnOrigin* unused_owned, |
+ scoped_ptr<ExpectDeletedOnOrigin> unused_passed, |
+ scoped_refptr<RefCountedExpectDeletedOnOrigin> unused_ref_counted) {} |
+ |
+} // namespace |
+ |
+TEST_F(BindToCurrentLoopTest, CallbackDeletedOnOriginalThread) { |
+ scoped_refptr<base::SingleThreadTaskRunner> origin_loop = |
+ base::ThreadTaskRunnerHandle::Get(); |
+ |
+ base::Thread thread("Other thread"); |
+ ASSERT_TRUE(thread.Start()); |
+ scoped_refptr<base::SingleThreadTaskRunner> thread_loop = |
+ thread.task_runner(); |
+ |
+ base::RunLoop run_loop; |
+ base::Closure barrier_closure = |
+ base::BarrierClosure(3, BindToCurrentLoop(run_loop.QuitClosure())); |
+ |
+ base::Closure* origin_cb_ptr; |
+ { |
+ ExpectDeletedOnOrigin* new_delete_on_origin = |
+ new ExpectDeletedOnOrigin(origin_loop, barrier_closure); |
+ scoped_ptr<ExpectDeletedOnOrigin> scoped_delete_on_origin( |
+ new ExpectDeletedOnOrigin(origin_loop, barrier_closure)); |
+ scoped_refptr<RefCountedExpectDeletedOnOrigin> counted_delete_on_origin = |
+ new RefCountedExpectDeletedOnOrigin(origin_loop, barrier_closure); |
+ origin_cb_ptr = new base::Closure(BindToCurrentLoop(base::Bind( |
+ &CallbackWithParamsToDelete, |
+ base::Owned(new_delete_on_origin), |
+ base::Passed(&scoped_delete_on_origin), |
+ counted_delete_on_origin))); |
+ } |
+ |
+ // Delete the callback returned by BindToCurrentLoop on the other thread. This |
+ // must cause the wrapped callback that was passed in to BindToCurrentLoop to |
+ // be deleted on the origin thread (where BindToCurrentLoop was invoked), and |
+ // hence the parameters of that wrapped callback must also be deleted on the |
+ // origin thread. |
+ thread_loop->DeleteSoon(FROM_HERE, origin_cb_ptr); |
+ |
+ // If this test times out, that means one of the ExpectDeletedOnOrigin or |
+ // RefCountedExpectDeletedOnOrigin objects failed to be deleted (bad). |
+ run_loop.Run(); |
+ |
+ thread.Stop(); |
+} |
+ |
} // namespace media |