Revved to chromium 7f6805bd9257180e07cb6924ea41bcb76cfa462b refs/remotes/origin/HEAD filter gyp out of build/landmines.py filter pepper out of mojo/examples/BUILD.gn filter html_viewer out of mojo/services/BUILD.gn filter js out of mojo/BUILD.gn filter js/bindings out of mojo/public/BUILD.gn applied patch gpu_media.patch applied patch cc_strip_video.patch applied patch ui_test_support.patch applied patch remove_ipc_deps.patch applied patch ui_compositor.patch applied patch net_sql.patch
diff --git a/BUILD.gn b/BUILD.gn index 18f2b01..d783ee0 100644 --- a/BUILD.gn +++ b/BUILD.gn
@@ -1227,6 +1227,7 @@ "process/memory_unittest_mac.mm", "process/process_metrics_unittest.cc", "process/process_metrics_unittest_ios.cc", + "process/process_unittest.cc", "process/process_util_unittest.cc", "process/process_util_unittest_ios.cc", "profiler/tracked_time_unittest.cc", @@ -1338,6 +1339,7 @@ "process/memory_unittest.cc", "process/memory_unittest_mac.h", "process/memory_unittest_mac.mm", + "process/process_unittest.cc", "process/process_util_unittest.cc", ]
diff --git a/OWNERS b/OWNERS index 04e7a6f..a014c74 100644 --- a/OWNERS +++ b/OWNERS
@@ -1,12 +1,14 @@ mark@chromium.org darin@chromium.org -willchan@chromium.org ajwong@chromium.org thakis@chromium.org danakj@chromium.org rvargas@chromium.org thestig@chromium.org +# On extended leave. +willchan@chromium.org + per-file *.isolate=csharp@chromium.org per-file *.isolate=maruel@chromium.org per-file bind.h=ajwong@chromium.org
diff --git a/android/BUILD.gn b/android/BUILD.gn new file mode 100644 index 0000000..befe397 --- /dev/null +++ b/android/BUILD.gn
@@ -0,0 +1,18 @@ +import("//build/config/android/config.gni") + +assert(is_android) +assert(!is_android_webview_build) + +shared_library("chromium_android_linker") { + sources = [ "linker/linker_jni.cc" ] + # The NDK contains the crazy_linker here: + # '<(android_ndk_root)/crazy_linker.gyp:crazy_linker' + # However, we use our own fork. See bug 384700. + deps = [ "//third_party/android_crazy_linker" ] + + # TODO(GYP): + # The crazy linker is never instrumented. + #'cflags!': [ + #'-finstrument-functions', + #], +}
diff --git a/android/java/src/org/chromium/base/library_loader/Linker.java b/android/java/src/org/chromium/base/library_loader/Linker.java index 2b3cb09..41104f0 100644 --- a/android/java/src/org/chromium/base/library_loader/Linker.java +++ b/android/java/src/org/chromium/base/library_loader/Linker.java
@@ -826,7 +826,7 @@ sCurrentLoadAddress = libInfo.mLoadAddress + libInfo.mLoadSize; } - sLoadedLibraries.put(libName, libInfo); + sLoadedLibraries.put(sharedRelRoName, libInfo); if (DEBUG) Log.i(TAG, "Library details " + libInfo.toString()); } }
diff --git a/base.gyp b/base.gyp index 41fac4f..95f6f7d 100644 --- a/base.gyp +++ b/base.gyp
@@ -564,6 +564,7 @@ 'process/memory_unittest_mac.mm', 'process/process_metrics_unittest.cc', 'process/process_metrics_unittest_ios.cc', + 'process/process_unittest.cc', 'process/process_util_unittest.cc', 'profiler/tracked_time_unittest.cc', 'rand_util_unittest.cc', @@ -683,6 +684,7 @@ 'sources/': [ # Only test the iOS-meaningful portion of process_utils. ['exclude', '^process/memory_unittest'], + ['exclude', '^process/process_unittest\\.cc$'], ['exclude', '^process/process_util_unittest\\.cc$'], ['include', '^process/process_util_unittest_ios\\.cc$'], # Requires spawning processes. @@ -1404,7 +1406,7 @@ 'includes': [ '../build/java.gypi' ], }, { - # TODO(GN) + # GN: //base/android/chromium_android_linker 'target_name': 'chromium_android_linker', 'type': 'shared_library', 'conditions': [
diff --git a/base_nacl.gyp b/base_nacl.gyp index 0816d31..7221d2a 100644 --- a/base_nacl.gyp +++ b/base_nacl.gyp
@@ -67,5 +67,60 @@ }, ], }], + ['disable_nacl==0', { + 'targets': [ + { + 'target_name': 'base_nacl_nonsfi', + 'type': 'none', + 'include_dirs': [ + '<(DEPTH)/native_client/src/public/linux_syscalls', + ], + 'variables': { + 'base_target': 1, + 'nacl_untrusted_build': 1, + 'nlib_target': 'libbase_nacl_nonsfi.a', + 'build_glibc': 0, + 'build_newlib': 0, + 'build_irt': 0, + 'build_pnacl_newlib': 0, + 'build_nonsfi_helper': 1, + + 'sources': [ + 'base_switches.cc', + 'base_switches.h', + + # For PathExists and ReadFromFD. + 'files/file_util_posix.cc', + + # For MessageLoopForIO based on libevent. + 'message_loop/message_pump_libevent.cc', + 'message_loop/message_pump_libevent.h', + + # For UnixDomainSocket::SendMsg and RecvMsg. + 'posix/unix_domain_socket_linux.cc', + + # For GetKnownDeadTerminationStatus and GetTerminationStatus. + 'process/kill_posix.cc', + + # Unlike libbase_nacl, for Non-SFI build, we need to use + # rand_util_posix for random implementation, instead of + # rand_util_nacl.cc, which is based on IRT. rand_util_nacl.cc is + # excluded below. + 'rand_util_posix.cc', + + # For CancelableSyncSocket. + 'sync_socket_nacl.cc', + ], + }, + 'sources!': [ + 'rand_util_nacl.cc', + ], + 'dependencies': [ + '<(DEPTH)/native_client/tools.gyp:prep_toolchain', + '<(DEPTH)/third_party/libevent/libevent_nacl_nonsfi.gyp:event_nacl_nonsfi', + ], + }, + ], + }], ], }
diff --git a/build_time.cc b/build_time.cc index 760269a..86503e2 100644 --- a/build_time.cc +++ b/build_time.cc
@@ -16,7 +16,11 @@ // // __DATE__ is exactly "Mmm DD YYYY". // __TIME__ is exactly "hh:mm:ss". +#if defined(DONT_EMBED_BUILD_METADATA) + const char kDateTime[] = "Sep 02 2008 08:00:00 PST"; +#else const char kDateTime[] = __DATE__ " " __TIME__ " PST"; +#endif bool result = Time::FromString(kDateTime, &integral_build_time); DCHECK(result); return integral_build_time;
diff --git a/build_time_unittest.cc b/build_time_unittest.cc index 399a53f..aac64a7 100644 --- a/build_time_unittest.cc +++ b/build_time_unittest.cc
@@ -7,7 +7,11 @@ #include "testing/gtest/include/gtest/gtest.h" TEST(BuildTime, DateLooksValid) { +#if !defined(DONT_EMBED_BUILD_METADATA) char build_date[] = __DATE__; +#else + char build_date[] = "Sep 02 2008"; +#endif EXPECT_EQ(11u, strlen(build_date)); EXPECT_EQ(' ', build_date[3]); @@ -15,7 +19,11 @@ } TEST(BuildTime, TimeLooksValid) { +#if defined(DONT_EMBED_BUILD_METADATA) + char build_time[] = "08:00:00"; +#else char build_time[] = __TIME__; +#endif EXPECT_EQ(8u, strlen(build_time)); EXPECT_EQ(':', build_time[2]);
diff --git a/compiler_specific.h b/compiler_specific.h index ba57cc3..6210d1a 100644 --- a/compiler_specific.h +++ b/compiler_specific.h
@@ -94,11 +94,17 @@ // (Typically used to silence a compiler warning when the assignment // is important for some other reason.) // Use like: -// int x ALLOW_UNUSED = ...; +// int x = ...; +// ALLOW_UNUSED_LOCAL(x); +#define ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0 + +// Annotate a typedef or function indicating it's ok if it's not used. +// Use like: +// typedef Foo Bar ALLOW_UNUSED_TYPE; #if defined(COMPILER_GCC) -#define ALLOW_UNUSED __attribute__((unused)) +#define ALLOW_UNUSED_TYPE __attribute__((unused)) #else -#define ALLOW_UNUSED +#define ALLOW_UNUSED_TYPE #endif // Annotate a function indicating it should not be inlined.
diff --git a/files/file_util_posix.cc b/files/file_util_posix.cc index 561f5c7..5a94cef 100644 --- a/files/file_util_posix.cc +++ b/files/file_util_posix.cc
@@ -59,6 +59,7 @@ namespace base { +#if !defined(__native_client_nonsfi__) namespace { #if defined(OS_BSD) || defined(OS_MACOSX) || defined(OS_NACL) @@ -345,6 +346,7 @@ return success; } +#endif // !defined(__native_client_nonsfi__) bool PathExists(const FilePath& path) { ThreadRestrictions::AssertIOAllowed(); @@ -356,6 +358,7 @@ return access(path.value().c_str(), F_OK) == 0; } +#if !defined(__native_client_nonsfi__) bool PathIsWritable(const FilePath& path) { ThreadRestrictions::AssertIOAllowed(); return access(path.value().c_str(), W_OK) == 0; @@ -368,6 +371,7 @@ return S_ISDIR(file_info.st_mode); return false; } +#endif // !defined(__native_client_nonsfi__) bool ReadFromFD(int fd, char* buffer, size_t bytes) { size_t total_read = 0; @@ -381,6 +385,7 @@ return total_read == bytes; } +#if !defined(__native_client_nonsfi__) bool CreateSymbolicLink(const FilePath& target_path, const FilePath& symlink_path) { DCHECK(!symlink_path.empty()); @@ -920,4 +925,6 @@ #endif // !defined(OS_MACOSX) } // namespace internal + +#endif // !defined(__native_client_nonsfi__) } // namespace base
diff --git a/logging_unittest.cc b/logging_unittest.cc index 6ebe32e..95a16f2 100644 --- a/logging_unittest.cc +++ b/logging_unittest.cc
@@ -240,7 +240,8 @@ // looking in the global namespace. namespace nested_test { class Streamable {}; - ALLOW_UNUSED std::ostream& operator<<(std::ostream& out, const Streamable&) { + ALLOW_UNUSED_TYPE std::ostream& operator<<(std::ostream& out, + const Streamable&) { return out << "Streamable"; } TEST_F(LoggingTest, StreamingWstringFindsCorrectOperator) {
diff --git a/mac/foundation_util_unittest.mm b/mac/foundation_util_unittest.mm index 916a13b..e60a0f6 100644 --- a/mac/foundation_util_unittest.mm +++ b/mac/foundation_util_unittest.mm
@@ -331,12 +331,12 @@ #endif // defined(ARCH_CPU_64_BITS) NSInteger some_nsinteger; - FormatNSIntegerAsType* pointer_to_some_nsinteger ALLOW_UNUSED = - &some_nsinteger; + FormatNSIntegerAsType* pointer_to_some_nsinteger = &some_nsinteger; + ALLOW_UNUSED_LOCAL(pointer_to_some_nsinteger); NSUInteger some_nsuinteger; - FormatNSUIntegerAsType* pointer_to_some_nsuinteger ALLOW_UNUSED = - &some_nsuinteger; + FormatNSUIntegerAsType* pointer_to_some_nsuinteger = &some_nsuinteger; + ALLOW_UNUSED_LOCAL(pointer_to_some_nsuinteger); // Check that format specifier works correctly for NSInteger. const struct {
diff --git a/macros.h b/macros.h index 2e3fc09..f89d7d1 100644 --- a/macros.h +++ b/macros.h
@@ -69,8 +69,12 @@ // DEPRECATED: Just use |arraysize()|, now that C++11 has removed the // limitations that forced the use of |ARRAYSIZE_UNSAFE()|. -// TODO(viettrungluu): Convert all instances and delete. crbug.com/423134 +// TODO(viettrungluu): Convert all instances and delete. (The only uses are now +// in Blink; the ifdef is to prevent it from reappearing in Chromium.) +// crbug.com/423134 +#if defined(BLINK_PLATFORM) || defined(BLINK_PLATFORM_IMPLEMENTATION) #define ARRAYSIZE_UNSAFE(a) arraysize(a) +#endif // Use implicit_cast as a safe version of static_cast or const_cast
diff --git a/memory/discardable_memory_unittest.cc b/memory/discardable_memory_unittest.cc index 2bdc079..600475e 100644 --- a/memory/discardable_memory_unittest.cc +++ b/memory/discardable_memory_unittest.cc
@@ -103,6 +103,22 @@ } #endif +// Test behavior when creating enough instances that could use up a 32-bit +// address space. +TEST_P(DiscardableMemoryTest, AddressSpace) { + const size_t kLargeSize = 4 * 1024 * 1024; // 4MiB. + const size_t kNumberOfInstances = 1024 + 1; // >4GiB total. + + scoped_ptr<DiscardableMemory> instances[kNumberOfInstances]; + for (auto& memory : instances) { + memory = CreateLockedMemory(kLargeSize); + ASSERT_TRUE(memory); + void* addr = memory->Memory(); + ASSERT_NE(nullptr, addr); + memory->Unlock(); + } +} + std::vector<DiscardableMemoryType> GetSupportedDiscardableMemoryTypes() { std::vector<DiscardableMemoryType> supported_types; DiscardableMemory::GetSupportedTypes(&supported_types);
diff --git a/message_loop/message_loop.cc b/message_loop/message_loop.cc index c01e542..f1d0d3b 100644 --- a/message_loop/message_loop.cc +++ b/message_loop/message_loop.cc
@@ -100,7 +100,7 @@ #if defined(OS_IOS) typedef MessagePumpIOSForIO MessagePumpForIO; -#elif defined(OS_NACL) +#elif defined(OS_NACL) && !defined(__native_client_nonsfi__) typedef MessagePumpDefault MessagePumpForIO; #elif defined(OS_POSIX) typedef MessagePumpLibevent MessagePumpForIO; @@ -676,7 +676,7 @@ //------------------------------------------------------------------------------ // MessageLoopForIO -#if !defined(OS_NACL) +#if !defined(OS_NACL) || defined(__native_client_nonsfi__) void MessageLoopForIO::AddIOObserver( MessageLoopForIO::IOObserver* io_observer) { ToPumpIO(pump_.get())->AddIOObserver(io_observer); @@ -714,6 +714,6 @@ } #endif -#endif // !defined(OS_NACL) +#endif // !defined(OS_NACL) || defined(__native_client_nonsfi__) } // namespace base
diff --git a/message_loop/message_loop.h b/message_loop/message_loop.h index bc98251..a180cc3 100644 --- a/message_loop/message_loop.h +++ b/message_loop/message_loop.h
@@ -596,7 +596,7 @@ return loop && loop->type() == MessageLoop::TYPE_IO; } -#if !defined(OS_NACL) +#if !defined(OS_NACL) || defined(__native_client_nonsfi__) #if defined(OS_WIN) typedef MessagePumpForIO::IOHandler IOHandler; @@ -642,7 +642,7 @@ FileDescriptorWatcher *controller, Watcher *delegate); #endif // defined(OS_IOS) || defined(OS_POSIX) -#endif // !defined(OS_NACL) +#endif // !defined(OS_NACL) || defined(__native_client_nonsfi__) }; // Do not add any member variables to MessageLoopForIO! This is important b/c
diff --git a/posix/unix_domain_socket_linux.cc b/posix/unix_domain_socket_linux.cc index 51b936b..20a5944 100644 --- a/posix/unix_domain_socket_linux.cc +++ b/posix/unix_domain_socket_linux.cc
@@ -6,7 +6,6 @@ #include <errno.h> #include <sys/socket.h> -#include <sys/uio.h> #include <unistd.h> #include <vector> @@ -18,8 +17,13 @@ #include "base/posix/eintr_wrapper.h" #include "base/stl_util.h" +#if !defined(__native_client_nonsfi__) +#include <sys/uio.h> +#endif + const size_t UnixDomainSocket::kMaxFileDescriptors = 16; +#if !defined(__native_client_nonsfi__) // Creates a connected pair of UNIX-domain SOCK_SEQPACKET sockets, and passes // ownership of the newly allocated file descriptors to |one| and |two|. // Returns true on success. @@ -37,6 +41,7 @@ const int enable = 1; return setsockopt(fd, SOL_SOCKET, SO_PASSCRED, &enable, sizeof(enable)) == 0; } +#endif // !defined(__native_client_nonsfi__) // static bool UnixDomainSocket::SendMsg(int fd, @@ -106,8 +111,14 @@ msg.msg_iov = &iov; msg.msg_iovlen = 1; - char control_buffer[CMSG_SPACE(sizeof(int) * kMaxFileDescriptors) + - CMSG_SPACE(sizeof(struct ucred))]; + const size_t kControlBufferSize = + CMSG_SPACE(sizeof(int) * kMaxFileDescriptors) +#if !defined(__native_client_nonsfi__) + // The PNaCl toolchain for Non-SFI binary build does not support ucred. + + CMSG_SPACE(sizeof(struct ucred)) +#endif + ; + char control_buffer[kControlBufferSize]; msg.msg_control = control_buffer; msg.msg_controllen = sizeof(control_buffer); @@ -130,21 +141,29 @@ wire_fds = reinterpret_cast<int*>(CMSG_DATA(cmsg)); wire_fds_len = payload_len / sizeof(int); } +#if !defined(__native_client_nonsfi__) + // The PNaCl toolchain for Non-SFI binary build does not support + // SCM_CREDENTIALS. if (cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_CREDENTIALS) { DCHECK(payload_len == sizeof(struct ucred)); DCHECK(pid == -1); pid = reinterpret_cast<struct ucred*>(CMSG_DATA(cmsg))->pid; } +#endif } } +#if !defined(__native_client_nonsfi__) + // The PNaCl toolchain for Non-SFI binary build does not support + // MSG_TRUNC or MSG_CTRUNC. if (msg.msg_flags & MSG_TRUNC || msg.msg_flags & MSG_CTRUNC) { for (unsigned i = 0; i < wire_fds_len; ++i) close(wire_fds[i]); errno = EMSGSIZE; return -1; } +#endif if (wire_fds) { for (unsigned i = 0; i < wire_fds_len; ++i) @@ -165,6 +184,7 @@ return r; } +#if !defined(__native_client_nonsfi__) // static ssize_t UnixDomainSocket::SendRecvMsg(int fd, uint8_t* reply, @@ -222,3 +242,4 @@ return reply_len; } +#endif // !defined(__native_client_nonsfi__)
diff --git a/posix/unix_domain_socket_linux.h b/posix/unix_domain_socket_linux.h index 59bb884..5281875 100644 --- a/posix/unix_domain_socket_linux.h +++ b/posix/unix_domain_socket_linux.h
@@ -21,10 +21,12 @@ // Maximum number of file descriptors that can be read by RecvMsg(). static const size_t kMaxFileDescriptors; +#if !defined(__native_client_nonsfi__) // Use to enable receiving process IDs in RecvMsgWithPid. Should be called on // the receiving socket (i.e., the socket passed to RecvMsgWithPid). Returns // true if successful. static bool EnableReceiveProcessId(int fd); +#endif // !defined(__native_client_nonsfi__) // Use sendmsg to write the given msg and include a vector of file // descriptors. Returns true if successful. @@ -50,6 +52,7 @@ ScopedVector<base::ScopedFD>* fds, base::ProcessId* pid); +#if !defined(__native_client_nonsfi__) // Perform a sendmsg/recvmsg pair. // 1. This process creates a UNIX SEQPACKET socketpair. Using // connection-oriented sockets (SEQPACKET or STREAM) is critical here, @@ -82,6 +85,7 @@ int recvmsg_flags, int* result_fd, const Pickle& request); +#endif // !defined(__native_client_nonsfi__) private: // Similar to RecvMsg, but allows to specify |flags| for recvmsg(2). static ssize_t RecvMsgWithFlags(int fd,
diff --git a/process/kill_posix.cc b/process/kill_posix.cc index d4ca726..bff7be4 100644 --- a/process/kill_posix.cc +++ b/process/kill_posix.cc
@@ -22,6 +22,7 @@ namespace { +#if !defined(__native_client_nonsfi__) bool WaitpidWithTimeout(ProcessHandle handle, int* status, base::TimeDelta wait) { @@ -83,6 +84,7 @@ return ret_pid > 0; } +#endif // !defined(__native_client_nonsfi__) TerminationStatus GetTerminationStatusImpl(ProcessHandle handle, bool can_block, @@ -130,6 +132,7 @@ } // namespace +#if !defined(__native_client_nonsfi__) // Attempts to kill the process identified by the given process // entry structure. Ignores specified exit_code; posix can't force that. // Returns true if this is successful, false otherwise. @@ -191,6 +194,7 @@ DPLOG(ERROR) << "Unable to terminate process group " << process_group_id; return result; } +#endif // !defined(__native_client_nonsfi__) TerminationStatus GetTerminationStatus(ProcessHandle handle, int* exit_code) { return GetTerminationStatusImpl(handle, false /* can_block */, exit_code); @@ -206,6 +210,7 @@ return GetTerminationStatusImpl(handle, true /* can_block */, exit_code); } +#if !defined(__native_client_nonsfi__) bool WaitForExitCode(ProcessHandle handle, int* exit_code) { int status; if (HANDLE_EINTR(waitpid(handle, &status, 0)) == -1) { @@ -354,7 +359,7 @@ bool WaitForSingleProcess(ProcessHandle handle, base::TimeDelta wait) { ProcessHandle parent_pid = GetParentProcessId(handle); - ProcessHandle our_pid = Process::Current().handle(); + ProcessHandle our_pid = GetCurrentProcessHandle(); if (parent_pid != our_pid) { #if defined(OS_MACOSX) // On Mac we can wait on non child processes. @@ -478,5 +483,6 @@ } #endif // !defined(OS_MACOSX) +#endif // !defined(__native_client_nonsfi__) } // namespace base
diff --git a/process/process.h b/process/process.h index 20e8884..7019474 100644 --- a/process/process.h +++ b/process/process.h
@@ -7,53 +7,81 @@ #include "base/base_export.h" #include "base/basictypes.h" +#include "base/move.h" #include "base/process/process_handle.h" #include "build/build_config.h" +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#endif + namespace base { +// Provides a move-only encapsulation of a process. +// +// This object is not tied to the lifetime of the underlying process: the +// process may be killed and this object may still around, and it will still +// claim to be valid. The actual behavior in that case is OS dependent like so: +// +// Windows: The underlying ProcessHandle will be valid after the process dies +// and can be used to gather some information about that process, but most +// methods will obviously fail. +// +// POSIX: The underlying PorcessHandle is not guaranteed to remain valid after +// the process dies, and it may be reused by the system, which means that it may +// end up pointing to the wrong process. class BASE_EXPORT Process { + MOVE_ONLY_TYPE_FOR_CPP_03(Process, RValue) + public: - Process() : process_(kNullProcessHandle) { - } + explicit Process(ProcessHandle handle = kNullProcessHandle); - explicit Process(ProcessHandle handle) : process_(handle) { - } + // Move constructor for C++03 move emulation of this type. + Process(RValue other); - // A handle to the current process. + // The destructor does not terminate the process. + ~Process() {} + + // Move operator= for C++03 move emulation of this type. + Process& operator=(RValue other); + + // Returns an object for the current process. static Process Current(); + // Returns true if processes can be backgrounded. static bool CanBackgroundProcesses(); - // Get/Set the handle for this process. The handle will be 0 if the process - // is no longer running. - ProcessHandle handle() const { return process_; } - void set_handle(ProcessHandle handle) { - process_ = handle; - } + // Returns true if this objects represents a valid process. + bool IsValid() const; + + // Returns a handle for this process. There is no guarantee about when that + // handle becomes invalid because this object retains ownership. + ProcessHandle Handle() const; + + // Returns a second object that represents this process. + Process Duplicate() const; // Get the PID for this process. ProcessId pid() const; - // Is the this process the current process. + // Returns true if this process is the current process. bool is_current() const; // Close the process handle. This will not terminate the process. void Close(); - // Terminates the process with extreme prejudice. The given result code will - // be the exit code of the process. If the process has already exited, this - // will do nothing. + // Terminates the process with extreme prejudice. The given |result_code| will + // be the exit code of the process. + // NOTE: On POSIX |result_code| is ignored. void Terminate(int result_code); // A process is backgrounded when it's priority is lower than normal. // Return true if this process is backgrounded, false otherwise. bool IsProcessBackgrounded() const; - // Set a process as backgrounded. If value is true, the priority - // of the process will be lowered. If value is false, the priority - // of the process will be made "normal" - equivalent to default - // process priority. + // Set a process as backgrounded. If value is true, the priority of the + // process will be lowered. If value is false, the priority of the process + // will be made "normal" - equivalent to default process priority. // Returns true if the priority was changed, false otherwise. bool SetProcessBackgrounded(bool value); @@ -62,7 +90,12 @@ int GetPriority() const; private: +#if defined(OS_WIN) + bool is_current_process_; + win::ScopedHandle process_; +#else ProcessHandle process_; +#endif }; } // namespace base
diff --git a/process/process_linux.cc b/process/process_linux.cc index 2c22d26..59ee288 100644 --- a/process/process_linux.cc +++ b/process/process_linux.cc
@@ -17,6 +17,7 @@ namespace base { namespace { + const int kForegroundPriority = 0; #if defined(OS_CHROMEOS) @@ -62,10 +63,37 @@ #else const int kBackgroundPriority = 5; #endif + +struct CheckForNicePermission { + CheckForNicePermission() : can_reraise_priority(false) { + // We won't be able to raise the priority if we don't have the right rlimit. + // The limit may be adjusted in /etc/security/limits.conf for PAM systems. + struct rlimit rlim; + if ((getrlimit(RLIMIT_NICE, &rlim) == 0) && + (20 - kForegroundPriority) <= static_cast<int>(rlim.rlim_cur)) { + can_reraise_priority = true; + } + }; + + bool can_reraise_priority; +}; + +} // namespace + +// static +bool Process::CanBackgroundProcesses() { +#if defined(OS_CHROMEOS) + if (cgroups.Get().enabled) + return true; +#endif + + static LazyInstance<CheckForNicePermission> check_for_nice_permission = + LAZY_INSTANCE_INITIALIZER; + return check_for_nice_permission.Get().can_reraise_priority; } bool Process::IsProcessBackgrounded() const { - DCHECK(process_); + DCHECK(IsValid()); #if defined(OS_CHROMEOS) if (cgroups.Get().enabled) { @@ -87,7 +115,7 @@ } bool Process::SetProcessBackgrounded(bool background) { - DCHECK(process_); + DCHECK(IsValid()); #if defined(OS_CHROMEOS) if (cgroups.Get().enabled) { @@ -108,30 +136,4 @@ return result == 0; } -struct CheckForNicePermission { - CheckForNicePermission() : can_reraise_priority(false) { - // We won't be able to raise the priority if we don't have the right rlimit. - // The limit may be adjusted in /etc/security/limits.conf for PAM systems. - struct rlimit rlim; - if ((getrlimit(RLIMIT_NICE, &rlim) == 0) && - (20 - kForegroundPriority) <= static_cast<int>(rlim.rlim_cur)) { - can_reraise_priority = true; - } - }; - - bool can_reraise_priority; -}; - -// static -bool Process::CanBackgroundProcesses() { -#if defined(OS_CHROMEOS) - if (cgroups.Get().enabled) - return true; -#endif - - static LazyInstance<CheckForNicePermission> check_for_nice_permission = - LAZY_INSTANCE_INITIALIZER; - return check_for_nice_permission.Get().can_reraise_priority; -} - } // namespace base
diff --git a/process/process_posix.cc b/process/process_posix.cc index 3d8b31d..ea8fd8c 100644 --- a/process/process_posix.cc +++ b/process/process_posix.cc
@@ -13,15 +13,54 @@ namespace base { +Process::Process(ProcessHandle handle) : process_(handle) { + CHECK_NE(handle, GetCurrentProcessHandle()); +} + +Process::Process(RValue other) + : process_(other.object->process_) { + other.object->Close(); +} + +Process& Process::operator=(RValue other) { + if (this != other.object) { + process_ = other.object->process_; + other.object->Close(); + } + return *this; +} + // static Process Process::Current() { - return Process(GetCurrentProcessHandle()); + Process process; + process.process_ = GetCurrentProcessHandle(); + return process.Pass(); +} + +#if !defined(OS_LINUX) +// static +bool Process::CanBackgroundProcesses() { + return false; +} +#endif // !defined(OS_LINUX) + +bool Process::IsValid() const { + return process_ != kNullProcessHandle; +} + +ProcessHandle Process::Handle() const { + return process_; +} + +Process Process::Duplicate() const { + if (is_current()) + return Current(); + + return Process(process_); } ProcessId Process::pid() const { - if (process_ == 0) - return 0; - + DCHECK(IsValid()); return GetProcId(process_); } @@ -30,7 +69,7 @@ } void Process::Close() { - process_ = 0; + process_ = kNullProcessHandle; // if the process wasn't terminated (so we waited) or the state // wasn't already collected w/ a wait from process_utils, we're gonna // end up w/ a zombie when it does finally exit. @@ -38,8 +77,7 @@ void Process::Terminate(int result_code) { // result_code isn't supportable. - if (!process_) - return; + DCHECK(IsValid()); // We don't wait here. It's the responsibility of other code to reap the // child. KillProcess(process_, result_code, false); @@ -48,6 +86,7 @@ #if !defined(OS_LINUX) bool Process::IsProcessBackgrounded() const { // See SetProcessBackgrounded(). + DCHECK(IsValid()); return false; } @@ -55,18 +94,13 @@ // POSIX only allows lowering the priority of a process, so if we // were to lower it we wouldn't be able to raise it back to its initial // priority. + DCHECK(IsValid()); return false; } - -// static -bool Process::CanBackgroundProcesses() { - return false; -} - -#endif +#endif // !defined(OS_LINUX) int Process::GetPriority() const { - DCHECK(process_); + DCHECK(IsValid()); return getpriority(PRIO_PROCESS, process_); }
diff --git a/process/process_unittest.cc b/process/process_unittest.cc new file mode 100644 index 0000000..66d6e63 --- /dev/null +++ b/process/process_unittest.cc
@@ -0,0 +1,161 @@ +// Copyright 2014 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/process/process.h" + +#include "base/process/kill.h" +#include "base/test/multiprocess_test.h" +#include "base/test/test_timeouts.h" +#include "base/threading/platform_thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/multiprocess_func_list.h" + + +namespace { + +#if defined(OS_WIN) +const int kExpectedStillRunningExitCode = 0x102; +#else +const int kExpectedStillRunningExitCode = 0; +#endif + +} // namespace + +namespace base { + +class ProcessTest : public MultiProcessTest { +}; + +TEST_F(ProcessTest, Create) { + Process process(SpawnChild("SimpleChildProcess")); + ASSERT_TRUE(process.IsValid()); + ASSERT_FALSE(process.is_current()); + process.Close(); + ASSERT_FALSE(process.IsValid()); +} + +TEST_F(ProcessTest, CreateCurrent) { + Process process = Process::Current(); + ASSERT_TRUE(process.IsValid()); + ASSERT_TRUE(process.is_current()); + process.Close(); + ASSERT_FALSE(process.IsValid()); +} + +TEST_F(ProcessTest, Move) { + Process process1(SpawnChild("SimpleChildProcess")); + EXPECT_TRUE(process1.IsValid()); + + Process process2; + EXPECT_FALSE(process2.IsValid()); + + process2 = process1.Pass(); + EXPECT_TRUE(process2.IsValid()); + EXPECT_FALSE(process1.IsValid()); + EXPECT_FALSE(process2.is_current()); + + Process process3 = Process::Current(); + process2 = process3.Pass(); + EXPECT_TRUE(process2.is_current()); + EXPECT_TRUE(process2.IsValid()); + EXPECT_FALSE(process3.IsValid()); +} + +TEST_F(ProcessTest, Duplicate) { + Process process1(SpawnChild("SimpleChildProcess")); + ASSERT_TRUE(process1.IsValid()); + + Process process2 = process1.Duplicate(); + ASSERT_TRUE(process1.IsValid()); + ASSERT_TRUE(process2.IsValid()); + EXPECT_EQ(process1.pid(), process2.pid()); + EXPECT_FALSE(process1.is_current()); + EXPECT_FALSE(process2.is_current()); + + process1.Close(); + ASSERT_TRUE(process2.IsValid()); +} + +TEST_F(ProcessTest, DuplicateCurrent) { + Process process1 = Process::Current(); + ASSERT_TRUE(process1.IsValid()); + + Process process2 = process1.Duplicate(); + ASSERT_TRUE(process1.IsValid()); + ASSERT_TRUE(process2.IsValid()); + EXPECT_EQ(process1.pid(), process2.pid()); + EXPECT_TRUE(process1.is_current()); + EXPECT_TRUE(process2.is_current()); + + process1.Close(); + ASSERT_TRUE(process2.IsValid()); +} + +MULTIPROCESS_TEST_MAIN(SleepyChildProcess) { + PlatformThread::Sleep(TestTimeouts::action_max_timeout()); + return 0; +} + +TEST_F(ProcessTest, Terminate) { + Process process(SpawnChild("SleepyChildProcess")); + ASSERT_TRUE(process.IsValid()); + + const int kDummyExitCode = 42; + int exit_code = kDummyExitCode; + EXPECT_EQ(TERMINATION_STATUS_STILL_RUNNING, + GetTerminationStatus(process.Handle(), &exit_code)); + EXPECT_EQ(kExpectedStillRunningExitCode, exit_code); + + exit_code = kDummyExitCode; + int kExpectedExitCode = 250; + process.Terminate(kExpectedExitCode); + WaitForSingleProcess(process.Handle(), TestTimeouts::action_max_timeout()); + + EXPECT_NE(TERMINATION_STATUS_STILL_RUNNING, + GetTerminationStatus(process.Handle(), &exit_code)); +#if !defined(OS_POSIX) + // The POSIX implementation actually ignores the exit_code. + EXPECT_EQ(kExpectedExitCode, exit_code); +#endif +} + +// Ensure that the priority of a process is restored correctly after +// backgrounding and restoring. +// Note: a platform may not be willing or able to lower the priority of +// a process. The calls to SetProcessBackground should be noops then. +TEST_F(ProcessTest, SetProcessBackgrounded) { + Process process(SpawnChild("SimpleChildProcess")); + int old_priority = process.GetPriority(); +#if defined(OS_WIN) + EXPECT_TRUE(process.SetProcessBackgrounded(true)); + EXPECT_TRUE(process.IsProcessBackgrounded()); + EXPECT_TRUE(process.SetProcessBackgrounded(false)); + EXPECT_FALSE(process.IsProcessBackgrounded()); +#else + process.SetProcessBackgrounded(true); + process.SetProcessBackgrounded(false); +#endif + int new_priority = process.GetPriority(); + EXPECT_EQ(old_priority, new_priority); +} + +// Same as SetProcessBackgrounded but to this very process. It uses +// a different code path at least for Windows. +TEST_F(ProcessTest, SetProcessBackgroundedSelf) { + Process process = Process::Current(); + int old_priority = process.GetPriority(); +#if defined(OS_WIN) + EXPECT_TRUE(process.SetProcessBackgrounded(true)); + EXPECT_TRUE(process.IsProcessBackgrounded()); + EXPECT_TRUE(process.SetProcessBackgrounded(false)); + EXPECT_FALSE(process.IsProcessBackgrounded()); +#else + process.SetProcessBackgrounded(true); + process.SetProcessBackgrounded(false); +#endif + int new_priority = process.GetPriority(); + EXPECT_EQ(old_priority, new_priority); +} + +} // namespace base
diff --git a/process/process_util_unittest.cc b/process/process_util_unittest.cc index 9188fd3..d846d1a 100644 --- a/process/process_util_unittest.cc +++ b/process/process_util_unittest.cc
@@ -311,45 +311,6 @@ remove(signal_file.c_str()); } -// Ensure that the priority of a process is restored correctly after -// backgrounding and restoring. -// Note: a platform may not be willing or able to lower the priority of -// a process. The calls to SetProcessBackground should be noops then. -TEST_F(ProcessUtilTest, SetProcessBackgrounded) { - base::ProcessHandle handle = SpawnChild("SimpleChildProcess"); - base::Process process(handle); - int old_priority = process.GetPriority(); -#if defined(OS_WIN) - EXPECT_TRUE(process.SetProcessBackgrounded(true)); - EXPECT_TRUE(process.IsProcessBackgrounded()); - EXPECT_TRUE(process.SetProcessBackgrounded(false)); - EXPECT_FALSE(process.IsProcessBackgrounded()); -#else - process.SetProcessBackgrounded(true); - process.SetProcessBackgrounded(false); -#endif - int new_priority = process.GetPriority(); - EXPECT_EQ(old_priority, new_priority); -} - -// Same as SetProcessBackgrounded but to this very process. It uses -// a different code path at least for Windows. -TEST_F(ProcessUtilTest, SetProcessBackgroundedSelf) { - base::Process process(base::Process::Current().handle()); - int old_priority = process.GetPriority(); -#if defined(OS_WIN) - EXPECT_TRUE(process.SetProcessBackgrounded(true)); - EXPECT_TRUE(process.IsProcessBackgrounded()); - EXPECT_TRUE(process.SetProcessBackgrounded(false)); - EXPECT_FALSE(process.IsProcessBackgrounded()); -#else - process.SetProcessBackgrounded(true); - process.SetProcessBackgrounded(false); -#endif - int new_priority = process.GetPriority(); - EXPECT_EQ(old_priority, new_priority); -} - #if defined(OS_WIN) // TODO(estade): if possible, port this test. TEST_F(ProcessUtilTest, GetAppOutput) {
diff --git a/process/process_win.cc b/process/process_win.cc index 1217b50..05041b2 100644 --- a/process/process_win.cc +++ b/process/process_win.cc
@@ -10,20 +10,83 @@ namespace base { +Process::Process(ProcessHandle handle) + : is_current_process_(false), + process_(handle) { + CHECK_NE(handle, ::GetCurrentProcess()); +} + +Process::Process(RValue other) + : is_current_process_(other.object->is_current_process_), + process_(other.object->process_.Take()) { + other.object->Close(); +} + +Process& Process::operator=(RValue other) { + if (this != other.object) { + process_.Set(other.object->process_.Take()); + is_current_process_ = other.object->is_current_process_; + other.object->Close(); + } + return *this; +} + +// static +Process Process::Current() { + Process process; + process.is_current_process_ = true; + return process.Pass(); +} + +// static +bool Process::CanBackgroundProcesses() { + return true; +} + +bool Process::IsValid() const { + return process_.IsValid() || is_current(); +} + +ProcessHandle Process::Handle() const { + return is_current_process_ ? GetCurrentProcess() : process_.Get(); +} + +Process Process::Duplicate() const { + if (is_current()) + return Current(); + + ProcessHandle out_handle; + if (!IsValid() || !::DuplicateHandle(GetCurrentProcess(), + Handle(), + GetCurrentProcess(), + &out_handle, + 0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + return Process(); + } + return Process(out_handle); +} + +ProcessId Process::pid() const { + DCHECK(IsValid()); + return GetProcId(Handle()); +} + +bool Process::is_current() const { + return is_current_process_; +} + void Process::Close() { - if (!process_) + is_current_process_ = false; + if (!process_.IsValid()) return; - // Don't call CloseHandle on a pseudo-handle. - if (process_ != ::GetCurrentProcess()) - ::CloseHandle(process_); - - process_ = NULL; + process_.Close(); } void Process::Terminate(int result_code) { - if (!process_) - return; + DCHECK(IsValid()); // Call NtTerminateProcess directly, without going through the import table, // which might have been hooked with a buggy replacement by third party @@ -32,12 +95,11 @@ typedef UINT (WINAPI *TerminateProcessPtr)(HANDLE handle, UINT code); TerminateProcessPtr terminate_process = reinterpret_cast<TerminateProcessPtr>( GetProcAddress(module, "NtTerminateProcess")); - terminate_process(process_, result_code); + terminate_process(Handle(), result_code); } bool Process::IsProcessBackgrounded() const { - if (!process_) - return false; // Failure case. + DCHECK(IsValid()); DWORD priority = GetPriority(); if (priority == 0) return false; // Failure case. @@ -46,47 +108,24 @@ } bool Process::SetProcessBackgrounded(bool value) { - if (!process_) - return false; + DCHECK(IsValid()); // Vista and above introduce a real background mode, which not only // sets the priority class on the threads but also on the IO generated // by it. Unfortunately it can only be set for the calling process. DWORD priority; - if ((base::win::GetVersion() >= base::win::VERSION_VISTA) && - (process_ == ::GetCurrentProcess())) { + if ((base::win::GetVersion() >= base::win::VERSION_VISTA) && (is_current())) { priority = value ? PROCESS_MODE_BACKGROUND_BEGIN : PROCESS_MODE_BACKGROUND_END; } else { priority = value ? BELOW_NORMAL_PRIORITY_CLASS : NORMAL_PRIORITY_CLASS; } - return (::SetPriorityClass(process_, priority) != 0); -} - -ProcessId Process::pid() const { - if (process_ == 0) - return 0; - - return GetProcId(process_); -} - -bool Process::is_current() const { - return process_ == GetCurrentProcess(); -} - -// static -Process Process::Current() { - return Process(::GetCurrentProcess()); -} - -// static -bool Process::CanBackgroundProcesses() { - return true; + return (::SetPriorityClass(Handle(), priority) != 0); } int Process::GetPriority() const { - DCHECK(process_); - return ::GetPriorityClass(process_); + DCHECK(IsValid()); + return ::GetPriorityClass(Handle()); } } // namespace base
diff --git a/strings/safe_sprintf.cc b/strings/safe_sprintf.cc index 1c92718..5b57563 100644 --- a/strings/safe_sprintf.cc +++ b/strings/safe_sprintf.cc
@@ -176,8 +176,7 @@ // overflowed |size_|) at any time during padding. inline bool Pad(char pad, size_t padding, size_t len) { DEBUG_CHECK(pad); - DEBUG_CHECK(padding >= 0 && padding <= kSSizeMax); - DEBUG_CHECK(len >= 0); + DEBUG_CHECK(padding <= kSSizeMax); for (; padding > len; --padding) { if (!Out(pad)) { if (--padding) { @@ -283,7 +282,6 @@ DEBUG_CHECK(base <= 16); DEBUG_CHECK(!sign || base == 10); DEBUG_CHECK(pad == '0' || pad == ' '); - DEBUG_CHECK(padding >= 0); DEBUG_CHECK(padding <= kSSizeMax); DEBUG_CHECK(!(sign && prefix && *prefix));
diff --git a/threading/thread_restrictions.h b/threading/thread_restrictions.h index 3653c96..39f32a7 100644 --- a/threading/thread_restrictions.h +++ b/threading/thread_restrictions.h
@@ -15,22 +15,16 @@ #define ENABLE_THREAD_RESTRICTIONS 0 #endif -class AcceleratedPresenter; class BrowserProcessImpl; class HistogramSynchronizer; class NativeBackendKWallet; class ScopedAllowWaitForLegacyWebViewApi; class TestingAutomationProvider; -namespace browser_sync { -class NonFrontendDataTypeController; -class UIModelWorker; -} namespace cc { class CompletionEvent; } namespace chromeos { -class AudioMixerAlsa; class BlockingMethodCaller; namespace system { class StatisticsProviderImpl; @@ -44,7 +38,6 @@ class BrowserGpuMemoryBufferManager; class BrowserShutdownProfileDumper; class BrowserTestBase; -class GLHelper; class GpuChannelHost; class NestedMessagePumpAndroid; class RenderWidgetResizeHelper; @@ -58,20 +51,12 @@ class BackendImpl; class InFlightIO; } -namespace media { -class AudioOutputController; -} -namespace metrics { -class MetricsService; -} namespace mojo { namespace common { class WatcherThreadManager; } } namespace net { -class FileStreamPosix; -class FileStreamWin; namespace internal { class AddressTrackerLinux; } @@ -206,29 +191,20 @@ // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. - friend class ::chromeos::AudioMixerAlsa; // http://crbug.com/125206 friend class ::chromeos::BlockingMethodCaller; // http://crbug.com/125360 friend class ::chromeos::system::StatisticsProviderImpl; // http://crbug.com/125385 - friend class browser_sync::NonFrontendDataTypeController; // http://crbug.com/19757 - friend class browser_sync::UIModelWorker; // http://crbug.com/19757 friend class chrome_browser_net::Predictor; // http://crbug.com/78451 friend class content::BrowserGpuChannelHostFactory; // http://crbug.com/125248 friend class content::BrowserGpuMemoryBufferManager; // http://crbug.com/420368 - friend class content::GLHelper; // http://crbug.com/125415 friend class content::GpuChannelHost; // http://crbug.com/125264 friend class content::TextInputClientMac; // http://crbug.com/121917 friend class dbus::Bus; // http://crbug.com/125222 friend class disk_cache::BackendImpl; // http://crbug.com/74623 friend class disk_cache::InFlightIO; // http://crbug.com/74623 - friend class media::AudioOutputController; // http://crbug.com/120973 - friend class net::FileStreamPosix; // http://crbug.com/115067 - friend class net::FileStreamWin; // http://crbug.com/115067 friend class net::internal::AddressTrackerLinux; // http://crbug.com/125097 - friend class ::AcceleratedPresenter; // http://crbug.com/125391 friend class ::BrowserProcessImpl; // http://crbug.com/125207 - friend class metrics::MetricsService; // http://crbug.com/124954 friend class ::NativeBackendKWallet; // http://crbug.com/125331 // END USAGE THAT NEEDS TO BE FIXED.
diff --git a/tuple_unittest.cc b/tuple_unittest.cc index 402394c..8d620de 100644 --- a/tuple_unittest.cc +++ b/tuple_unittest.cc
@@ -30,7 +30,8 @@ } // namespace TEST(TupleTest, Basic) { - Tuple0 t0 ALLOW_UNUSED = MakeTuple(); + Tuple0 t0 = MakeTuple(); + ALLOW_UNUSED_LOCAL(t0); Tuple1<int> t1(1); Tuple2<int, const char*> t2 = MakeTuple(1, static_cast<const char*>("wee")); Tuple3<int, int, int> t3(1, 2, 3);
diff --git a/win/event_trace_consumer_unittest.cc b/win/event_trace_consumer_unittest.cc index 92e91b9..3043152 100644 --- a/win/event_trace_consumer_unittest.cc +++ b/win/event_trace_consumer_unittest.cc
@@ -14,7 +14,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" -#include "base/process/process.h" +#include "base/process/process_handle.h" #include "base/strings/stringprintf.h" #include "base/win/event_trace_controller.h" #include "base/win/event_trace_provider.h" @@ -79,8 +79,7 @@ class EtwTraceConsumerBaseTest: public testing::Test { public: EtwTraceConsumerBaseTest() - : session_name_(StringPrintf(L"TestSession-%d", - Process::Current().pid())) { + : session_name_(StringPrintf(L"TestSession-%d", GetCurrentProcId())) { } virtual void SetUp() {
diff --git a/win/event_trace_controller_unittest.cc b/win/event_trace_controller_unittest.cc index 8ca9cf5..be11128 100644 --- a/win/event_trace_controller_unittest.cc +++ b/win/event_trace_controller_unittest.cc
@@ -11,7 +11,7 @@ #include "base/files/file_util.h" #include "base/files/scoped_temp_dir.h" #include "base/logging.h" -#include "base/process/process.h" +#include "base/process/process_handle.h" #include "base/strings/stringprintf.h" #include "base/sys_info.h" #include "base/win/event_trace_controller.h" @@ -110,8 +110,7 @@ class EtwTraceControllerTest : public testing::Test { public: EtwTraceControllerTest() - : session_name_( - StringPrintf(L"TestSession-%d", Process::Current().pid())) { + : session_name_(StringPrintf(L"TestSession-%d", GetCurrentProcId())) { } virtual void SetUp() {
diff --git a/win/registry.cc b/win/registry.cc index e8fb892..23ad12c 100644 --- a/win/registry.cc +++ b/win/registry.cc
@@ -34,23 +34,67 @@ } // namespace -// RegKey ---------------------------------------------------------------------- +// Watches for modifications to a key. +class RegKey::Watcher : public ObjectWatcher::Delegate { + public: + explicit Watcher(RegKey* owner) : owner_(owner) {} + ~Watcher() {} -RegKey::RegKey() - : key_(NULL), - watch_event_(0), - wow64access_(0) { + bool StartWatching(HKEY key, const ChangeCallback& callback); + + // Implementation of ObjectWatcher::Delegate. + void OnObjectSignaled(HANDLE object) override { + DCHECK(watch_event_.IsValid() && watch_event_.Get() == object); + ChangeCallback callback = callback_; + callback_.Reset(); + callback.Run(); + } + + private: + RegKey* owner_; + ScopedHandle watch_event_; + ObjectWatcher object_watcher_; + ChangeCallback callback_; + DISALLOW_COPY_AND_ASSIGN(Watcher); +}; + +bool RegKey::Watcher::StartWatching(HKEY key, const ChangeCallback& callback) { + DCHECK(key); + DCHECK(callback_.is_null()); + + if (!watch_event_.IsValid()) + watch_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); + + if (!watch_event_.IsValid()) + return false; + + DWORD filter = REG_NOTIFY_CHANGE_NAME | + REG_NOTIFY_CHANGE_ATTRIBUTES | + REG_NOTIFY_CHANGE_LAST_SET | + REG_NOTIFY_CHANGE_SECURITY; + + // Watch the registry key for a change of value. + LONG result = RegNotifyChangeKeyValue(key, TRUE, filter, watch_event_.Get(), + TRUE); + if (result != ERROR_SUCCESS) { + watch_event_.Close(); + return false; + } + + callback_ = callback; + return object_watcher_.StartWatching(watch_event_.Get(), this); } -RegKey::RegKey(HKEY key) - : key_(key), - watch_event_(0), - wow64access_(0) { +// RegKey ---------------------------------------------------------------------- + +RegKey::RegKey() : key_(NULL), wow64access_(0) { +} + +RegKey::RegKey(HKEY key) : key_(key), wow64access_(0) { } RegKey::RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access) : key_(NULL), - watch_event_(0), wow64access_(0) { if (rootkey) { if (access & (KEY_SET_VALUE | KEY_CREATE_SUB_KEY | KEY_CREATE_LINK)) @@ -150,7 +194,6 @@ } void RegKey::Close() { - StopWatching(); if (key_) { ::RegCloseKey(key_); key_ = NULL; @@ -168,7 +211,6 @@ HKEY RegKey::Take() { DCHECK(wow64access_ == 0); - StopWatching(); HKEY key = key_; key_ = NULL; return key; @@ -367,44 +409,14 @@ return result; } -LONG RegKey::StartWatching() { - DCHECK(key_); - if (!watch_event_) - watch_event_ = CreateEvent(NULL, TRUE, FALSE, NULL); +bool RegKey::StartWatching(const ChangeCallback& callback) { + if (!key_watcher_) + key_watcher_.reset(new Watcher(this)); - DWORD filter = REG_NOTIFY_CHANGE_NAME | - REG_NOTIFY_CHANGE_ATTRIBUTES | - REG_NOTIFY_CHANGE_LAST_SET | - REG_NOTIFY_CHANGE_SECURITY; + if (!key_watcher_.get()->StartWatching(key_, callback)) + return false; - // Watch the registry key for a change of value. - LONG result = RegNotifyChangeKeyValue(key_, TRUE, filter, watch_event_, TRUE); - if (result != ERROR_SUCCESS) { - CloseHandle(watch_event_); - watch_event_ = 0; - } - - return result; -} - -bool RegKey::HasChanged() { - if (watch_event_) { - if (WaitForSingleObject(watch_event_, 0) == WAIT_OBJECT_0) { - StartWatching(); - return true; - } - } - return false; -} - -LONG RegKey::StopWatching() { - LONG result = ERROR_INVALID_HANDLE; - if (watch_event_) { - CloseHandle(watch_event_); - watch_event_ = 0; - result = ERROR_SUCCESS; - } - return result; + return true; } // static
diff --git a/win/registry.h b/win/registry.h index e5524b8..c3e015b 100644 --- a/win/registry.h +++ b/win/registry.h
@@ -12,6 +12,8 @@ #include "base/base_export.h" #include "base/basictypes.h" #include "base/stl_util.h" +#include "base/win/object_watcher.h" +#include "base/win/scoped_handle.h" namespace base { namespace win { @@ -25,6 +27,9 @@ // are not touched in case of failure. class BASE_EXPORT RegKey { public: + // Called from the MessageLoop when the key changes. + typedef base::Callback<void()> ChangeCallback; + RegKey(); explicit RegKey(HKEY key); RegKey(HKEY rootkey, const wchar_t* subkey, REGSAM access); @@ -120,22 +125,16 @@ // Starts watching the key to see if any of its values have changed. // The key must have been opened with the KEY_NOTIFY access privilege. - LONG StartWatching(); + // Returns true on success. + // To stop watching, delete this RegKey object. To continue watching the + // object after the callback is invoked, call StartWatching again. + bool StartWatching(const ChangeCallback& callback); - // If StartWatching hasn't been called, always returns false. - // Otherwise, returns true if anything under the key has changed. - // This can't be const because the |watch_event_| may be refreshed. - bool HasChanged(); - - // Will automatically be called by destructor if not manually called - // beforehand. Returns true if it was watching, false otherwise. - LONG StopWatching(); - - inline bool IsWatching() const { return watch_event_ != 0; } - HANDLE watch_event() const { return watch_event_; } HKEY Handle() const { return key_; } private: + class Watcher; + // Calls RegDeleteKeyEx on supported platforms, alternatively falls back to // RegDeleteKey. static LONG RegDeleteKeyExWrapper(HKEY hKey, @@ -147,9 +146,10 @@ static LONG RegDelRecurse(HKEY root_key, const std::wstring& name, REGSAM access); + HKEY key_; // The registry key being iterated. - HANDLE watch_event_; REGSAM wow64access_; + scoped_ptr<Watcher> key_watcher_; DISALLOW_COPY_AND_ASSIGN(RegKey); };
diff --git a/win/registry_unittest.cc b/win/registry_unittest.cc index d2610ef..6548474 100644 --- a/win/registry_unittest.cc +++ b/win/registry_unittest.cc
@@ -7,7 +7,10 @@ #include <cstring> #include <vector> +#include "base/bind.h" #include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "base/run_loop.h" #include "base/stl_util.h" #include "base/win/windows_version.h" #include "testing/gtest/include/gtest/gtest.h" @@ -349,6 +352,68 @@ ASSERT_EQ(ERROR_SUCCESS, key.DeleteKey(L"foo")); } +class TestChangeDelegate { + public: + TestChangeDelegate() : called_(false) {} + ~TestChangeDelegate() {} + + void OnKeyChanged() { + MessageLoop::current()->QuitWhenIdle(); + called_ = true; + } + + bool WasCalled() { + bool was_called = called_; + called_ = false; + return was_called; + } + + private: + bool called_; +}; + +TEST_F(RegistryTest, ChangeCallback) { + RegKey key; + TestChangeDelegate delegate; + MessageLoop message_loop; + + std::wstring foo_key(kRootKey); + foo_key += L"\\Foo"; + ASSERT_EQ(ERROR_SUCCESS, key.Create(HKEY_CURRENT_USER, foo_key.c_str(), + KEY_READ)); + + ASSERT_TRUE(key.StartWatching(Bind(&TestChangeDelegate::OnKeyChanged, + Unretained(&delegate)))); + EXPECT_FALSE(delegate.WasCalled()); + + // Make some change. + RegKey key2; + ASSERT_EQ(ERROR_SUCCESS, key2.Open(HKEY_CURRENT_USER, foo_key.c_str(), + KEY_READ | KEY_SET_VALUE)); + ASSERT_TRUE(key2.Valid()); + EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name", L"data")); + + // Allow delivery of the notification. + EXPECT_FALSE(delegate.WasCalled()); + base::RunLoop().Run(); + + ASSERT_TRUE(delegate.WasCalled()); + EXPECT_FALSE(delegate.WasCalled()); + + ASSERT_TRUE(key.StartWatching(Bind(&TestChangeDelegate::OnKeyChanged, + Unretained(&delegate)))); + + // Change something else. + EXPECT_EQ(ERROR_SUCCESS, key2.WriteValue(L"name2", L"data2")); + base::RunLoop().Run(); + ASSERT_TRUE(delegate.WasCalled()); + + ASSERT_TRUE(key.StartWatching(Bind(&TestChangeDelegate::OnKeyChanged, + Unretained(&delegate)))); + base::RunLoop().RunUntilIdle(); + EXPECT_FALSE(delegate.WasCalled()); +} + } // namespace } // namespace win