Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions src/backend/vulkan/runtime/vulkan_device_api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -34,13 +34,21 @@ namespace runtime {
namespace vulkan {

VulkanDeviceAPI* VulkanDeviceAPI::Global() {
#ifdef _WIN32
// DLL static destructors run during loader shutdown, when calling the Vulkan
// driver (e.g. vkDestroyDevice) is no longer safe. Keep the singleton alive
// for the process lifetime, as CUDADeviceAPI does, and let the OS reclaim it.
static auto* inst = new VulkanDeviceAPI();
return inst;
#else
// Most of the TVM Global() functions allocate with "new" and do
// not deallocate, as the OS can clean up any leftover buffers at
// the end. In this case, we need the VulkanDeviceAPI destructor
// to call vkDestroyInstance, to prevent a segfault on exit when
// using some nvidia drivers.
static VulkanDeviceAPI inst;
return &inst;
#endif
}

VulkanDeviceAPI::VulkanDeviceAPI() {
Expand Down
40 changes: 40 additions & 0 deletions tests/python/runtime/test_runtime_device_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import subprocess
import sys

import pytest

import tvm
import tvm.testing

Expand Down Expand Up @@ -48,5 +50,43 @@ def test_check_if_device_exists():
)


@pytest.mark.gpu
@pytest.mark.skipif(
tvm.get_global_func("device_api.vulkan", allow_missing=True) is None,
reason="Vulkan runtime is not built",
)
@pytest.mark.parametrize("allocate_tensor", [False, True])
def test_vulkan_process_exit(allocate_tensor):
"""Device initialization and allocation must allow a clean process exit."""
# Initialize Vulkan only in the child: exit-time failures cannot be caught
# by an in-process assertion and must not crash the pytest process itself.
script = """
import sys
import numpy as np
import tvm

dev = tvm.vulkan(0)
if not dev.exist:
sys.exit(77)
if int(sys.argv[1]):
expected = np.arange(128, dtype="float32")
tensor = tvm.runtime.tensor(expected, dev)
np.testing.assert_array_equal(tensor.numpy(), expected)
print("Vulkan work completed", flush=True)
"""
proc = subprocess.run(
[sys.executable, "-c", script, str(int(allocate_tensor))],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
timeout=120,
check=False,
)
if proc.returncode == 77:
pytest.skip("No Vulkan device is available")
assert proc.returncode == 0, f"Vulkan subprocess exited with {proc.returncode}:\n{proc.stdout}"
assert "Vulkan work completed" in proc.stdout


if __name__ == "__main__":
tvm.testing.main()