2
0
mirror of https://gitlab.isc.org/isc-projects/kea synced 2025-08-31 14:05:33 +00:00

[#1095] make state model thread safe

This commit is contained in:
Razvan Becheriu
2020-05-06 18:19:27 +03:00
parent 33d425fb03
commit cb523c4cd2
10 changed files with 193 additions and 41 deletions

View File

@@ -116,9 +116,9 @@ NameAddTransaction::verifyStates() {
NameChangeTransaction::verifyStates();
// Verify NameAddTransaction states by attempting to fetch them.
getState(ADDING_FWD_ADDRS_ST);
getState(REPLACING_FWD_ADDRS_ST);
getState(REPLACING_REV_PTRS_ST);
getStateInternal(ADDING_FWD_ADDRS_ST);
getStateInternal(REPLACING_FWD_ADDRS_ST);
getStateInternal(REPLACING_REV_PTRS_ST);
}
void

View File

@@ -120,9 +120,9 @@ NameRemoveTransaction::verifyStates() {
NameChangeTransaction::verifyStates();
// Verify NameRemoveTransaction states by attempting to fetch them.
getState(REMOVING_FWD_ADDRS_ST);
getState(REMOVING_FWD_RRS_ST);
getState(REMOVING_REV_PTRS_ST);
getStateInternal(REMOVING_FWD_ADDRS_ST);
getStateInternal(REMOVING_FWD_RRS_ST);
getStateInternal(REMOVING_REV_PTRS_ST);
}
void

View File

@@ -244,11 +244,11 @@ NameChangeTransaction::verifyStates() {
StateModel::verifyStates();
// Verify NCT states. This ensures that derivations provide the handlers.
getState(READY_ST);
getState(SELECTING_FWD_SERVER_ST);
getState(SELECTING_REV_SERVER_ST);
getState(PROCESS_TRANS_OK_ST);
getState(PROCESS_TRANS_FAILED_ST);
getStateInternal(READY_ST);
getStateInternal(SELECTING_FWD_SERVER_ST);
getStateInternal(SELECTING_REV_SERVER_ST);
getStateInternal(PROCESS_TRANS_OK_ST);
getStateInternal(PROCESS_TRANS_FAILED_ST);
}
void

View File

@@ -221,7 +221,7 @@ public:
NameChangeTransaction::verifyStates();
// Check our states.
getState(DOING_UPDATE_ST);
getStateInternal(DOING_UPDATE_ST);
}
// Expose the protected methods to be tested.

View File

@@ -337,7 +337,7 @@ HAService::partnerInMaintenanceStateHandler() {
switch (communication_state_->getPartnerState()) {
case HA_UNAVAILABLE_ST:
verboseTransition(HA_PARTNER_DOWN_ST);
/* Falls through. */
default:
postNextEvent(NOP_EVT);
}

View File

@@ -731,7 +731,6 @@ IfaceMgr::startDHCPReceiver(const uint16_t family) {
dhcp_receiver_.reset(new WatchedThread());
dhcp_receiver_->start(boost::bind(&IfaceMgr::receiveDHCP4Packets, this));
break;
case AF_INET6:
// If the queue doesn't exist, packet queing has been configured
@@ -1211,7 +1210,8 @@ Pkt4Ptr IfaceMgr::receive4Direct(uint32_t timeout_sec, uint32_t timeout_usec /*
// Note the external socket to call its callback without
// the lock taken so it can be deleted.
ex_sock = s;
break; }
break;
}
}
}

View File

@@ -74,14 +74,14 @@ LabeledValueSet::add(LabeledValuePtr entry) {
isc_throw(LabeledValueError,
"value: " << value << " is already defined as: "
<< getLabel(value));
}
}
map_[entry->getValue()]=entry;
map_[value] = entry;
}
void
LabeledValueSet::add(const int value, const std::string& label) {
add (LabeledValuePtr(new LabeledValue(value,label)));
add(LabeledValuePtr(new LabeledValue(value,label)));
}
const LabeledValuePtr&

View File

@@ -113,7 +113,7 @@ void
StateModel::runModel(unsigned int run_event) {
/// If the dictionaries aren't built bail out.
if (!dictionaries_initted_) {
abortModel("runModel invoked before model has been initialized");
abortModel("runModel invoked before model has been initialized");
}
try {
@@ -127,8 +127,7 @@ StateModel::runModel(unsigned int run_event) {
// Keep going until a handler sets next event to a NOP_EVT.
} while (!isModelDone() && getNextEvent() != NOP_EVT);
}
catch (const std::exception& ex) {
} catch (const std::exception& ex) {
// The model has suffered an unexpected exception. This constitutes
// a model violation and indicates a programmatic shortcoming.
// In theory, the model should account for all error scenarios and
@@ -144,6 +143,10 @@ StateModel::nopStateHandler() {
void
StateModel::initDictionaries() {
std::lock_guard<std::mutex> lock(*mutex_);
if (dictionaries_initted_) {
isc_throw(StateModelError, "Dictionaries already initialized");
}
// First let's build and verify the dictionary of events.
try {
defineEvents();
@@ -166,7 +169,7 @@ StateModel::initDictionaries() {
void
StateModel::defineEvent(unsigned int event_value, const std::string& label) {
if (!isModelNew()) {
if (!isModelNewInternal()) {
// Don't allow for self-modifying models.
isc_throw(StateModelError, "Events may only be added to a new model."
<< event_value << " - " << label);
@@ -193,7 +196,7 @@ StateModel::getEvent(unsigned int event_value) {
void
StateModel::defineState(unsigned int state_value, const std::string& label,
StateHandler handler, const StatePausing& state_pausing) {
if (!isModelNew()) {
if (!isModelNewInternal()) {
// Don't allow for self-modifying maps.
isc_throw(StateModelError, "States may only be added to a new model."
<< state_value << " - " << label);
@@ -209,6 +212,12 @@ StateModel::defineState(unsigned int state_value, const std::string& label,
const StatePtr
StateModel::getState(unsigned int state_value) {
std::lock_guard<std::mutex> lock(*mutex_);
return getStateInternal(state_value);
}
const StatePtr
StateModel::getStateInternal(unsigned int state_value) {
if (!states_.isDefined(state_value)) {
isc_throw(StateModelError,
"State value is not defined:" << state_value);
@@ -243,8 +252,8 @@ StateModel::defineStates() {
void
StateModel::verifyStates() {
getState(NEW_ST);
getState(END_ST);
getStateInternal(NEW_ST);
getStateInternal(END_ST);
}
void
@@ -254,8 +263,9 @@ StateModel::onModelFailure(const std::string&) {
void
StateModel::transition(unsigned int state, unsigned int event) {
setState(state);
postNextEvent(event);
std::lock_guard<std::mutex> lock(*mutex_);
setStateInternal(state);
postNextEventInternal(event);
}
void
@@ -265,6 +275,7 @@ StateModel::endModel() {
void
StateModel::unpauseModel() {
std::lock_guard<std::mutex> lock(*mutex_);
paused_ = false;
}
@@ -280,6 +291,11 @@ StateModel::abortModel(const std::string& explanation) {
void
StateModel::setState(unsigned int state) {
std::lock_guard<std::mutex> lock(*mutex_);
setStateInternal(state);
}
void
StateModel::setStateInternal(unsigned int state) {
if (state != END_ST && !states_.isDefined(state)) {
isc_throw(StateModelError,
"Attempt to set state to an undefined value: " << state );
@@ -296,13 +312,19 @@ StateModel::setState(unsigned int state) {
// If we're entering the new state we need to see if we should
// pause the state model in this state.
if (on_entry_flag_ && !paused_ && (getState(state)->shouldPause())) {
if (on_entry_flag_ && !paused_ && getStateInternal(state)->shouldPause()) {
paused_ = true;
}
}
void
StateModel::postNextEvent(unsigned int event_value) {
std::lock_guard<std::mutex> lock(*mutex_);
postNextEventInternal(event_value);
}
void
StateModel::postNextEventInternal(unsigned int event_value) {
// Check for FAIL_EVT as special case of model error before events are
// defined.
if (event_value != FAIL_EVT && !events_.isDefined(event_value)) {
@@ -310,13 +332,13 @@ StateModel::postNextEvent(unsigned int event_value) {
"Attempt to post an undefined event, value: " << event_value);
}
std::lock_guard<std::mutex> lock(*mutex_);
last_event_ = next_event_;
next_event_ = event_value;
}
bool
StateModel::doOnEntry() {
std::lock_guard<std::mutex> lock(*mutex_);
bool ret = on_entry_flag_;
on_entry_flag_ = false;
return (ret);
@@ -324,6 +346,7 @@ StateModel::doOnEntry() {
bool
StateModel::doOnExit() {
std::lock_guard<std::mutex> lock(*mutex_);
bool ret = on_exit_flag_;
on_exit_flag_ = false;
return (ret);
@@ -352,9 +375,15 @@ StateModel::getNextEvent() const {
std::lock_guard<std::mutex> lock(*mutex_);
return (next_event_);
}
bool
StateModel::isModelNew() const {
std::lock_guard<std::mutex> lock(*mutex_);
return isModelNewInternal();
}
bool
StateModel::isModelNewInternal() const {
return (curr_state_ == NEW_ST);
}
@@ -391,11 +420,23 @@ StateModel::isModelPaused() const {
std::string
StateModel::getStateLabel(const int state) const {
std::lock_guard<std::mutex> lock(*mutex_);
return getStateLabelInternal(state);
}
std::string
StateModel::getStateLabelInternal(const int state) const {
return (states_.getLabel(state));
}
std::string
StateModel::getEventLabel(const int event) const {
std::lock_guard<std::mutex> lock(*mutex_);
return getEventLabelInternal(event);
}
std::string
StateModel::getEventLabelInternal(const int event) const {
return (events_.getLabel(event));
}
@@ -404,10 +445,10 @@ StateModel::getContextStr() const {
std::lock_guard<std::mutex> lock(*mutex_);
std::ostringstream stream;
stream << "current state: [ "
<< curr_state_ << " " << getStateLabel(curr_state_)
<< curr_state_ << " " << getStateLabelInternal(curr_state_)
<< " ] next event: [ "
<< next_event_ << " " << getEventLabel(next_event_) << " ]";
return(stream.str());
<< next_event_ << " " << getEventLabelInternal(next_event_) << " ]";
return (stream.str());
}
std::string
@@ -415,10 +456,10 @@ StateModel::getPrevContextStr() const {
std::lock_guard<std::mutex> lock(*mutex_);
std::ostringstream stream;
stream << "previous state: [ "
<< prev_state_ << " " << getStateLabel(prev_state_)
<< prev_state_ << " " << getStateLabelInternal(prev_state_)
<< " ] last event: [ "
<< next_event_ << " " << getEventLabel(last_event_) << " ]";
return(stream.str());
<< next_event_ << " " << getEventLabelInternal(last_event_) << " ]";
return (stream.str());
}
} // namespace isc::util

View File

@@ -364,6 +364,7 @@ public:
void nopStateHandler();
protected:
/// @brief Initializes the event and state dictionaries.
///
/// This method invokes the define and verify methods for both events and
@@ -394,10 +395,15 @@ protected:
/// :
/// }
/// @endcode
///
/// This method is called in a thread safe context from
/// @ref initDictionaries.
virtual void defineEvents();
/// @brief Adds an event value and associated label to the set of events.
///
/// This method is called in a thread safe context from @ref defineEvents.
///
/// @param value is the numeric value of the event
/// @param label is the text label of the event used in log messages and
/// exceptions.
@@ -408,6 +414,8 @@ protected:
/// @brief Fetches the event referred to by value.
///
/// This method is called in a thread safe context from @ref verifyEvents.
///
/// @param value is the numeric value of the event desired.
///
/// @return returns a constant pointer reference to the event if found
@@ -438,6 +446,9 @@ protected:
/// :
/// }
/// @endcode
///
/// This method is called in a thread safe context from
/// @ref initDictionaries.
virtual void verifyEvents();
/// @brief Populates the set of states.
@@ -461,10 +472,15 @@ protected:
/// :
/// }
/// @endcode
///
/// This method is called in a thread safe context from
/// @ref initDictionaries.
virtual void defineStates();
/// @brief Adds an state value and associated label to the set of states.
///
/// This method is called in a thread safe context from @ref defineStates.
///
/// @param value is the numeric value of the state
/// @param label is the text label of the state used in log messages and
/// exceptions.
@@ -510,6 +526,9 @@ protected:
/// :
/// }
/// @endcode
///
/// This method is called in a thread safe context from
/// @ref initDictionaries.
virtual void verifyStates();
/// @brief Handler for fatal model execution errors.
@@ -595,6 +614,7 @@ protected:
bool doOnExit();
public:
/// @brief Fetches the model's current state.
///
/// This returns the model's notion of the current state. It is the
@@ -692,7 +712,98 @@ public:
/// @return Returns a std::string of the format described above.
std::string getPrevContextStr() const;
protected:
/// @brief Fetches the state referred to by value.
///
/// This method should be called in a thread safe context.
///
/// @param value is the numeric value of the state desired.
///
/// @return returns a constant pointer to the state if found
///
/// @throw StateModelError if the state is not defined.
const StatePtr getStateInternal(unsigned int value);
private:
/// @brief Sets the current state to the given state value.
///
/// This updates the model's notion of the current state and is the
/// state whose handler will be executed on the next iteration of the run
/// loop. This is intended primarily for internal use and testing. It is
/// unlikely that transitioning to a new state without a new event is of
/// much use.
/// This method should be called in a thread safe context.
///
/// @param state the new value to assign to the current state.
///
/// @throw StateModelError if the state is invalid.
void setStateInternal(unsigned int state);
/// @brief Sets the next event to the given event value.
///
/// This updates the model's notion of the next event and is the
/// event that will be passed into the current state's handler on the next
/// iteration of the run loop.
/// This method should be called in a thread safe context.
///
/// @param event the numeric event value to post as the next event.
///
/// @throw StateModelError if the event is undefined
void postNextEventInternal(unsigned int event);
/// @brief Returns whether or not the model is new.
///
/// This method should be called in a thread safe context.
///
/// @return Boolean true if the model has not been started.
bool isModelNewInternal() const;
/// @brief Fetches the label associated with an event value.
///
/// This method should be called in a thread safe context.
///
/// @param event is the numeric event value for which the label is desired.
///
/// @return Returns a string containing the event label or
/// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
std::string getEventLabelInternal(const int event) const;
/// @brief Fetches the label associated with an state value.
///
/// This method should be called in a thread safe context.
///
/// @param state is the numeric state value for which the label is desired.
///
/// @return Returns a const char* containing the state label or
/// LabeledValueSet::UNDEFINED_LABEL if the value is undefined.
std::string getStateLabelInternal(const int state) const;
/// @brief Convenience method which returns a string rendition of the
/// current state and next event.
///
/// The string will be of the form:
///
/// current state: [ {state} {label} ] next event: [ {event} {label} ]
///
/// This method should be called in a thread safe context.
///
/// @return Returns a std::string of the format described above.
std::string getContextStrInternal() const;
/// @brief Convenience method which returns a string rendition of the
/// previous state and last event.
///
/// The string will be of the form:
///
/// previous state: [ {state} {label} ] last event: [ {event} {label} ]
///
/// This method should be called in a thread safe context.
///
/// @return Returns a std::string of the format described above.
std::string getPrevContextStrInternal() const;
/// @brief The dictionary of valid events.
LabeledValueSet events_;

View File

@@ -234,12 +234,12 @@ public:
StateModel::verifyStates();
// Verify our states.
getState(DUMMY_ST);
getState(READY_ST);
getState(DO_WORK_ST);
getState(DONE_ST);
getState(PAUSE_ALWAYS_ST);
getState(PAUSE_ONCE_ST);
getStateInternal(DUMMY_ST);
getStateInternal(READY_ST);
getStateInternal(DO_WORK_ST);
getStateInternal(DONE_ST);
getStateInternal(PAUSE_ALWAYS_ST);
getStateInternal(PAUSE_ONCE_ST);
}
/// @brief Manually construct the event and state dictionaries.