cef/cef1/libcef/cef_thread.cc

229 lines
6.6 KiB
C++
Raw Normal View History

// Copyright (c) 2010 The Chromium Embedded Framework Authors.
// Portions copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "libcef/cef_thread.h"
#include "base/compiler_specific.h"
#include "base/message_loop.h"
#include "base/message_loop_proxy.h"
#if defined(OS_WIN)
#include <Objbase.h> // NOLINT(build/include_order)
#endif
using base::MessageLoopProxy;
// Friendly names for the well-known threads.
static const char* cef_thread_names[CefThread::ID_COUNT] = {
"Cef_UIThread", // UI
"Cef_FileThread", // FILE
"Cef_IOThread", // IO
};
// An implementation of MessageLoopProxy to be used in conjunction
// with CefThread.
class CefThreadMessageLoopProxy : public MessageLoopProxy {
public:
explicit CefThreadMessageLoopProxy(CefThread::ID identifier)
: id_(identifier) {
}
// TaskRunner implementation.
virtual bool PostDelayedTask(const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) OVERRIDE {
return CefThread::PostDelayedTask(id_, from_here, task, delay);
}
// SequencedTaskRunner implementation.
virtual bool PostNonNestableDelayedTask(
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) OVERRIDE {
return CefThread::PostNonNestableDelayedTask(id_, from_here, task, delay);
}
virtual bool RunsTasksOnCurrentThread() const OVERRIDE {
return CefThread::CurrentlyOn(id_);
}
private:
CefThread::ID id_;
DISALLOW_COPY_AND_ASSIGN(CefThreadMessageLoopProxy);
};
base::Lock CefThread::lock_;
CefThread* CefThread::cef_threads_[ID_COUNT];
CefThread::CefThread(CefThread::ID identifier)
: Thread(cef_thread_names[identifier]),
identifier_(identifier) {
Initialize();
}
CefThread::CefThread(ID identifier, MessageLoop* message_loop)
: Thread(cef_thread_names[identifier]),
identifier_(identifier) {
message_loop->set_thread_name(cef_thread_names[identifier]);
set_message_loop(message_loop);
Initialize();
}
void CefThread::Init() {
#if defined(OS_WIN)
// Initializes the COM library on the current thread.
CoInitialize(NULL);
#endif
#if defined(OS_MACOSX)
autorelease_pool_.reset(new base::mac::ScopedNSAutoreleasePool);
#endif
}
void CefThread::Cleanup() {
#if defined(OS_WIN)
// Closes the COM library on the current thread. CoInitialize must
// be balanced by a corresponding call to CoUninitialize.
CoUninitialize();
#endif
#if defined(OS_MACOSX)
autorelease_pool_.reset(NULL);
#endif
}
void CefThread::Initialize() {
base::AutoLock lock(lock_);
DCHECK(identifier_ >= 0 && identifier_ < ID_COUNT);
DCHECK(cef_threads_[identifier_] == NULL);
cef_threads_[identifier_] = this;
}
CefThread::~CefThread() {
// Stop the thread here, instead of the parent's class destructor. This is so
// that if there are pending tasks that run, code that checks that it's on the
// correct CefThread succeeds.
Stop();
base::AutoLock lock(lock_);
cef_threads_[identifier_] = NULL;
#ifndef NDEBUG
// Double check that the threads are ordererd correctly in the enumeration.
for (int i = identifier_ + 1; i < ID_COUNT; ++i) {
DCHECK(!cef_threads_[i]) <<
"Threads must be listed in the reverse order that they die";
}
#endif
}
// static
bool CefThread::IsWellKnownThread(ID identifier) {
base::AutoLock lock(lock_);
return (identifier >= 0 && identifier < ID_COUNT &&
cef_threads_[identifier]);
}
// static
bool CefThread::CurrentlyOn(ID identifier) {
base::AutoLock lock(lock_);
DCHECK(identifier >= 0 && identifier < ID_COUNT);
return cef_threads_[identifier] &&
cef_threads_[identifier]->message_loop() == MessageLoop::current();
}
// static
bool CefThread::PostTask(ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task) {
return PostTaskHelper(identifier, from_here, task, base::TimeDelta(), true);
}
// static
bool CefThread::PostDelayedTask(ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
return PostTaskHelper(identifier, from_here, task, delay, true);
}
// static
bool CefThread::PostNonNestableTask(
ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task) {
return PostTaskHelper(identifier, from_here, task, base::TimeDelta(), false);
}
// static
bool CefThread::PostNonNestableDelayedTask(
ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay) {
return PostTaskHelper(identifier, from_here, task, delay, false);
}
// static
bool CefThread::GetCurrentThreadIdentifier(ID* identifier) {
MessageLoop* cur_message_loop = MessageLoop::current();
for (int i = 0; i < ID_COUNT; ++i) {
if (cef_threads_[i] &&
cef_threads_[i]->message_loop() == cur_message_loop) {
*identifier = cef_threads_[i]->identifier_;
return true;
}
}
return false;
}
// static
scoped_refptr<MessageLoopProxy> CefThread::GetMessageLoopProxyForThread(
ID identifier) {
scoped_refptr<MessageLoopProxy> proxy =
new CefThreadMessageLoopProxy(identifier);
return proxy;
}
// static
bool CefThread::PostTaskHelper(
ID identifier,
const tracked_objects::Location& from_here,
const base::Closure& task,
base::TimeDelta delay,
bool nestable) {
DCHECK(identifier >= 0 && identifier < ID_COUNT);
// Optimization: to avoid unnecessary locks, we listed the ID enumeration in
// order of lifetime. So no need to lock if we know that the other thread
// outlives this one.
// Note: since the array is so small, ok to loop instead of creating a map,
// which would require a lock because std::map isn't thread safe, defeating
// the whole purpose of this optimization.
ID current_thread;
bool guaranteed_to_outlive_target_thread =
GetCurrentThreadIdentifier(&current_thread) &&
current_thread >= identifier;
if (!guaranteed_to_outlive_target_thread)
lock_.Acquire();
MessageLoop* message_loop = cef_threads_[identifier] ?
cef_threads_[identifier]->message_loop() : NULL;
if (message_loop) {
if (nestable) {
message_loop->PostDelayedTask(from_here, task, delay);
} else {
message_loop->PostNonNestableDelayedTask(from_here, task, delay);
}
}
if (!guaranteed_to_outlive_target_thread)
lock_.Release();
return !!message_loop;
}