diff options
author | Ben Boeckel <mathstuf@gmail.com> | 2013-08-02 19:41:45 (GMT) |
---|---|---|
committer | Brad King <brad.king@kitware.com> | 2013-08-08 17:31:10 (GMT) |
commit | f9bb20fe2bf1d9154a3244579ea84400912473b4 (patch) | |
tree | 7284afc04b2583fa0b5eaa8062498b7bd3c74bcc /Source/cmVariableWatchCommand.cxx | |
parent | 05dad99f5a1ee781da52df0ccc61b236087bcd2d (diff) | |
download | CMake-f9bb20fe2bf1d9154a3244579ea84400912473b4.zip CMake-f9bb20fe2bf1d9154a3244579ea84400912473b4.tar.gz CMake-f9bb20fe2bf1d9154a3244579ea84400912473b4.tar.bz2 |
variable_watch: Don't share memory for callbacks
The command itself is owned by the cmMakefile class, but the
cmVariableWatch which holds a pointer to the cmVariableWatchCommand via
the client_data for the callback outlives the cmMakefile class in the Qt
GUI. This means that when the cmMakefile is destroyed, the variable
watch is still in effect, but with a stale pointer.
To fix this, each callback is now a separate entity completely and
doesn't rely on the command which spawned it at all.
An example CMakeLists.txt which demonstrates the issue (only displayed
in cmake-gui, so no tests can be written for it):
set(var 0)
variable_watch(var)
Diffstat (limited to 'Source/cmVariableWatchCommand.cxx')
-rw-r--r-- | Source/cmVariableWatchCommand.cxx | 137 |
1 files changed, 83 insertions, 54 deletions
diff --git a/Source/cmVariableWatchCommand.cxx b/Source/cmVariableWatchCommand.cxx index 2b81d79..3b75ade 100644 --- a/Source/cmVariableWatchCommand.cxx +++ b/Source/cmVariableWatchCommand.cxx @@ -14,63 +14,27 @@ #include "cmVariableWatch.h" //---------------------------------------------------------------------------- -static void cmVariableWatchCommandVariableAccessed( - const std::string& variable, int access_type, void* client_data, - const char* newValue, const cmMakefile* mf) +struct cmVariableWatchCallbackData { - cmVariableWatchCommand* command - = static_cast<cmVariableWatchCommand*>(client_data); - command->VariableAccessed(variable, access_type, newValue, mf); -} + bool InCallback; + std::string Command; +}; //---------------------------------------------------------------------------- -cmVariableWatchCommand::cmVariableWatchCommand() +static void cmVariableWatchCommandVariableAccessed( + const std::string& variable, int access_type, void* client_data, + const char* newValue, const cmMakefile* mf) { - this->InCallback = false; -} + cmVariableWatchCallbackData* data + = static_cast<cmVariableWatchCallbackData*>(client_data); -//---------------------------------------------------------------------------- -bool cmVariableWatchCommand -::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &) -{ - if ( args.size() < 1 ) - { - this->SetError("must be called with at least one argument."); - return false; - } - std::string variable = args[0]; - if ( args.size() > 1 ) - { - std::string command = args[1]; - this->Handlers[variable].Commands.push_back(args[1]); - } - if ( variable == "CMAKE_CURRENT_LIST_FILE" ) - { - cmOStringStream ostr; - ostr << "cannot be set on the variable: " << variable.c_str(); - this->SetError(ostr.str().c_str()); - return false; - } - - this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch( - variable, cmVariableWatchCommandVariableAccessed, this); - - return true; -} - -//---------------------------------------------------------------------------- -void cmVariableWatchCommand::VariableAccessed(const std::string& variable, - int access_type, const char* newValue, const cmMakefile* mf) -{ - if ( this->InCallback ) + if ( data->InCallback ) { return; } - this->InCallback = true; + data->InCallback = true; cmListFileFunction newLFF; - cmVariableWatchCommandHandler *handler = &this->Handlers[variable]; - cmVariableWatchCommandHandler::VectorOfCommands::iterator it; cmListFileArgument arg; bool processed = false; const char* accessString = cmVariableWatch::GetAccessAsString(access_type); @@ -80,10 +44,8 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable, cmMakefile* makefile = const_cast<cmMakefile*>(mf); std::string stack = makefile->GetProperty("LISTFILE_STACK"); - for ( it = handler->Commands.begin(); it != handler->Commands.end(); - ++ it ) + if ( !data->Command.empty() ) { - std::string command = *it; newLFF.Arguments.clear(); newLFF.Arguments.push_back( cmListFileArgument(variable, true, "unknown", 9999)); @@ -95,7 +57,7 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable, cmListFileArgument(currentListFile, true, "unknown", 9999)); newLFF.Arguments.push_back( cmListFileArgument(stack, true, "unknown", 9999)); - newLFF.Name = command; + newLFF.Name = data->Command; newLFF.FilePath = "Some weird path"; newLFF.Line = 9999; cmExecutionStatus status; @@ -107,9 +69,9 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable, error << "Error in cmake code at\n" << arg.FilePath << ":" << arg.Line << ":\n" << "A command failed during the invocation of callback \"" - << command << "\"."; + << data->Command << "\"."; cmSystemTools::Error(error.str().c_str()); - this->InCallback = false; + data->InCallback = false; return; } processed = true; @@ -121,5 +83,72 @@ void cmVariableWatchCommand::VariableAccessed(const std::string& variable, << accessString << " with value \"" << newValue << "\"."; makefile->IssueMessage(cmake::LOG, msg.str()); } - this->InCallback = false; + + data->InCallback = false; +} + +//---------------------------------------------------------------------------- +static void deleteVariableWatchCallbackData(void* client_data) +{ + cmVariableWatchCallbackData* data + = static_cast<cmVariableWatchCallbackData*>(client_data); + delete data; +} + +//---------------------------------------------------------------------------- +cmVariableWatchCommand::cmVariableWatchCommand() +{ +} + +//---------------------------------------------------------------------------- +cmVariableWatchCommand::~cmVariableWatchCommand() +{ + std::set<std::string>::const_iterator it; + for ( it = this->WatchedVariables.begin(); + it != this->WatchedVariables.end(); + ++it ) + { + this->Makefile->GetCMakeInstance()->GetVariableWatch()->RemoveWatch( + *it, cmVariableWatchCommandVariableAccessed); + } +} + +//---------------------------------------------------------------------------- +bool cmVariableWatchCommand +::InitialPass(std::vector<std::string> const& args, cmExecutionStatus &) +{ + if ( args.size() < 1 ) + { + this->SetError("must be called with at least one argument."); + return false; + } + std::string variable = args[0]; + std::string command; + if ( args.size() > 1 ) + { + command = args[1]; + } + if ( variable == "CMAKE_CURRENT_LIST_FILE" ) + { + cmOStringStream ostr; + ostr << "cannot be set on the variable: " << variable.c_str(); + this->SetError(ostr.str().c_str()); + return false; + } + + cmVariableWatchCallbackData* data = new cmVariableWatchCallbackData; + + data->InCallback = false; + data->Command = command; + + this->WatchedVariables.insert(variable); + if ( !this->Makefile->GetCMakeInstance()->GetVariableWatch()->AddWatch( + variable, cmVariableWatchCommandVariableAccessed, + data, deleteVariableWatchCallbackData) ) + { + deleteVariableWatchCallbackData(data); + return false; + } + + return true; } |