blob: e97735906518cdcf18e27eca1bc4a95b6cc276dc [file] [log] [blame]
// Copyright 2013 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "syzygy/trace/common/service.h"
#include "base/logging.h"
namespace trace {
namespace common {
Service::Service(const base::StringPiece16& name)
: name_(name.begin(), name.end()),
state_(kUnused),
owning_thread_id_(base::PlatformThread::CurrentId()) {
DCHECK(!name.empty());
}
Service::~Service() {
}
void Service::set_instance_id(const base::StringPiece16& instance_id) {
DCHECK_EQ(kUnused, state_);
instance_id_.assign(instance_id.begin(), instance_id.end());
}
void Service::set_started_callback(ServiceCallback callback) {
DCHECK_EQ(kUnused, state_);
started_callback_ = callback;
}
void Service::set_interrupted_callback(ServiceCallback callback) {
DCHECK_EQ(kUnused, state_);
interrupted_callback_ = callback;
}
void Service::set_stopped_callback(ServiceCallback callback) {
DCHECK_EQ(kUnused, state_);
stopped_callback_ = callback;
}
Service::State Service::state() {
// This is only safe as reading and writing to a 32-bit value is atomic on
// x86. Otherwise we'd need a lock here.
return state_;
}
bool Service::Start() {
DCHECK_EQ(owning_thread_id_, base::PlatformThread::CurrentId());
DCHECK_EQ(kUnused, state_);
LOG(INFO) << "Starting the " << name_ << " service with instance ID \""
<< instance_id_ << "\".";
if (!StartImpl()) {
LOG(ERROR) << "Failed to stop " << name_ << " service with instance ID \""
<< instance_id_ << "\".";
set_state(kErrored);
return false;
}
return true;
}
bool Service::Stop() {
DCHECK_NE(kErrored, state_);
// No need to try to stop things again.
if (state_ == kStopping || state_ == kStopped)
return true;
LOG(INFO) << "Stopping the " << name_ << " service with instance ID \""
<< instance_id_ << "\".";
set_state(kStopping);
if (!StopImpl()) {
LOG(ERROR) << "Failed to stop " << name_ << " service with instance ID \""
<< instance_id_ << "\".";
set_state(kErrored);
return false;
}
return true;
}
bool Service::Join() {
DCHECK_EQ(owning_thread_id_, base::PlatformThread::CurrentId());
DCHECK_NE(kErrored, state_);
LOG(INFO) << "Joining the " << name_ << " service with instance ID \""
<< instance_id_ << "\".";
if (!JoinImpl()) {
LOG(ERROR) << "Failed to join " << name_ << " service with instance ID \""
<< instance_id_ << "\".";
set_state(kErrored);
return false;
}
// We expect the service implementation to have transitioned us to the stopped
// state as it finished work.
DCHECK_EQ(kStopped, state_);
return true;
}
bool Service::OnInitialized() {
DCHECK_EQ(kUnused, state_);
set_state(kInitialized);
return true;
}
bool Service::OnStarted() {
DCHECK_EQ(kInitialized, state_);
if (!started_callback_.is_null() && !started_callback_.Run(this))
return false;
set_state(kRunning);
return true;
}
bool Service::OnInterrupted() {
// A service can be interrupted from another thread, another instance of
// Service, another process, etc. Thus, it's completely valid for a Service
// instance to be interrupted in most any state, except stopped or in error.
DCHECK(state_ != kStopped && state_ != kErrored);
if (!interrupted_callback_.is_null() && !interrupted_callback_.Run(this))
return false;
return true;
}
bool Service::OnStopped() {
DCHECK_EQ(kStopping, state_);
if (!stopped_callback_.is_null() && !stopped_callback_.Run(this))
return false;
set_state(kStopped);
return true;
}
void Service::set_state(State state) {
// This is only safe because writing to state_ is atomic, otherwise we'd
// have to use a lock here.
OnStateChange(state_, state);
state_ = state;
}
} // namespace common
} // namespace trace