blob: 0dcdf9d1b669664964c6718841c3b625419e7950 [file] [log] [blame]
/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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 "halstate.h"
HalState::HalState() : mState(State::Constructed) {
}
bool HalState::init() {
// Ensure that checking the state and waiting on the condition is atomic.
std::unique_lock<std::mutex> lock(mStateMutex);
if (mState != State::Stopped && mState != State::Constructed) {
// We can only initialize when freshly constructed or stopped
return false;
}
if (mInfo) {
// We are in the correct state but our info object is still allocated.
// This is a logic error somewhere and should not happen.
return false;
}
auto info = std::make_unique<Info>();
if (info->init()) {
// Only store the info object to keep it alive if init succeeded.
// Otherwise we're going to remain in the previous state and having an
// uninitialized info object around will cause inconsistency.
mInfo = std::move(info);
mState = State::Initialized;
return true;
}
// If we failed to initalize we remain in the same state.
return false;
}
bool HalState::stop(StopHandler stopHandler) {
{
// Ensure atomicity in checking and setting state
std::unique_lock<std::mutex> lock(mStateMutex);
if (mState == State::Stopping || mState == State::Stopped) {
// Already stopping or stopped, nothing to do
return false;
}
if (mState != State::Running) {
// Make sure there is no info object anymore. It should never exist
// in the stopped state.
mInfo.reset();
// If we're neither stopping, stopped nor running then we can't stop
// again. It seems that sometimes the client expects to be able to
// call this when initialized or constructed so we'll set the state
// to stopped. We have to return false to prevent the caller from
// waiting for the callback though. Calling the callback here from
// the same thread that called stop could cause a deadlock.
mState = State::Stopped;
return false;
}
mState = State::Stopping;
}
mInfo->stop(std::bind(&HalState::onStop, this, stopHandler));
// We have now requested the stop, we'll change state in the stop handler
// when it's called.
return true;
}
bool HalState::eventLoop() {
{
// Atomically check and set state to running
std::unique_lock<std::mutex> lock(mStateMutex);
if (mState != State::Initialized || !mInfo) {
return false;
}
mState = State::Running;
}
mInfo->eventLoop();
return true;
}
Info* HalState::info() {
return mInfo.get();
}
void HalState::onStop(StopHandler stopHandler) {
stopHandler();
mInfo.reset();
std::unique_lock<std::mutex> lock(mStateMutex);
mState = State::Stopped;
}