diff --git a/ETL/ETL/_smach.h b/ETL/ETL/_smach.h index 9496e77..a21e035 100644 --- a/ETL/ETL/_smach.h +++ b/ETL/ETL/_smach.h @@ -27,6 +27,7 @@ /* === H E A D E R S ======================================================= */ +#include #include #include #include @@ -267,6 +268,21 @@ public: return ret; } }; + + class guard { + private: + bool *flag; + public: + explicit guard(bool &flag): flag() { + assert(!flag); + if (!flag) { flag = true; this->flag = &flag; } + } + ~guard() + { if (flag) *flag = false;} + operator bool() const + { return flag != nullptr; } + }; + private: @@ -282,6 +298,9 @@ private: const state_base* state_stack[SMACH_STATE_STACK_SIZE]; void* state_context_stack[SMACH_STATE_STACK_SIZE]; int states_on_stack; + + bool changing_state; + bool changing_default_state; public: @@ -306,36 +325,35 @@ public: bool set_default_state(const state_base *nextstate) { + guard lock(changing_default_state); + if (!lock) return false; + // Keep track of the current state unless // the state switch fails - const state_base *prev_state=default_state; - - // If we are already in a state, leave it and - // collapse the state stack - if(default_state && default_context) - default_state->leave_state(default_context); - - // Set this as our current state - default_state=nextstate; - default_context=0; - + const state_base *prev_state = default_state; + void *prev_context = default_context; + + default_state = 0; + default_context = 0; + + // If we are already in a state, leave it + if (prev_state && prev_context) + prev_state->leave_state(prev_context); + // Attempt to enter the state - if(default_state) - { - default_context=default_state->enter_state(machine_context); - if(default_context) + default_state=nextstate; + if (default_state) { + default_context = default_state->enter_state(machine_context); + if (default_context) return true; } - else - return true; // We failed, so attempt to return to previous state - default_state=prev_state; - - // If we had a previous state, enter it - if(default_state) { - default_context=default_state->enter_state(machine_context); - if (!default_context) default_context=0; + default_state = prev_state; + if (default_state) { + default_context = default_state->enter_state(machine_context); + if (!default_context) + default_state = 0; } // At this point we are not in the @@ -348,23 +366,28 @@ public: bool egress() { - // Pop all states off the state stack - while(states_on_stack) pop_state(); + // Try to pop all states off the state stack, before lock + while(states_on_stack) + if (!pop_state()) return false; + guard lock(changing_state); + if (!lock) return false; + // If we are not in a state, then I guess // we were successful. - if(!curr_state) + if (!curr_state) return true; - const state_base* old_state=curr_state; - void *old_context=state_context; + const state_base* old_state = curr_state; + void *old_context = state_context; // Clear out the current state and its state_context - curr_state=0;state_context=0; + curr_state = 0; + state_context = 0; // Leave the state if (old_state && old_context) - return old_state->leave_state(old_context); + old_state->leave_state(old_context); return true; } @@ -376,31 +399,35 @@ public: bool enter(const state_base *nextstate) { + guard lock(changing_state); + if (!lock) return false; + // Keep track of the current state unless // the state switch fails const state_base *prev_state=curr_state; // If we are already in a state, leave it and // collapse the state stack - if(curr_state) - egress(); - - // Set this as our current state - curr_state=nextstate; - state_context=0; + changing_state = false; + egress(); + assert(!state_context); + state_context = 0; + changing_state = true; // Attempt to enter the state - state_context=curr_state->enter_state(machine_context); - if(state_context) - return true; - - // We failed, so attempt to return to previous state - curr_state=prev_state; - + curr_state = nextstate; + if (curr_state) { + state_context = curr_state->enter_state(machine_context); + if (state_context) + return true; + } + // If we had a previous state, enter it - if(curr_state) { - state_context=curr_state->enter_state(machine_context); - if (!state_context) curr_state = 0; + curr_state = prev_state; + if (curr_state) { + state_context = curr_state->enter_state(machine_context); + if (!state_context) + curr_state = 0; } // At this point we are not in the @@ -417,58 +444,69 @@ public: bool push_state(const state_base *nextstate) { - // If there are not enough slots, then throw something. - if(states_on_stack==SMACH_STATE_STACK_SIZE) - throw(std::overflow_error("smach<>::push_state(): state stack overflow!")); - - // If there is no current state, nor anything on stack, + // If there is no current state, // just go ahead and enter the given state. - if(!curr_state && !states_on_stack) + // before locking + if (!curr_state) return enter(nextstate); + + guard lock(changing_state); + if (!lock) return false; - // Push the current state onto the stack - state_stack[states_on_stack]=curr_state; - state_context_stack[states_on_stack++]=state_context; + // If there are not enough slots, then throw something. + if (states_on_stack==SMACH_STATE_STACK_SIZE) + throw(std::overflow_error("smach<>::push_state(): state stack overflow!")); - // Make the next state the current state - curr_state=nextstate; + // Push the current state onto the stack + state_stack[states_on_stack] = curr_state; + state_context_stack[states_on_stack++] = state_context; // Try to enter the next state - state_context=curr_state->enter_state(machine_context); - if(state_context) - return true; + state_context = 0; + curr_state = nextstate; + if (curr_state) { + state_context = curr_state->enter_state(machine_context); + if (state_context) + return true; + } // Unable to push state, return to old one - curr_state=state_stack[--states_on_stack]; - state_context=state_context_stack[states_on_stack]; + curr_state = state_stack[--states_on_stack]; + state_context = state_context_stack[states_on_stack]; return false; } //! Pops state off of state stack /*! Decreases state depth */ - void + bool pop_state() { - // If we aren't in a state, then there is nothing - // to do. - if(!curr_state) - throw(std::underflow_error("smach<>::pop_state(): stack is empty!")); - - if(states_on_stack) - { - const state_base* old_state=curr_state; - void *old_context=state_context; - - // Pop previous state off of stack - --states_on_stack; - curr_state=state_stack[states_on_stack]; - state_context=state_context_stack[states_on_stack]; - - if (old_state && old_context) - old_state->leave_state(old_context); + { // scope for locking + guard lock(changing_state); + if (!lock) return false; + + // If we aren't in a state, then there is nothing + // to do. + if (!curr_state && !states_on_stack) + throw(std::underflow_error("smach<>::pop_state(): stack is empty!")); + + if (states_on_stack) { + const state_base* old_state = curr_state; + void *old_context = state_context; + + // Pop previous state off of stack + --states_on_stack; + curr_state = state_stack[states_on_stack]; + state_context = state_context_stack[states_on_stack]; + + if (old_state && old_context) + old_state->leave_state(old_context); + return true; + } } - else // If there are no states on stack, just egress - egress(); + + // call egress with changing_state unlocked + return egress(); } //! State Machine Constructor @@ -480,16 +518,30 @@ public: machine_context(machine_context), default_state(0), default_context(0), - states_on_stack(0) + states_on_stack(0), + changing_state(false), + changing_default_state(false) { } //! The destructor ~smach() { + assert(!changing_state); + changing_state = false; egress(); - if(default_state && default_context) - default_state->leave_state(default_context); + // reset default state + assert(!changing_default_state); + changing_default_state = false; + const state_base *prev_state = default_state; + void* prev_context = default_context; + default_state=0; + default_context=0; + if (prev_state && prev_context) + prev_state->leave_state(prev_context); + + assert(!default_state && !default_context); + assert(!curr_state && !state_context); } //! Sets up a child state machine diff --git a/synfig-studio/src/gui/docks/dock_toolbox.cpp b/synfig-studio/src/gui/docks/dock_toolbox.cpp index 4f9633f..ae6110f 100644 --- a/synfig-studio/src/gui/docks/dock_toolbox.cpp +++ b/synfig-studio/src/gui/docks/dock_toolbox.cpp @@ -296,6 +296,14 @@ Dock_Toolbox::update_tools() set_active_state("none"); } + +void +Dock_Toolbox::refresh() +{ + update_tools(); +} + + void Dock_Toolbox::on_drop_drag_data_received(const Glib::RefPtr& context, int /*x*/, int /*y*/, const Gtk::SelectionData& selection_data_, guint /*info*/, guint time) { diff --git a/synfig-studio/src/gui/docks/dock_toolbox.h b/synfig-studio/src/gui/docks/dock_toolbox.h index 4fb2a9a..6303543 100644 --- a/synfig-studio/src/gui/docks/dock_toolbox.h +++ b/synfig-studio/src/gui/docks/dock_toolbox.h @@ -75,15 +75,15 @@ class Dock_Toolbox : public Dockable void change_state_(const Smach::state_base *state); -public: + void update_tools(); - void change_state(const synfig::String& statename, bool force = false); + void set_active_state(const synfig::String& statename); - void update_tools(); +public: - void refresh() { update_tools(); } + void change_state(const synfig::String& statename, bool force = false); - void set_active_state(const synfig::String& statename); + void refresh(); void add_state(const Smach::state_base *state); diff --git a/synfig-studio/src/gui/docks/dockable.cpp b/synfig-studio/src/gui/docks/dockable.cpp index d5cf170..c219401 100644 --- a/synfig-studio/src/gui/docks/dockable.cpp +++ b/synfig-studio/src/gui/docks/dockable.cpp @@ -219,7 +219,10 @@ Dockable::reset_container() // Allocating size to widget without calling gtk_widget_get_preferred_width/height(). // How does the code know the size to allocate? // related with combination of Grid, ScrolledWindow and TreeView - App::process_all_events(); + //App::process_all_events(); + // Update: + // Seems bug in other place, process_all_events() here produces + // a concurrent event processing and collissions } void diff --git a/synfig-studio/src/gui/instance.cpp b/synfig-studio/src/gui/instance.cpp index c96d24a..e77bb3c 100644 --- a/synfig-studio/src/gui/instance.cpp +++ b/synfig-studio/src/gui/instance.cpp @@ -211,7 +211,7 @@ void Instance::set_undo_status(bool x) { undo_status_=x; - App::dock_toolbox->update_tools(); + App::dock_toolbox->refresh(); signal_undo_redo_status_changed()(); } @@ -219,7 +219,7 @@ void Instance::set_redo_status(bool x) { redo_status_=x; - App::dock_toolbox->update_tools(); + App::dock_toolbox->refresh(); signal_undo_redo_status_changed()(); }