summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlex Vakulenko <avakulenko@google.com>2016-01-25 21:02:07 +0000
committerandroid-build-merger <android-build-merger@google.com>2016-01-25 21:02:07 +0000
commit7555b4a0e682456898a7d628286978b5fc5de3e4 (patch)
tree024c351584392fb462e4352e4cafeec57e033b0f
parent6f0e280039b67bb2c90ef7ea80252c63744be324 (diff)
parent1ac3a5e3e15a0a3db48c4cb13736ce38033831fe (diff)
downloadlibchrome-7555b4a0e682456898a7d628286978b5fc5de3e4.tar.gz
libchrome: Add files needed by some veyron variants on Chrome OS
am: 1ac3a5e3e1 * commit '1ac3a5e3e15a0a3db48c4cb13736ce38033831fe': libchrome: Add files needed by some veyron variants on Chrome OS
-rw-r--r--SConstruct4
-rw-r--r--base/files/memory_mapped_file.cc95
-rw-r--r--base/files/memory_mapped_file_posix.cc91
-rw-r--r--base/posix/global_descriptors.cc85
-rw-r--r--base/strings/nullable_string16.cc17
-rw-r--r--base/strings/nullable_string16.h46
-rw-r--r--base/strings/nullable_string16_unittest.cc35
-rw-r--r--base/synchronization/waitable_event_watcher_posix.cc270
8 files changed, 643 insertions, 0 deletions
diff --git a/SConstruct b/SConstruct
index b316900289..5e003905e9 100644
--- a/SConstruct
+++ b/SConstruct
@@ -57,6 +57,8 @@ base_libs = [
files/file_util_linux.cc
files/file_util_posix.cc
files/important_file_writer.cc
+ files/memory_mapped_file.cc
+ files/memory_mapped_file_posix.cc
files/scoped_file.cc
files/scoped_temp_dir.cc
guid.cc
@@ -97,6 +99,7 @@ base_libs = [
pending_task.cc
pickle.cc
posix/file_descriptor_shuffle.cc
+ posix/global_descriptors.cc
posix/safe_strerror.cc
posix/unix_domain_socket_linux.cc
process/internal_linux.cc
@@ -139,6 +142,7 @@ base_libs = [
synchronization/lock.cc
synchronization/lock_impl_posix.cc
synchronization/waitable_event_posix.cc
+ synchronization/waitable_event_watcher_posix.cc
sync_socket_posix.cc
sys_info.cc
sys_info_chromeos.cc
diff --git a/base/files/memory_mapped_file.cc b/base/files/memory_mapped_file.cc
new file mode 100644
index 0000000000..0fd9d6796b
--- /dev/null
+++ b/base/files/memory_mapped_file.cc
@@ -0,0 +1,95 @@
+// Copyright 2013 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 "base/files/memory_mapped_file.h"
+
+#include <utility>
+
+#include "base/files/file_path.h"
+#include "base/logging.h"
+#include "base/sys_info.h"
+#include "build/build_config.h"
+
+namespace base {
+
+const MemoryMappedFile::Region MemoryMappedFile::Region::kWholeFile = {0, 0};
+
+bool MemoryMappedFile::Region::operator==(
+ const MemoryMappedFile::Region& other) const {
+ return other.offset == offset && other.size == size;
+}
+
+bool MemoryMappedFile::Region::operator!=(
+ const MemoryMappedFile::Region& other) const {
+ return other.offset != offset || other.size != size;
+}
+
+MemoryMappedFile::~MemoryMappedFile() {
+ CloseHandles();
+}
+
+#if !defined(OS_NACL)
+bool MemoryMappedFile::Initialize(const FilePath& file_name) {
+ if (IsValid())
+ return false;
+
+ file_.Initialize(file_name, File::FLAG_OPEN | File::FLAG_READ);
+
+ if (!file_.IsValid()) {
+ DLOG(ERROR) << "Couldn't open " << file_name.AsUTF8Unsafe();
+ return false;
+ }
+
+ if (!MapFileRegionToMemory(Region::kWholeFile)) {
+ CloseHandles();
+ return false;
+ }
+
+ return true;
+}
+
+bool MemoryMappedFile::Initialize(File file) {
+ return Initialize(std::move(file), Region::kWholeFile);
+}
+
+bool MemoryMappedFile::Initialize(File file, const Region& region) {
+ if (IsValid())
+ return false;
+
+ if (region != Region::kWholeFile) {
+ DCHECK_GE(region.offset, 0);
+ DCHECK_GT(region.size, 0);
+ }
+
+ file_ = std::move(file);
+
+ if (!MapFileRegionToMemory(region)) {
+ CloseHandles();
+ return false;
+ }
+
+ return true;
+}
+
+bool MemoryMappedFile::IsValid() const {
+ return data_ != NULL;
+}
+
+// static
+void MemoryMappedFile::CalculateVMAlignedBoundaries(int64_t start,
+ int64_t size,
+ int64_t* aligned_start,
+ int64_t* aligned_size,
+ int32_t* offset) {
+ // Sadly, on Windows, the mmap alignment is not just equal to the page size.
+ const int64_t mask =
+ static_cast<int64_t>(SysInfo::VMAllocationGranularity()) - 1;
+ DCHECK_LT(mask, std::numeric_limits<int32_t>::max());
+ *offset = start & mask;
+ *aligned_start = start & ~mask;
+ *aligned_size = (size + *offset + mask) & ~mask;
+}
+#endif
+
+} // namespace base
diff --git a/base/files/memory_mapped_file_posix.cc b/base/files/memory_mapped_file_posix.cc
new file mode 100644
index 0000000000..1067fdc9ac
--- /dev/null
+++ b/base/files/memory_mapped_file_posix.cc
@@ -0,0 +1,91 @@
+// Copyright 2013 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 "base/files/memory_mapped_file.h"
+
+#include <stddef.h>
+#include <stdint.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "base/logging.h"
+#include "base/threading/thread_restrictions.h"
+#include "build/build_config.h"
+
+namespace base {
+
+MemoryMappedFile::MemoryMappedFile() : data_(NULL), length_(0) {
+}
+
+#if !defined(OS_NACL)
+bool MemoryMappedFile::MapFileRegionToMemory(
+ const MemoryMappedFile::Region& region) {
+ ThreadRestrictions::AssertIOAllowed();
+
+ off_t map_start = 0;
+ size_t map_size = 0;
+ int32_t data_offset = 0;
+
+ if (region == MemoryMappedFile::Region::kWholeFile) {
+ int64_t file_len = file_.GetLength();
+ if (file_len == -1) {
+ DPLOG(ERROR) << "fstat " << file_.GetPlatformFile();
+ return false;
+ }
+ map_size = static_cast<size_t>(file_len);
+ length_ = map_size;
+ } else {
+ // The region can be arbitrarily aligned. mmap, instead, requires both the
+ // start and size to be page-aligned. Hence, we map here the page-aligned
+ // outer region [|aligned_start|, |aligned_start| + |size|] which contains
+ // |region| and then add up the |data_offset| displacement.
+ int64_t aligned_start = 0;
+ int64_t aligned_size = 0;
+ CalculateVMAlignedBoundaries(region.offset,
+ region.size,
+ &aligned_start,
+ &aligned_size,
+ &data_offset);
+
+ // Ensure that the casts in the mmap call below are sane.
+ if (aligned_start < 0 || aligned_size < 0 ||
+ aligned_start > std::numeric_limits<off_t>::max() ||
+ static_cast<uint64_t>(aligned_size) >
+ std::numeric_limits<size_t>::max() ||
+ static_cast<uint64_t>(region.size) >
+ std::numeric_limits<size_t>::max()) {
+ DLOG(ERROR) << "Region bounds are not valid for mmap";
+ return false;
+ }
+
+ map_start = static_cast<off_t>(aligned_start);
+ map_size = static_cast<size_t>(aligned_size);
+ length_ = static_cast<size_t>(region.size);
+ }
+
+ data_ = static_cast<uint8_t*>(mmap(NULL, map_size, PROT_READ, MAP_SHARED,
+ file_.GetPlatformFile(), map_start));
+ if (data_ == MAP_FAILED) {
+ DPLOG(ERROR) << "mmap " << file_.GetPlatformFile();
+ return false;
+ }
+
+ data_ += data_offset;
+ return true;
+}
+#endif
+
+void MemoryMappedFile::CloseHandles() {
+ ThreadRestrictions::AssertIOAllowed();
+
+ if (data_ != NULL)
+ munmap(data_, length_);
+ file_.Close();
+
+ data_ = NULL;
+ length_ = 0;
+}
+
+} // namespace base
diff --git a/base/posix/global_descriptors.cc b/base/posix/global_descriptors.cc
new file mode 100644
index 0000000000..6c187838ad
--- /dev/null
+++ b/base/posix/global_descriptors.cc
@@ -0,0 +1,85 @@
+// Copyright (c) 2012 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 "base/posix/global_descriptors.h"
+
+#include <vector>
+#include <utility>
+
+#include "base/logging.h"
+
+namespace base {
+
+GlobalDescriptors::Descriptor::Descriptor(Key key, int fd)
+ : key(key), fd(fd), region(base::MemoryMappedFile::Region::kWholeFile) {
+}
+
+GlobalDescriptors::Descriptor::Descriptor(Key key,
+ int fd,
+ base::MemoryMappedFile::Region region)
+ : key(key), fd(fd), region(region) {
+}
+
+// static
+GlobalDescriptors* GlobalDescriptors::GetInstance() {
+ typedef Singleton<base::GlobalDescriptors,
+ LeakySingletonTraits<base::GlobalDescriptors> >
+ GlobalDescriptorsSingleton;
+ return GlobalDescriptorsSingleton::get();
+}
+
+int GlobalDescriptors::Get(Key key) const {
+ const int ret = MaybeGet(key);
+
+ if (ret == -1)
+ DLOG(FATAL) << "Unknown global descriptor: " << key;
+ return ret;
+}
+
+int GlobalDescriptors::MaybeGet(Key key) const {
+ for (Mapping::const_iterator
+ i = descriptors_.begin(); i != descriptors_.end(); ++i) {
+ if (i->key == key)
+ return i->fd;
+ }
+
+ return -1;
+}
+
+void GlobalDescriptors::Set(Key key, int fd) {
+ Set(key, fd, base::MemoryMappedFile::Region::kWholeFile);
+}
+
+void GlobalDescriptors::Set(Key key,
+ int fd,
+ base::MemoryMappedFile::Region region) {
+ for (auto& i : descriptors_) {
+ if (i.key == key) {
+ i.fd = fd;
+ i.region = region;
+ return;
+ }
+ }
+
+ descriptors_.push_back(Descriptor(key, fd, region));
+}
+
+base::MemoryMappedFile::Region GlobalDescriptors::GetRegion(Key key) const {
+ for (const auto& i : descriptors_) {
+ if (i.key == key)
+ return i.region;
+ }
+ DLOG(FATAL) << "Unknown global descriptor: " << key;
+ return base::MemoryMappedFile::Region::kWholeFile;
+}
+
+void GlobalDescriptors::Reset(const Mapping& mapping) {
+ descriptors_ = mapping;
+}
+
+GlobalDescriptors::GlobalDescriptors() {}
+
+GlobalDescriptors::~GlobalDescriptors() {}
+
+} // namespace base
diff --git a/base/strings/nullable_string16.cc b/base/strings/nullable_string16.cc
new file mode 100644
index 0000000000..07f81d4339
--- /dev/null
+++ b/base/strings/nullable_string16.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2013 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 "base/strings/nullable_string16.h"
+
+#include <ostream>
+
+#include "base/strings/utf_string_conversions.h"
+
+namespace base {
+
+std::ostream& operator<<(std::ostream& out, const NullableString16& value) {
+ return value.is_null() ? out << "(null)" : out << UTF16ToUTF8(value.string());
+}
+
+} // namespace base
diff --git a/base/strings/nullable_string16.h b/base/strings/nullable_string16.h
new file mode 100644
index 0000000000..016c25c250
--- /dev/null
+++ b/base/strings/nullable_string16.h
@@ -0,0 +1,46 @@
+// 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.
+
+#ifndef BASE_STRINGS_NULLABLE_STRING16_H_
+#define BASE_STRINGS_NULLABLE_STRING16_H_
+
+#include <iosfwd>
+
+#include "base/base_export.h"
+#include "base/strings/string16.h"
+
+namespace base {
+
+// This class is a simple wrapper for string16 which also contains a null
+// state. This should be used only where the difference between null and
+// empty is meaningful.
+class NullableString16 {
+ public:
+ NullableString16() : is_null_(true) { }
+ NullableString16(const string16& string, bool is_null)
+ : string_(string), is_null_(is_null) {
+ }
+
+ const string16& string() const { return string_; }
+ bool is_null() const { return is_null_; }
+
+ private:
+ string16 string_;
+ bool is_null_;
+};
+
+inline bool operator==(const NullableString16& a, const NullableString16& b) {
+ return a.is_null() == b.is_null() && a.string() == b.string();
+}
+
+inline bool operator!=(const NullableString16& a, const NullableString16& b) {
+ return !(a == b);
+}
+
+BASE_EXPORT std::ostream& operator<<(std::ostream& out,
+ const NullableString16& value);
+
+} // namespace base
+
+#endif // BASE_STRINGS_NULLABLE_STRING16_H_
diff --git a/base/strings/nullable_string16_unittest.cc b/base/strings/nullable_string16_unittest.cc
new file mode 100644
index 0000000000..f02fdcebfe
--- /dev/null
+++ b/base/strings/nullable_string16_unittest.cc
@@ -0,0 +1,35 @@
+// Copyright 2013 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 "base/strings/nullable_string16.h"
+#include "base/strings/utf_string_conversions.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace base {
+
+TEST(NullableString16Test, DefaultConstructor) {
+ NullableString16 s;
+ EXPECT_TRUE(s.is_null());
+ EXPECT_EQ(string16(), s.string());
+}
+
+TEST(NullableString16Test, Equals) {
+ NullableString16 a(ASCIIToUTF16("hello"), false);
+ NullableString16 b(ASCIIToUTF16("hello"), false);
+ EXPECT_EQ(a, b);
+}
+
+TEST(NullableString16Test, NotEquals) {
+ NullableString16 a(ASCIIToUTF16("hello"), false);
+ NullableString16 b(ASCIIToUTF16("world"), false);
+ EXPECT_NE(a, b);
+}
+
+TEST(NullableString16Test, NotEqualsNull) {
+ NullableString16 a(ASCIIToUTF16("hello"), false);
+ NullableString16 b;
+ EXPECT_NE(a, b);
+}
+
+} // namespace base
diff --git a/base/synchronization/waitable_event_watcher_posix.cc b/base/synchronization/waitable_event_watcher_posix.cc
new file mode 100644
index 0000000000..aa425f2550
--- /dev/null
+++ b/base/synchronization/waitable_event_watcher_posix.cc
@@ -0,0 +1,270 @@
+// Copyright (c) 2012 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 "base/synchronization/waitable_event_watcher.h"
+
+#include "base/bind.h"
+#include "base/location.h"
+#include "base/macros.h"
+#include "base/single_thread_task_runner.h"
+#include "base/synchronization/lock.h"
+#include "base/synchronization/waitable_event.h"
+
+namespace base {
+
+// -----------------------------------------------------------------------------
+// WaitableEventWatcher (async waits).
+//
+// The basic design is that we add an AsyncWaiter to the wait-list of the event.
+// That AsyncWaiter has a pointer to MessageLoop, and a Task to be posted to it.
+// The MessageLoop ends up running the task, which calls the delegate.
+//
+// Since the wait can be canceled, we have a thread-safe Flag object which is
+// set when the wait has been canceled. At each stage in the above, we check the
+// flag before going onto the next stage. Since the wait may only be canceled in
+// the MessageLoop which runs the Task, we are assured that the delegate cannot
+// be called after canceling...
+
+// -----------------------------------------------------------------------------
+// A thread-safe, reference-counted, write-once flag.
+// -----------------------------------------------------------------------------
+class Flag : public RefCountedThreadSafe<Flag> {
+ public:
+ Flag() { flag_ = false; }
+
+ void Set() {
+ AutoLock locked(lock_);
+ flag_ = true;
+ }
+
+ bool value() const {
+ AutoLock locked(lock_);
+ return flag_;
+ }
+
+ private:
+ friend class RefCountedThreadSafe<Flag>;
+ ~Flag() {}
+
+ mutable Lock lock_;
+ bool flag_;
+
+ DISALLOW_COPY_AND_ASSIGN(Flag);
+};
+
+// -----------------------------------------------------------------------------
+// This is an asynchronous waiter which posts a task to a MessageLoop when
+// fired. An AsyncWaiter may only be in a single wait-list.
+// -----------------------------------------------------------------------------
+class AsyncWaiter : public WaitableEvent::Waiter {
+ public:
+ AsyncWaiter(MessageLoop* message_loop,
+ const base::Closure& callback,
+ Flag* flag)
+ : message_loop_(message_loop),
+ callback_(callback),
+ flag_(flag) { }
+
+ bool Fire(WaitableEvent* event) override {
+ // Post the callback if we haven't been cancelled.
+ if (!flag_->value()) {
+ message_loop_->task_runner()->PostTask(FROM_HERE, callback_);
+ }
+
+ // We are removed from the wait-list by the WaitableEvent itself. It only
+ // remains to delete ourselves.
+ delete this;
+
+ // We can always return true because an AsyncWaiter is never in two
+ // different wait-lists at the same time.
+ return true;
+ }
+
+ // See StopWatching for discussion
+ bool Compare(void* tag) override { return tag == flag_.get(); }
+
+ private:
+ MessageLoop *const message_loop_;
+ base::Closure callback_;
+ scoped_refptr<Flag> flag_;
+};
+
+// -----------------------------------------------------------------------------
+// For async waits we need to make a callback in a MessageLoop thread. We do
+// this by posting a callback, which calls the delegate and keeps track of when
+// the event is canceled.
+// -----------------------------------------------------------------------------
+void AsyncCallbackHelper(Flag* flag,
+ const WaitableEventWatcher::EventCallback& callback,
+ WaitableEvent* event) {
+ // Runs in MessageLoop thread.
+ if (!flag->value()) {
+ // This is to let the WaitableEventWatcher know that the event has occured
+ // because it needs to be able to return NULL from GetWatchedObject
+ flag->Set();
+ callback.Run(event);
+ }
+}
+
+WaitableEventWatcher::WaitableEventWatcher()
+ : message_loop_(NULL),
+ cancel_flag_(NULL),
+ waiter_(NULL),
+ event_(NULL) {
+}
+
+WaitableEventWatcher::~WaitableEventWatcher() {
+ StopWatching();
+}
+
+// -----------------------------------------------------------------------------
+// The Handle is how the user cancels a wait. After deleting the Handle we
+// insure that the delegate cannot be called.
+// -----------------------------------------------------------------------------
+bool WaitableEventWatcher::StartWatching(
+ WaitableEvent* event,
+ const EventCallback& callback) {
+ MessageLoop *const current_ml = MessageLoop::current();
+ DCHECK(current_ml) << "Cannot create WaitableEventWatcher without a "
+ "current MessageLoop";
+
+ // A user may call StartWatching from within the callback function. In this
+ // case, we won't know that we have finished watching, expect that the Flag
+ // will have been set in AsyncCallbackHelper().
+ if (cancel_flag_.get() && cancel_flag_->value()) {
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
+
+ cancel_flag_ = NULL;
+ }
+
+ DCHECK(!cancel_flag_.get()) << "StartWatching called while still watching";
+
+ cancel_flag_ = new Flag;
+ callback_ = callback;
+ internal_callback_ =
+ base::Bind(&AsyncCallbackHelper, cancel_flag_, callback_, event);
+ WaitableEvent::WaitableEventKernel* kernel = event->kernel_.get();
+
+ AutoLock locked(kernel->lock_);
+
+ event_ = event;
+
+ if (kernel->signaled_) {
+ if (!kernel->manual_reset_)
+ kernel->signaled_ = false;
+
+ // No hairpinning - we can't call the delegate directly here. We have to
+ // enqueue a task on the MessageLoop as normal.
+ current_ml->task_runner()->PostTask(FROM_HERE, internal_callback_);
+ return true;
+ }
+
+ message_loop_ = current_ml;
+ current_ml->AddDestructionObserver(this);
+
+ kernel_ = kernel;
+ waiter_ = new AsyncWaiter(current_ml, internal_callback_, cancel_flag_.get());
+ event->Enqueue(waiter_);
+
+ return true;
+}
+
+void WaitableEventWatcher::StopWatching() {
+ callback_.Reset();
+
+ if (message_loop_) {
+ message_loop_->RemoveDestructionObserver(this);
+ message_loop_ = NULL;
+ }
+
+ if (!cancel_flag_.get()) // if not currently watching...
+ return;
+
+ if (cancel_flag_->value()) {
+ // In this case, the event has fired, but we haven't figured that out yet.
+ // The WaitableEvent may have been deleted too.
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ if (!kernel_.get()) {
+ // We have no kernel. This means that we never enqueued a Waiter on an
+ // event because the event was already signaled when StartWatching was
+ // called.
+ //
+ // In this case, a task was enqueued on the MessageLoop and will run.
+ // We set the flag in case the task hasn't yet run. The flag will stop the
+ // delegate getting called. If the task has run then we have the last
+ // reference to the flag and it will be deleted immedately after.
+ cancel_flag_->Set();
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ AutoLock locked(kernel_->lock_);
+ // We have a lock on the kernel. No one else can signal the event while we
+ // have it.
+
+ // We have a possible ABA issue here. If Dequeue was to compare only the
+ // pointer values then it's possible that the AsyncWaiter could have been
+ // fired, freed and the memory reused for a different Waiter which was
+ // enqueued in the same wait-list. We would think that that waiter was our
+ // AsyncWaiter and remove it.
+ //
+ // To stop this, Dequeue also takes a tag argument which is passed to the
+ // virtual Compare function before the two are considered a match. So we need
+ // a tag which is good for the lifetime of this handle: the Flag. Since we
+ // have a reference to the Flag, its memory cannot be reused while this object
+ // still exists. So if we find a waiter with the correct pointer value, and
+ // which shares a Flag pointer, we have a real match.
+ if (kernel_->Dequeue(waiter_, cancel_flag_.get())) {
+ // Case 2: the waiter hasn't been signaled yet; it was still on the wait
+ // list. We've removed it, thus we can delete it and the task (which cannot
+ // have been enqueued with the MessageLoop because the waiter was never
+ // signaled)
+ delete waiter_;
+ internal_callback_.Reset();
+ cancel_flag_ = NULL;
+ return;
+ }
+
+ // Case 3: the waiter isn't on the wait-list, thus it was signaled. It may
+ // not have run yet, so we set the flag to tell it not to bother enqueuing the
+ // task on the MessageLoop, but to delete it instead. The Waiter deletes
+ // itself once run.
+ cancel_flag_->Set();
+ cancel_flag_ = NULL;
+
+ // If the waiter has already run then the task has been enqueued. If the Task
+ // hasn't yet run, the flag will stop the delegate from getting called. (This
+ // is thread safe because one may only delete a Handle from the MessageLoop
+ // thread.)
+ //
+ // If the delegate has already been called then we have nothing to do. The
+ // task has been deleted by the MessageLoop.
+}
+
+WaitableEvent* WaitableEventWatcher::GetWatchedEvent() {
+ if (!cancel_flag_.get())
+ return NULL;
+
+ if (cancel_flag_->value())
+ return NULL;
+
+ return event_;
+}
+
+// -----------------------------------------------------------------------------
+// This is called when the MessageLoop which the callback will be run it is
+// deleted. We need to cancel the callback as if we had been deleted, but we
+// will still be deleted at some point in the future.
+// -----------------------------------------------------------------------------
+void WaitableEventWatcher::WillDestroyCurrentMessageLoop() {
+ StopWatching();
+}
+
+} // namespace base