diff options
author | Alex Vakulenko <avakulenko@google.com> | 2016-01-25 21:02:07 +0000 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2016-01-25 21:02:07 +0000 |
commit | 7555b4a0e682456898a7d628286978b5fc5de3e4 (patch) | |
tree | 024c351584392fb462e4352e4cafeec57e033b0f | |
parent | 6f0e280039b67bb2c90ef7ea80252c63744be324 (diff) | |
parent | 1ac3a5e3e15a0a3db48c4cb13736ce38033831fe (diff) | |
download | libchrome-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-- | SConstruct | 4 | ||||
-rw-r--r-- | base/files/memory_mapped_file.cc | 95 | ||||
-rw-r--r-- | base/files/memory_mapped_file_posix.cc | 91 | ||||
-rw-r--r-- | base/posix/global_descriptors.cc | 85 | ||||
-rw-r--r-- | base/strings/nullable_string16.cc | 17 | ||||
-rw-r--r-- | base/strings/nullable_string16.h | 46 | ||||
-rw-r--r-- | base/strings/nullable_string16_unittest.cc | 35 | ||||
-rw-r--r-- | base/synchronization/waitable_event_watcher_posix.cc | 270 |
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 |