Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(49)

Unified Diff: chrome/common/service_process_util_posix.cc

Issue 6349029: Get service processes working on Mac and Linux. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: fix up linux build Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/common/service_process_util_posix.cc
diff --git a/chrome/common/service_process_util_posix.cc b/chrome/common/service_process_util_posix.cc
index e547223865decae38b3fd719bbb2f8fea2f75cd0..603afa450feb1b356e5294ffef4c46a0bc1b7c8b 100644
--- a/chrome/common/service_process_util_posix.cc
+++ b/chrome/common/service_process_util_posix.cc
@@ -4,14 +4,22 @@
#include "chrome/common/service_process_util.h"
+#include <signal.h>
+#include <unistd.h>
+
#include "base/file_util.h"
#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/message_pump_libevent.h"
#include "base/path_service.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_version_info.h"
+#include "chrome/common/multi_process_lock.h"
namespace {
+int g_signal_socket = -1;
+
// Gets the name of the lock file for service process.
FilePath GetServiceProcessLockFilePath() {
FilePath user_data_dir;
@@ -21,45 +29,182 @@ FilePath GetServiceProcessLockFilePath() {
return user_data_dir.Append(lock_file_name);
}
-} // namespace
+// Attempts to take a lock named |name|. If |waiting| then is will
Scott Byer 2011/02/02 00:12:38 nit: then this will
dmac 2011/02/02 00:56:25 Done.
+// make multiple attempts before failing.
+// Caller is responsible for ownership of the MultiProcessLock.
+MultiProcessLock* TakeNamedLock(const std::string& name, bool waiting) {
+ scoped_ptr<MultiProcessLock> lock(MultiProcessLock::Create(name));
+ if (lock == NULL) return NULL;
+ bool got_lock = false;
+ for (int i = 0; i < 10; ++i) {
+ if (lock->TryLock()) {
+ got_lock = true;
+ break;
+ }
+ if (!waiting) break;
+ base::PlatformThread::Sleep(100 * i);
+ }
+ if (!got_lock) {
+ lock.reset();
+ }
+ return lock.release();
+}
-bool ForceServiceProcessShutdown(const std::string& version) {
- NOTIMPLEMENTED();
- return false;
+MultiProcessLock* TakeServiceRunningLock(bool waiting) {
+ std::string lock_name =
+ GetServiceProcessScopedName("_service_running");
+ return TakeNamedLock(lock_name, waiting);
}
-bool CheckServiceProcessReady() {
- const FilePath path = GetServiceProcessLockFilePath();
- return file_util::PathExists(path);
+MultiProcessLock* TakeServiceInitializingLock(bool waiting) {
+ std::string lock_name =
+ GetServiceProcessScopedName("_service_initializing");
+ return TakeNamedLock(lock_name, waiting);
}
-struct ServiceProcessState::StateData {
- // No state yet for Posix.
+// Watches for |kShutDownMessage| to be written to the file descriptor it is
+// watching. When it reads |kShutDownMessage|, it performs |shutdown_task_|.
+// Used here to monitor the socket listening to g_signal_socket.
+class ServiceProcessShutdownMonitor
+ : public base::MessagePumpLibevent::Watcher {
+ public:
+
+ enum {
+ kShutDownMessage = 0xdecea5e
+ };
+
+ explicit ServiceProcessShutdownMonitor(Task* shutdown_task)
+ : shutdown_task_(shutdown_task) {
+ }
+
+ virtual ~ServiceProcessShutdownMonitor();
+
+ virtual void OnFileCanReadWithoutBlocking(int fd);
+ virtual void OnFileCanWriteWithoutBlocking(int fd);
+
+ private:
+ scoped_ptr<Task> shutdown_task_;
};
-bool ServiceProcessState::TakeSingletonLock() {
- // TODO(sanjeevr): Implement singleton mechanism for POSIX.
+ServiceProcessShutdownMonitor::~ServiceProcessShutdownMonitor() {
+}
+
+void ServiceProcessShutdownMonitor::OnFileCanReadWithoutBlocking(int fd) {
+ if (shutdown_task_.get()) {
+ int buffer;
+ int length = read(fd, &buffer, sizeof(buffer));
+ if ((length == sizeof(kShutDownMessage)) && (buffer == kShutDownMessage)) {
Scott Byer 2011/02/02 00:12:38 Should be sizeof(buffer) to match the write side.
dmac 2011/02/02 00:56:25 Done.
+ shutdown_task_->Run();
+ shutdown_task_.reset();
+ } else if (length > 0) {
+ LOG(ERROR) << "Unexpected read: " << buffer;
+ } else if (length == 0) {
+ LOG(ERROR) << "Unexpected fd close";
+ } else if (length < 0) {
+ PLOG(ERROR) << "read";
+ }
+ }
+}
+
+void ServiceProcessShutdownMonitor::OnFileCanWriteWithoutBlocking(int fd) {
NOTIMPLEMENTED();
+}
+
+// "Forced" Shutdowns on POSIX are done via signals. The magic signal for
+// a shutdown is SIGTERM. "write" is a signal safe function. PLOG(ERROR) is
+// not, but we don't ever expect it to be called.
+void SigTermHandler(int sig, siginfo_t* info, void* uap) {
+ // TODO(dmaclach): add security here to make sure that we are being shut
+ // down by an appropriate process.
+ int message = ServiceProcessShutdownMonitor::kShutDownMessage;
+ if (write(g_signal_socket, &message, sizeof(message)) < 0) {
+ PLOG(ERROR) << "write";
+ }
+}
+
+} // namespace
+
+// See comment for SigTermHandler.
+bool ForceServiceProcessShutdown(const std::string& version,
+ base::ProcessId process_id) {
+ if (kill(process_id, SIGTERM) < 0) {
+ PLOG(ERROR) << "kill";
+ return false;
+ }
return true;
}
-void ServiceProcessState::SignalReady(Task* shutdown_task) {
- // TODO(hclam): Implement better mechanism for these platform.
- // Also we need to save shutdown task. For now we just delete the shutdown
- // task because we have not way to listen for shutdown requests.
- delete shutdown_task;
- const FilePath path = GetServiceProcessLockFilePath();
- FILE* file = file_util::OpenFile(path, "wb+");
- if (!file)
- return;
- VLOG(1) << "Created Service Process lock file: " << path.value();
- file_util::TruncateFile(file) && file_util::CloseFile(file);
+bool CheckServiceProcessReady() {
+ scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false));
+ return running_lock.get() == NULL;
+}
+
+struct ServiceProcessState::StateData
+ : public base::RefCountedThreadSafe<ServiceProcessState::StateData> {
+ scoped_ptr<MultiProcessLock> initializing_lock_;
+ scoped_ptr<MultiProcessLock> running_lock_;
+ scoped_ptr<ServiceProcessShutdownMonitor> shut_down_monitor_;
+ base::MessagePumpLibevent::FileDescriptorWatcher watcher_;
+ int sockets_[2];
+ struct sigaction old_action_;
+ bool set_action_;
+
+ // WatchFileDescriptor needs to be set up by the thread that is going
+ // to be monitoring it.
+ void SignalReady() {
+ CHECK(MessageLoopForIO::current()->WatchFileDescriptor(
+ sockets_[0], true, MessageLoopForIO::WATCH_READ,
+ &watcher_, shut_down_monitor_.get()));
+ g_signal_socket = sockets_[1];
+
+ // Set up signal handler for SIGTERM.
+ struct sigaction action;
+ action.sa_sigaction = SigTermHandler;
+ sigemptyset(&action.sa_mask);
+ action.sa_flags = SA_SIGINFO;
+ if (sigaction(SIGTERM, &action, &old_action_) == 0) {
+ // If the old_action is not default, somebody else has installed a
+ // a competing handler. Our handler is going to override it so it
+ // won't be called. If this occurs it needs to be fixed.
+ DCHECK_EQ(old_action_.sa_handler, SIG_DFL);
+ set_action_ = true;
+ initializing_lock_.reset();
+ } else {
+ PLOG(ERROR) << "sigaction";
+ }
+ }
+};
+
+bool ServiceProcessState::TakeSingletonLock() {
+ CHECK(!state_);
+ state_ = new StateData;
+ state_->AddRef();
+ state_->sockets_[0] = -1;
+ state_->sockets_[1] = -1;
+ state_->set_action_ = false;
+ state_->initializing_lock_.reset(TakeServiceInitializingLock(true));
+ return state_->initializing_lock_.get();
}
-void ServiceProcessState::SignalStopped() {
- const FilePath path = GetServiceProcessLockFilePath();
- file_util::Delete(path, false);
- shared_mem_service_data_.reset();
+bool ServiceProcessState::SignalReady(MessageLoop* message_loop,
+ Task* shutdown_task) {
+ CHECK(state_);
+ CHECK_EQ(g_signal_socket, -1);
+ DCHECK_EQ(message_loop->type(), MessageLoop::TYPE_IO);
+
+ state_->running_lock_.reset(TakeServiceRunningLock(true));
+ if (state_->running_lock_.get() == NULL) {
+ return false;
+ }
+ state_->shut_down_monitor_.reset(
+ new ServiceProcessShutdownMonitor(shutdown_task));
+ if (pipe(state_->sockets_) < 0) {
+ PLOG(ERROR) << "pipe";
+ return false;
+ }
+ message_loop->PostTask(FROM_HERE,
+ NewRunnableMethod(state_, &ServiceProcessState::StateData::SignalReady));
+ return true;
}
bool ServiceProcessState::AddToAutoRun() {
@@ -73,11 +218,25 @@ bool ServiceProcessState::RemoveFromAutoRun() {
}
void ServiceProcessState::TearDownState() {
+ g_signal_socket = -1;
+ if (state_) {
+ if (state_->sockets_[0] != -1) {
+ close(state_->sockets_[0]);
+ }
+ if (state_->sockets_[1] != -1) {
+ close(state_->sockets_[1]);
+ }
+ if (state_->set_action_) {
+ if (sigaction(SIGTERM, &state_->old_action_, NULL) < 0) {
+ PLOG(ERROR) << "sigaction";
+ }
+ }
+ state_->Release();
+ state_ = NULL;
+ }
}
bool ServiceProcessState::ShouldHandleOtherVersion() {
- // On POSIX, the shared memory is a file in disk. We may have a stale file
- // lying around from a previous run. So the check is not reliable.
- return false;
+ scoped_ptr<MultiProcessLock> running_lock(TakeServiceRunningLock(false));
+ return running_lock.get() == NULL;
Scott Byer 2011/02/02 00:12:38 Shouldn't this just call CheckServiceProcessReady(
dmac 2011/02/02 00:56:25 Got rid of it instead.
}
-

Powered by Google App Engine
This is Rietveld 408576698