Implement ClosureThread
ClosureThread runs a Closure in a platform thread. Chromium Thread class involves message loop, so we choose to implement our own based on the lower level SimpleThread which wraps PlatformThread. Change-Id: Ibb759f7debcc027c0764195ca93506f9a202110b
This commit is contained in:
parent
2c8418fd22
commit
3f8b37a377
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "media/base/closure_thread.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
ClosureThread::ClosureThread(
|
||||
const std::string& name_prefix,
|
||||
const base::Closure& task)
|
||||
: base::SimpleThread(name_prefix), task_(task) {}
|
||||
|
||||
ClosureThread::~ClosureThread() {
|
||||
if (!HasBeenJoined())
|
||||
Join();
|
||||
}
|
||||
|
||||
void ClosureThread::Run() { task_.Run(); }
|
||||
|
||||
} // namespace media
|
|
@ -0,0 +1,47 @@
|
|||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#ifndef MEDIA_BASE_CLOSURE_THREAD_H_
|
||||
#define MEDIA_BASE_CLOSURE_THREAD_H_
|
||||
|
||||
#include "base/callback.h"
|
||||
#include "base/threading/simple_thread.h"
|
||||
|
||||
namespace media {
|
||||
|
||||
/// Class for creating a thread which invokes a closure.
|
||||
/// Start() starts the thread and invokes the given closure inside the thread.
|
||||
///
|
||||
/// NOTE: It is invalid to destroy a ClosureThread without Start() having been
|
||||
/// called (and a thread never created).
|
||||
///
|
||||
/// Thread Safety: A ClosureThread is not completely thread safe. It is safe to
|
||||
/// access it from the creating thread or from the newly created thread. This
|
||||
/// implies that the creator thread should be the thread that calls Join.
|
||||
class ClosureThread : public base::SimpleThread {
|
||||
public:
|
||||
/// Create a ClosureThread. The thread will not be created until Start() is
|
||||
/// called.
|
||||
/// @param name_prefix is the thread name prefix. Every thread has a name,
|
||||
/// in the form of @a name_prefix/TID, for example "my_thread/321".
|
||||
/// @param task is the Closure to run in the thread.
|
||||
explicit ClosureThread(const std::string& name_prefix,
|
||||
const base::Closure& task);
|
||||
|
||||
/// The destructor calls Join automatically if it is not yet joined.
|
||||
virtual ~ClosureThread();
|
||||
|
||||
protected:
|
||||
/// SimpleThread implementation overrides.
|
||||
virtual void Run() OVERRIDE;
|
||||
|
||||
private:
|
||||
const base::Closure task_;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
||||
#endif // MEDIA_BASE_CLOSURE_THREAD_H_
|
|
@ -0,0 +1,120 @@
|
|||
// Copyright 2014 Google Inc. All rights reserved.
|
||||
//
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file or at
|
||||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "media/base/closure_thread.h"
|
||||
|
||||
#include "base/bind.h"
|
||||
#include "base/memory/scoped_ptr.h"
|
||||
#include "base/synchronization/waitable_event.h"
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
using ::testing::DoAll;
|
||||
using ::testing::Invoke;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace {
|
||||
|
||||
const char kThreadNamePrefix[] = "TestClosureThread";
|
||||
|
||||
// Mock operation for testing.
|
||||
class MockOperation {
|
||||
public:
|
||||
MOCK_METHOD0(DoSomething, bool());
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace media {
|
||||
|
||||
class ClosureThreadTest : public ::testing::Test {
|
||||
public:
|
||||
ClosureThreadTest()
|
||||
: thread_(
|
||||
new ClosureThread(kThreadNamePrefix,
|
||||
base::Bind(&ClosureThreadTest::ClosureCallback,
|
||||
base::Unretained(this)))),
|
||||
val_(0) {}
|
||||
|
||||
virtual ~ClosureThreadTest() {}
|
||||
|
||||
void ClosureCallback() {
|
||||
// Exit the loop if DoSomething return false.
|
||||
while (operation_.DoSomething())
|
||||
continue;
|
||||
}
|
||||
|
||||
void IncrementVal() { ++val_; }
|
||||
|
||||
protected:
|
||||
int val() { return val_; }
|
||||
void set_val(int val) { val_ = val; }
|
||||
|
||||
MockOperation operation_;
|
||||
scoped_ptr<ClosureThread> thread_;
|
||||
|
||||
private:
|
||||
int val_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(ClosureThreadTest);
|
||||
};
|
||||
|
||||
TEST_F(ClosureThreadTest, Basic) {
|
||||
EXPECT_CALL(operation_, DoSomething()).WillOnce(Return(false));
|
||||
|
||||
ASSERT_EQ(kThreadNamePrefix, thread_->name_prefix());
|
||||
ASSERT_FALSE(thread_->HasBeenStarted());
|
||||
thread_->Start();
|
||||
ASSERT_TRUE(thread_->HasBeenStarted());
|
||||
thread_->Join();
|
||||
}
|
||||
|
||||
TEST_F(ClosureThreadTest, CheckInteraction) {
|
||||
base::WaitableEvent event_in_thread(true, false);
|
||||
base::WaitableEvent event_in_main(true, false);
|
||||
set_val(8);
|
||||
|
||||
// Expect the operation to be invoked twice:
|
||||
// 1) When invoked for the first time, increment the value then wait
|
||||
// for the signal from main event and return true to continue;
|
||||
// 2) When invoked for the second time, increment the value again and
|
||||
// return true to quit the closure loop.
|
||||
EXPECT_CALL(operation_, DoSomething())
|
||||
.WillOnce(DoAll(Invoke(this, &ClosureThreadTest::IncrementVal),
|
||||
Invoke(&event_in_main, &base::WaitableEvent::Signal),
|
||||
Invoke(&event_in_thread, &base::WaitableEvent::Wait),
|
||||
Return(true)))
|
||||
.WillOnce(DoAll(Invoke(this, &ClosureThreadTest::IncrementVal),
|
||||
Return(false)));
|
||||
|
||||
thread_->Start();
|
||||
|
||||
// Wait until |thread_| signals the main thread.
|
||||
event_in_main.Wait();
|
||||
EXPECT_EQ(9, val());
|
||||
|
||||
// Signal |thread_| to continue.
|
||||
event_in_thread.Signal();
|
||||
thread_->Join();
|
||||
EXPECT_EQ(10, val());
|
||||
}
|
||||
|
||||
TEST_F(ClosureThreadTest, NotJoined) {
|
||||
EXPECT_CALL(operation_, DoSomething()).WillOnce(Return(false));
|
||||
|
||||
thread_->Start();
|
||||
// Destroy the thread. The thread should be joined automatically.
|
||||
thread_.reset();
|
||||
}
|
||||
|
||||
// Expect death if the thread is destroyed without being started.
|
||||
TEST_F(ClosureThreadTest, NotStarted) {
|
||||
ASSERT_FALSE(thread_->HasBeenStarted());
|
||||
ClosureThread* thread = thread_.release();
|
||||
EXPECT_DEBUG_DEATH(delete thread, ".*Check failed: HasBeenStarted.*");
|
||||
}
|
||||
|
||||
} // namespace media
|
|
@ -63,6 +63,8 @@
|
|||
'buffer_writer.h',
|
||||
'byte_queue.cc',
|
||||
'byte_queue.h',
|
||||
'closure_thread.cc',
|
||||
'closure_thread.h',
|
||||
'container_names.cc',
|
||||
'container_names.h',
|
||||
'demuxer.cc',
|
||||
|
@ -110,6 +112,7 @@
|
|||
'aes_encryptor_unittest.cc',
|
||||
'bit_reader_unittest.cc',
|
||||
'buffer_writer_unittest.cc',
|
||||
'closure_thread_unittest.cc',
|
||||
'container_names_unittest.cc',
|
||||
'fake_prng.cc', # For rsa_key_unittest
|
||||
'fake_prng.h', # For rsa_key_unittest
|
||||
|
|
Loading…
Reference in New Issue