diff --git a/Help/manual/cmake-instrumentation.7.rst b/Help/manual/cmake-instrumentation.7.rst index 29f2c85ff0..582adf8e42 100644 --- a/Help/manual/cmake-instrumentation.7.rst +++ b/Help/manual/cmake-instrumentation.7.rst @@ -66,14 +66,20 @@ Indexing Indexing is the process of collating generated instrumentation data. The available hooks to trigger indexing include options such as after every build, -or every :manual:`ctest ` invocation, and are configured as part of the `v1 Query Files`_. -Whenever a hook is triggered, an index file is generated containing a list of -snippet files newer than the previous indexing. This index file is passed to -user-defined `Callbacks`_ commands to process the data. +or every :manual:`ctest ` invocation, and are configured as part of +the `v1 Query Files`_. Whenever a hook is triggered, an index file is generated +containing a list of snippet files newer than the previous indexing. This index +file is passed to user-defined `Callbacks`_ commands to process the data. Indexing and can also be performed by manually invoking :option:`ctest --collect-instrumentation`. +Indexing, and the subsequent callbacks, will not occur concurrently in a +single build tree. When multiple hooks trigger indexing at the same time, +a file-based lock is used to ensure one indexing completes, executes all of its +callbacks, and deletes the instrumentation data before the next indexing can +begin. + .. _`cmake-instrumentation Callbacks`: Callbacks diff --git a/Source/cmInstrumentation.cxx b/Source/cmInstrumentation.cxx index b0adf60d1f..d866c73455 100644 --- a/Source/cmInstrumentation.cxx +++ b/Source/cmInstrumentation.cxx @@ -348,6 +348,8 @@ int cmInstrumentation::CollectTimingData(cmInstrumentationQuery::Hook hook) return 0; } + this->LockIndexing(); + // Touch index file immediately to claim snippets std::string suffix_time = ComputeSuffixTime(); std::string const& index_name = cmStrCat("index-", suffix_time, ".json"); @@ -428,6 +430,8 @@ int cmInstrumentation::CollectTimingData(cmInstrumentationQuery::Hook hook) this->RemoveOldFiles("content"); this->RemoveOldFiles("trace"); + this->indexLock.Release(); + return 0; } @@ -837,7 +841,7 @@ int cmInstrumentation::SpawnBuildDaemon() // preBuild Hook if (this->LockBuildDaemon()) { // Release lock before spawning the build daemon, to prevent blocking it. - this->lock.Release(); + this->buildLock.Release(); this->CollectTimingData(cmInstrumentationQuery::Hook::PreBuild); } @@ -859,11 +863,26 @@ int cmInstrumentation::SpawnBuildDaemon() // Prevent multiple build daemons from running simultaneously bool cmInstrumentation::LockBuildDaemon() { - std::string const lockFile = cmStrCat(this->timingDirv1, "/.build.lock"); - if (!cmSystemTools::FileExists(lockFile)) { - cmSystemTools::Touch(lockFile, true); + // 0 = non-blocking, 0s timeout + return this->AcquireLock(".build.lock", this->buildLock, 0); +} + +// Prevent multiple index processes from claiming snippets simultaneously +bool cmInstrumentation::LockIndexing() +{ + return this->AcquireLock(".index.lock", this->indexLock, + // -1 = no timeout + static_cast(-1)); +} + +bool cmInstrumentation::AcquireLock(std::string const& lock_file, + cmFileLock& lock, unsigned long timeout) +{ + std::string const lock_path = cmStrCat(this->timingDirv1, '/', lock_file); + if (!cmSystemTools::FileExists(lock_path)) { + cmSystemTools::Touch(lock_path, true); } - return this->lock.Lock(lockFile, 0).IsOk(); + return lock.Lock(lock_path, timeout).IsOk(); } /* @@ -890,6 +909,7 @@ int cmInstrumentation::CollectTimingAfterBuild(int ppid) int ret = this->InstrumentCommand( "build", {}, [waitForBuild]() { return waitForBuild(); }, cm::nullopt, cm::nullopt, LoadQueriesAfter::Yes); + this->buildLock.Release(); this->CollectTimingData(cmInstrumentationQuery::Hook::PostBuild); return ret; } diff --git a/Source/cmInstrumentation.h b/Source/cmInstrumentation.h index 33b15c5ba0..c99a221274 100644 --- a/Source/cmInstrumentation.h +++ b/Source/cmInstrumentation.h @@ -70,6 +70,7 @@ public: int CollectTimingData(cmInstrumentationQuery::Hook hook); int SpawnBuildDaemon(); bool LockBuildDaemon(); + bool LockIndexing(); int CollectTimingAfterBuild(int ppid); void AddHook(cmInstrumentationQuery::Hook hook); void AddOption(cmInstrumentationQuery::Option option); @@ -87,6 +88,8 @@ public: private: Json::Value ReadJsonSnippet(std::string const& file_name); + bool AcquireLock(std::string const& lock_file, cmFileLock& lock, + unsigned long timeout); void WriteInstrumentationJson(Json::Value& index, std::string const& directory, std::string const& file_name); @@ -134,5 +137,6 @@ private: cmsys::SystemInformation& GetSystemInformation(); #endif int writtenJsonQueries = 0; - cmFileLock lock; + cmFileLock buildLock; // non-blocking + cmFileLock indexLock; // never held alongside buildLock };