loader_platform_thread_mutex loader_lock;
loader_platform_thread_mutex loader_json_lock;
loader_platform_thread_mutex loader_preload_icd_lock;
+loader_platform_thread_mutex loader_global_instance_list_lock;
// A list of ICDs that gets initialized when the loader does its global initialization. This list should never be used by anything
// other than EnumerateInstanceExtensionProperties(), vkDestroyInstance, and loader_release(). This list does not change
}
struct loader_icd_term *loader_get_icd_and_device(const void *device, struct loader_device **found_dev, uint32_t *icd_index) {
+ loader_platform_thread_lock_mutex(&loader_global_instance_list_lock);
*found_dev = NULL;
for (struct loader_instance *inst = loader.instances; inst; inst = inst->next) {
uint32_t index = 0;
if (NULL != icd_index) {
*icd_index = index;
}
+ loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
return icd_term;
}
index++;
}
}
+ loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
return NULL;
}
loader_platform_thread_create_mutex(&loader_lock);
loader_platform_thread_create_mutex(&loader_json_lock);
loader_platform_thread_create_mutex(&loader_preload_icd_lock);
+ loader_platform_thread_create_mutex(&loader_global_instance_list_lock);
+
// initialize logging
loader_debug_init();
#if defined(_WIN32)
loader_platform_thread_delete_mutex(&loader_lock);
loader_platform_thread_delete_mutex(&loader_json_lock);
loader_platform_thread_delete_mutex(&loader_preload_icd_lock);
+ loader_platform_thread_delete_mutex(&loader_global_instance_list_lock);
}
// Preload the ICD libraries that are likely to be needed so we don't repeatedly load/unload them later
return NULL;
} else {
disp = loader_get_instance_layer_dispatch(instance);
+ loader_platform_thread_lock_mutex(&loader_global_instance_list_lock);
for (struct loader_instance *inst = loader.instances; inst; inst = inst->next) {
if (&inst->disp->layer_inst_disp == disp) {
ptr_instance = inst;
break;
}
}
+ loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
}
return ptr_instance;
}
// Remove this instance from the list of instances:
struct loader_instance *prev = NULL;
+ loader_platform_thread_lock_mutex(&loader_global_instance_list_lock);
struct loader_instance *next = loader.instances;
while (next != NULL) {
if (next == ptr_instance) {
prev = next;
next = next->next;
}
+ loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
while (NULL != icd_terms) {
if (icd_terms->instance) {
extern loader_platform_thread_mutex loader_lock;
extern loader_platform_thread_mutex loader_json_lock;
extern loader_platform_thread_mutex loader_preload_icd_lock;
+extern loader_platform_thread_mutex loader_global_instance_list_lock;
bool compare_vk_extension_properties(const VkExtensionProperties *op1, const VkExtensionProperties *op2);
}
memcpy(&ptr_instance->disp->layer_inst_disp, &instance_disp, sizeof(instance_disp));
+ loader_platform_thread_lock_mutex(&loader_global_instance_list_lock);
ptr_instance->next = loader.instances;
loader.instances = ptr_instance;
+ loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
// Activate any layers on instance chain
res = loader_enable_instance_layers(ptr_instance, &ici, &ptr_instance->instance_layer_list);
if (NULL != ptr_instance) {
if (res != VK_SUCCESS) {
+ loader_platform_thread_lock_mutex(&loader_global_instance_list_lock);
// error path, should clean everything up
if (loader.instances == ptr_instance) {
loader.instances = ptr_instance->next;
}
+ loader_platform_thread_unlock_mutex(&loader_global_instance_list_lock);
+
loader_instance_heap_free(ptr_instance, ptr_instance->disp);
// Remove any created VK_EXT_debug_report or VK_EXT_debug_utils items
destroy_debug_callbacks_chain(ptr_instance, pAllocator);
ptr_instance->InstanceCreationDeletionDebugFunctionHead = ptr_instance->DbgFunctionHead;
ptr_instance->DbgFunctionHead = NULL;
}
+ // Only unlock when ptr_instance isn't NULL, as if it is, the above code didn't make it to when loader_lock was locked.
loader_platform_thread_unlock_mutex(&loader_lock);
}
set_target_properties(test_regression ${LOADER_STANDARD_CXX_PROPERTIES})
target_compile_definitions(test_regression PUBLIC VK_NO_PROTOTYPES)
+# Threading tests live in separate executabe just for threading tests as it'll need support
+# in the test harness to enable in CI, as thread sanitizer doesn't work with address sanitizer enabled.
add_executable(
test_threading
loader_testing_main.cpp
target_link_options(testing_framework_util PUBLIC -fsanitize=thread)
target_compile_options(vulkan PUBLIC -fsanitize=thread)
target_link_options(vulkan PUBLIC -fsanitize=thread)
+ target_compile_options(gtest PUBLIC -fsanitize=thread)
+ target_link_options(gtest PUBLIC -fsanitize=thread)
endif()
endif()
#if TEST_ICD_EXPORT_ICD_GPDPA
FRAMEWORK_EXPORT VKAPI_ATTR PFN_vkVoidFunction VKAPI_CALL vk_icdGetPhysicalDeviceProcAddr(VkInstance instance, const char* pName) {
- // std::cout << "icdGetPhysicalDeviceProcAddr: " << pName << "\n";
return get_physical_device_func(instance, pName);
}
#endif // TEST_ICD_EXPORT_ICD_GPDPA
create_info.instance_info.pNext = wrapper.get();
}
-PlatformShimWrapper::PlatformShimWrapper(std::vector<fs::FolderManager>* folders) noexcept {
+PlatformShimWrapper::PlatformShimWrapper(std::vector<fs::FolderManager>* folders, bool enable_log) noexcept {
#if defined(WIN32) || defined(__APPLE__)
shim_library = LibraryWrapper(SHIM_LIBRARY_NAME);
PFN_get_platform_shim get_platform_shim_func = shim_library.get_symbol(GET_PLATFORM_SHIM_STR);
#endif
platform_shim->reset();
- // leave it permanently on at full blast
- set_env_var("VK_LOADER_DEBUG", "all");
+ if (enable_log) {
+ set_env_var("VK_LOADER_DEBUG", "all");
+ }
}
PlatformShimWrapper::~PlatformShimWrapper() noexcept { platform_shim->reset(); }
fs::path TestLayerHandle::get_layer_full_path() noexcept { return layer_library.lib_path; }
fs::path TestLayerHandle::get_layer_manifest_path() noexcept { return manifest_path; }
-FrameworkEnvironment::FrameworkEnvironment() noexcept : platform_shim(&folders), vulkan_functions() {
+FrameworkEnvironment::FrameworkEnvironment() noexcept : FrameworkEnvironment(true) {}
+FrameworkEnvironment::FrameworkEnvironment(bool enable_log) noexcept : platform_shim(&folders, enable_log), vulkan_functions() {
// This order is important, it matches the enum ManifestLocation, used to index the folders vector
folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("null_dir"));
folders.emplace_back(FRAMEWORK_BUILD_DIRECTORY, std::string("icd_manifests"));
return surface;
}
+
+extern "C" {
+void __ubsan_on_report() { FAIL() << "Encountered an undefined behavior sanitizer error"; }
+void __asan_on_error() { FAIL() << "Encountered an address sanitizer error"; }
+void __tsan_on_report() { FAIL() << "Encountered a thread sanitizer error"; }
+} // extern "C"
struct FrameworkEnvironment; // forward declaration
struct PlatformShimWrapper {
- PlatformShimWrapper(std::vector<fs::FolderManager>* folders) noexcept;
+ PlatformShimWrapper(std::vector<fs::FolderManager>* folders, bool enable_log) noexcept;
~PlatformShimWrapper() noexcept;
PlatformShimWrapper(PlatformShimWrapper const&) = delete;
PlatformShimWrapper& operator=(PlatformShimWrapper const&) = delete;
};
struct FrameworkEnvironment {
- FrameworkEnvironment() noexcept;
+ FrameworkEnvironment() noexcept; // default is to enable VK_LOADER_DEBUG=all
+ FrameworkEnvironment(bool enable_log) noexcept;
void add_icd(TestICDDetails icd_details) noexcept;
void add_implicit_layer(ManifestLayer layer_manifest, const std::string& json_name) noexcept;
ASSERT_TRUE(tracker.empty());
fail_index++;
}
+ remove_env_var("VK_DRIVER_FILES");
}
// Test failure during vkCreateDevice to make sure we don't leak memory if
driver.physical_devices.emplace_back("physical_device_1");
driver.physical_devices[1].add_queue_family_properties({{VK_QUEUE_GRAPHICS_BIT, 1, 0, {1, 1, 1}}, false});
- const char* layer_name = "VkLayerImplicit0";
+ const char* layer_name = "VK_LAYER_VkLayerImplicit0";
env.add_implicit_layer(ManifestLayer{}.add_layer(ManifestLayer::LayerDescription{}
.set_name(layer_name)
.set_lib_path(TEST_LAYER_PATH_EXPORT_VERSION_2)
#include <mutex>
#include <thread>
+#include <atomic>
-void create_destroy_device_loop(uint32_t num_loops_create_destroy_device, uint32_t num_loops_try_get_proc_addr, InstWrapper* inst,
- VkPhysicalDevice phys_dev) {
+void create_destroy_instance_loop_with_function_queries(FrameworkEnvironment* env, uint32_t num_loops_create_destroy_instance,
+ uint32_t num_loops_try_get_instance_proc_addr,
+ uint32_t num_loops_try_get_device_proc_addr) {
+ for (uint32_t i = 0; i < num_loops_create_destroy_instance; i++) {
+ InstWrapper inst{env->vulkan_functions};
+ inst.CheckCreate();
+ PFN_vkEnumeratePhysicalDevices enum_pd = nullptr;
+ for (uint32_t j = 0; j < num_loops_try_get_instance_proc_addr; j++) {
+ enum_pd = inst.load("vkEnumeratePhysicalDevices");
+ ASSERT_NE(enum_pd, nullptr);
+ }
+ VkPhysicalDevice phys_dev = inst.GetPhysDev();
+
+ DeviceWrapper dev{inst};
+ dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(1.0));
+ dev.CheckCreate(phys_dev);
+ for (uint32_t j = 0; j < num_loops_try_get_device_proc_addr; j++) {
+ PFN_vkCmdBindPipeline p = dev.load("vkCmdBindPipeline");
+ p(VK_NULL_HANDLE, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, VK_NULL_HANDLE);
+ }
+ }
+}
+
+void create_destroy_device_loop(FrameworkEnvironment* env, uint32_t num_loops_create_destroy_device,
+ uint32_t num_loops_try_get_proc_addr) {
+ InstWrapper inst{env->vulkan_functions};
+ inst.CheckCreate();
+ VkPhysicalDevice phys_dev = inst.GetPhysDev();
for (uint32_t i = 0; i < num_loops_create_destroy_device; i++) {
- DeviceWrapper dev{*inst};
+ DeviceWrapper dev{inst};
dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(1.0));
dev.CheckCreate(phys_dev);
for (uint32_t j = 0; j < num_loops_try_get_proc_addr; j++) {
- PFN_vkCmdBindPipeline p =
- reinterpret_cast<PFN_vkCmdBindPipeline>(dev->vkGetDeviceProcAddr(dev.dev, "vkCmdBindPipeline"));
- ASSERT_NE(p, nullptr);
- PFN_vkCmdBindDescriptorSets d =
- reinterpret_cast<PFN_vkCmdBindDescriptorSets>(dev->vkGetDeviceProcAddr(dev.dev, "vkCmdBindDescriptorSets"));
- ASSERT_NE(d, nullptr);
- PFN_vkCmdBindVertexBuffers vb =
- reinterpret_cast<PFN_vkCmdBindVertexBuffers>(dev->vkGetDeviceProcAddr(dev.dev, "vkCmdBindVertexBuffers"));
- ASSERT_NE(vb, nullptr);
- PFN_vkCmdBindIndexBuffer ib =
- reinterpret_cast<PFN_vkCmdBindIndexBuffer>(dev->vkGetDeviceProcAddr(dev.dev, "vkCmdBindIndexBuffer"));
- ASSERT_NE(ib, nullptr);
- PFN_vkCmdDraw c = reinterpret_cast<PFN_vkCmdDraw>(dev->vkGetDeviceProcAddr(dev.dev, "vkCmdDraw"));
- ASSERT_NE(c, nullptr);
+ PFN_vkCmdBindPipeline p = dev.load("vkCmdBindPipeline");
+ PFN_vkCmdBindDescriptorSets d = dev.load("vkCmdBindDescriptorSets");
+ PFN_vkCmdBindVertexBuffers vb = dev.load("vkCmdBindVertexBuffers");
+ PFN_vkCmdBindIndexBuffer ib = dev.load("vkCmdBindIndexBuffer");
+ PFN_vkCmdDraw c = dev.load("vkCmdDraw");
+ p(VK_NULL_HANDLE, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, VK_NULL_HANDLE);
+ d(VK_NULL_HANDLE, VkPipelineBindPoint::VK_PIPELINE_BIND_POINT_GRAPHICS, VK_NULL_HANDLE, 0, 0, nullptr, 0, nullptr);
+ vb(VK_NULL_HANDLE, 0, 0, nullptr, nullptr);
+ ib(VK_NULL_HANDLE, 0, 0, VkIndexType::VK_INDEX_TYPE_UINT16);
+ c(VK_NULL_HANDLE, 0, 0, 0, 0);
}
}
}
const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) {}
VKAPI_ATTR void VKAPI_CALL test_vkCmdDraw(VkCommandBuffer cmd_buf, uint32_t vertexCount, uint32_t instanceCount,
uint32_t firstVertex, uint32_t firstInstance) {}
-TEST(ThreadingTests, ConcurentGetDeviceProcAddr) {
- FrameworkEnvironment env{};
+TEST(Threading, InstanceCreateDestroyLoop) {
+ const auto processor_count = std::thread::hardware_concurrency();
+
+ FrameworkEnvironment env{false};
+ env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
+ uint32_t num_loops_create_destroy_instance = 500;
+ uint32_t num_loops_try_get_instance_proc_addr = 5;
+ uint32_t num_loops_try_get_device_proc_addr = 100;
+ auto& driver = env.get_test_icd();
+
+ driver.physical_devices.emplace_back("physical_device_0");
+ driver.physical_devices.back().known_device_functions.push_back(
+ {"vkCmdBindPipeline", to_vkVoidFunction(test_vkCmdBindPipeline)});
+
+ std::vector<std::thread> instance_creation_threads;
+ std::vector<std::thread> function_query_threads;
+ for (uint32_t i = 0; i < processor_count; i++) {
+ instance_creation_threads.emplace_back(create_destroy_instance_loop_with_function_queries, &env,
+ num_loops_create_destroy_instance, num_loops_try_get_instance_proc_addr,
+ num_loops_try_get_device_proc_addr);
+ }
+ for (uint32_t i = 0; i < processor_count; i++) {
+ instance_creation_threads[i].join();
+ }
+}
+
+TEST(Threading, DeviceCreateDestroyLoop) {
+ const auto processor_count = std::thread::hardware_concurrency();
+
+ FrameworkEnvironment env{false};
env.add_icd(TestICDDetails(TEST_ICD_PATH_VERSION_2_EXPORT_ICD_GPDPA));
- uint32_t num_threads = 100;
- uint32_t num_loops_create_destroy_device = 10;
- uint32_t num_loops_try_get_proc_addr = 100;
+
+ uint32_t num_loops_create_destroy_device = 1000;
+ uint32_t num_loops_try_get_device_proc_addr = 5;
auto& driver = env.get_test_icd();
driver.physical_devices.emplace_back("physical_device_0");
{"vkCmdBindIndexBuffer", to_vkVoidFunction(test_vkCmdBindIndexBuffer)});
driver.physical_devices.back().known_device_functions.push_back({"vkCmdDraw", to_vkVoidFunction(test_vkCmdDraw)});
- InstWrapper inst{env.vulkan_functions};
- inst.CheckCreate();
-
- VkPhysicalDevice phys_dev = inst.GetPhysDev();
-
- DeviceWrapper dev{inst};
- dev.create_info.add_device_queue(DeviceQueueCreateInfo{}.add_priority(1.0));
- dev.CheckCreate(phys_dev);
+ std::vector<std::thread> device_creation_threads;
- std::vector<std::thread> threads;
- for (uint32_t i = 0; i < num_threads; i++) {
- threads.emplace_back(create_destroy_device_loop, num_loops_create_destroy_device, num_loops_try_get_proc_addr, &inst,
- phys_dev);
+ for (uint32_t i = 0; i < processor_count; i++) {
+ device_creation_threads.emplace_back(create_destroy_device_loop, &env, num_loops_create_destroy_device,
+ num_loops_try_get_device_proc_addr);
}
- for (uint32_t i = 0; i < num_threads; i++) {
- threads[i].join();
+ for (uint32_t i = 0; i < processor_count; i++) {
+ device_creation_threads[i].join();
}
}