VkPipelineCache: Do not publically expose internal mutexes
They make it unnecessarily easy to write code that violates the mutex lock requirements.
Replace with the `getOrCreate` pattern already used in vk::Device, which keeps the locking internal.
This change fixes a couple of places that we were accesssing data without correct locking (real bugs).
Bug: b/153194656
Change-Id: I132c450594f4042160b575197789bca7f1a5e25f
Reviewed-on: https://swiftshader-review.googlesource.com/c/SwiftShader/+/43650
Kokoro-Result: kokoro <noreply+kokoro@google.com>
Tested-by: Ben Clayton <bclayton@google.com>
Reviewed-by: Nicolas Capens <nicolascapens@google.com>
diff --git a/src/Vulkan/VkDevice.hpp b/src/Vulkan/VkDevice.hpp
index 3ba442d..8931949 100644
--- a/src/Vulkan/VkDevice.hpp
+++ b/src/Vulkan/VkDevice.hpp
@@ -86,8 +86,14 @@
};
};
+ // getOrCreate() queries the cache for a Routine with the given key.
+ // If one is found, it is returned, otherwise createRoutine(key) is
+ // called, the returned Routine is added to the cache, and it is
+ // returned.
+ // Function must be a function of the signature:
+ // std::shared_ptr<rr::Routine>(const Key &)
template<typename Function>
- std::shared_ptr<rr::Routine> getOrCreate(const Key &key, Function createRoutine)
+ std::shared_ptr<rr::Routine> getOrCreate(const Key &key, Function &&createRoutine)
{
std::lock_guard<std::mutex> lock(mutex);
diff --git a/src/Vulkan/VkPipeline.cpp b/src/Vulkan/VkPipeline.cpp
index e8182b2..0de27b2 100644
--- a/src/Vulkan/VkPipeline.cpp
+++ b/src/Vulkan/VkPipeline.cpp
@@ -483,21 +483,10 @@
if(pPipelineCache)
{
- PipelineCache &pipelineCache = *pPipelineCache;
- {
- std::unique_lock<std::mutex> lock(pipelineCache.getShaderMutex());
- const std::shared_ptr<sw::SpirvShader> *spirvShader = pipelineCache[key];
- if(!spirvShader)
- {
- auto shader = createShader(key, module, robustBufferAccess, device->getDebuggerContext());
- setShader(pipelineStage, shader);
- pipelineCache.insert(key, getShader(pipelineStage));
- }
- else
- {
- setShader(pipelineStage, *spirvShader);
- }
- }
+ auto shader = pPipelineCache->getOrCreateShader(key, [&] {
+ return createShader(key, module, robustBufferAccess, device->getDebuggerContext());
+ });
+ setShader(pipelineStage, shader);
}
else
{
@@ -583,35 +572,14 @@
stage.stage, stage.pName, module->getCode(), nullptr, 0, stage.pSpecializationInfo);
if(pPipelineCache)
{
- PipelineCache &pipelineCache = *pPipelineCache;
- {
- std::unique_lock<std::mutex> lock(pipelineCache.getShaderMutex());
- const std::shared_ptr<sw::SpirvShader> *spirvShader = pipelineCache[shaderKey];
- if(!spirvShader)
- {
- shader = createShader(shaderKey, module, robustBufferAccess, device->getDebuggerContext());
- pipelineCache.insert(shaderKey, shader);
- }
- else
- {
- shader = *spirvShader;
- }
- }
+ shader = pPipelineCache->getOrCreateShader(shaderKey, [&] {
+ return createShader(shaderKey, module, robustBufferAccess, device->getDebuggerContext());
+ });
- {
- const PipelineCache::ComputeProgramKey programKey(shader.get(), layout);
- std::unique_lock<std::mutex> lock(pipelineCache.getProgramMutex());
- const std::shared_ptr<sw::ComputeProgram> *computeProgram = pipelineCache[programKey];
- if(!computeProgram)
- {
- program = createProgram(programKey);
- pipelineCache.insert(programKey, program);
- }
- else
- {
- program = *computeProgram;
- }
- }
+ const PipelineCache::ComputeProgramKey programKey(shader.get(), layout);
+ program = pPipelineCache->getOrCreateComputeProgram(programKey, [&] {
+ return createProgram(programKey);
+ });
}
else
{
diff --git a/src/Vulkan/VkPipelineCache.cpp b/src/Vulkan/VkPipelineCache.cpp
index f54ac90..c725b3f 100644
--- a/src/Vulkan/VkPipelineCache.cpp
+++ b/src/Vulkan/VkPipelineCache.cpp
@@ -224,12 +224,14 @@
PipelineCache *srcCache = Cast(pSrcCaches[i]);
{
- std::unique_lock<std::mutex> lock(spirvShadersMutex);
+ std::unique_lock<std::mutex> thisLock(spirvShadersMutex);
+ std::unique_lock<std::mutex> srcLock(srcCache->spirvShadersMutex);
spirvShaders.insert(srcCache->spirvShaders.begin(), srcCache->spirvShaders.end());
}
{
- std::unique_lock<std::mutex> lock(computeProgramsMutex);
+ std::unique_lock<std::mutex> thisLock(computeProgramsMutex);
+ std::unique_lock<std::mutex> srcLock(srcCache->computeProgramsMutex);
computePrograms.insert(srcCache->computePrograms.begin(), srcCache->computePrograms.end());
}
}
@@ -237,26 +239,4 @@
return VK_SUCCESS;
}
-const std::shared_ptr<sw::SpirvShader> *PipelineCache::operator[](const PipelineCache::SpirvShaderKey &key) const
-{
- auto it = spirvShaders.find(key);
- return (it != spirvShaders.end()) ? &(it->second) : nullptr;
-}
-
-void PipelineCache::insert(const PipelineCache::SpirvShaderKey &key, const std::shared_ptr<sw::SpirvShader> &shader)
-{
- spirvShaders[key] = shader;
-}
-
-const std::shared_ptr<sw::ComputeProgram> *PipelineCache::operator[](const PipelineCache::ComputeProgramKey &key) const
-{
- auto it = computePrograms.find(key);
- return (it != computePrograms.end()) ? &(it->second) : nullptr;
-}
-
-void PipelineCache::insert(const PipelineCache::ComputeProgramKey &key, const std::shared_ptr<sw::ComputeProgram> &computeProgram)
-{
- computePrograms[key] = computeProgram;
-}
-
} // namespace vk
diff --git a/src/Vulkan/VkPipelineCache.hpp b/src/Vulkan/VkPipelineCache.hpp
index 42b4df6..85c5836 100644
--- a/src/Vulkan/VkPipelineCache.hpp
+++ b/src/Vulkan/VkPipelineCache.hpp
@@ -93,9 +93,13 @@
const SpecializationInfo specializationInfo;
};
- std::mutex &getShaderMutex() { return spirvShadersMutex; }
- const std::shared_ptr<sw::SpirvShader> *operator[](const PipelineCache::SpirvShaderKey &key) const;
- void insert(const PipelineCache::SpirvShaderKey &key, const std::shared_ptr<sw::SpirvShader> &shader);
+ // getOrCreateShader() queries the cache for a shader with the given key.
+ // If one is found, it is returned, otherwise create() is called, the
+ // returned shader is added to the cache, and it is returned.
+ // Function must be a function of the signature:
+ // std::shared_ptr<sw::SpirvShader>()
+ template<typename Function>
+ inline std::shared_ptr<sw::SpirvShader> getOrCreateShader(const PipelineCache::SpirvShaderKey &key, Function &&create);
struct ComputeProgramKey
{
@@ -117,9 +121,14 @@
const vk::PipelineLayout *layout;
};
- std::mutex &getProgramMutex() { return computeProgramsMutex; }
- const std::shared_ptr<sw::ComputeProgram> *operator[](const PipelineCache::ComputeProgramKey &key) const;
- void insert(const PipelineCache::ComputeProgramKey &key, const std::shared_ptr<sw::ComputeProgram> &computeProgram);
+ // getOrCreateComputeProgram() queries the cache for a compute program with
+ // the given key.
+ // If one is found, it is returned, otherwise create() is called, the
+ // returned program is added to the cache, and it is returned.
+ // Function must be a function of the signature:
+ // std::shared_ptr<sw::ComputeProgram>()
+ template<typename Function>
+ inline std::shared_ptr<sw::ComputeProgram> getOrCreateComputeProgram(const PipelineCache::ComputeProgramKey &key, Function &&create);
private:
struct CacheHeader
@@ -135,10 +144,10 @@
uint8_t *data = nullptr;
std::mutex spirvShadersMutex;
- std::map<SpirvShaderKey, std::shared_ptr<sw::SpirvShader>> spirvShaders;
+ std::map<SpirvShaderKey, std::shared_ptr<sw::SpirvShader>> spirvShaders; // guarded by spirvShadersMutex
std::mutex computeProgramsMutex;
- std::map<ComputeProgramKey, std::shared_ptr<sw::ComputeProgram>> computePrograms;
+ std::map<ComputeProgramKey, std::shared_ptr<sw::ComputeProgram>> computePrograms; // guarded by computeProgramsMutex
};
static inline PipelineCache *Cast(VkPipelineCache object)
@@ -146,6 +155,32 @@
return PipelineCache::Cast(object);
}
+template<typename Function>
+std::shared_ptr<sw::ComputeProgram> PipelineCache::getOrCreateComputeProgram(const PipelineCache::ComputeProgramKey &key, Function &&create)
+{
+ std::unique_lock<std::mutex> lock(computeProgramsMutex);
+
+ auto it = computePrograms.find(key);
+ if(it != computePrograms.end()) { return it->second; }
+
+ auto created = create();
+ computePrograms.emplace(key, created);
+ return created;
+}
+
+template<typename Function>
+std::shared_ptr<sw::SpirvShader> PipelineCache::getOrCreateShader(const PipelineCache::SpirvShaderKey &key, Function &&create)
+{
+ std::unique_lock<std::mutex> lock(spirvShadersMutex);
+
+ auto it = spirvShaders.find(key);
+ if(it != spirvShaders.end()) { return it->second; }
+
+ auto created = create();
+ spirvShaders.emplace(key, created);
+ return created;
+}
+
} // namespace vk
#endif // VK_PIPELINE_CACHE_HPP_