| 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 | 
|  |