Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Initializations should be refcounted #16

Closed
Saancreed opened this issue Jun 6, 2021 · 1 comment · Fixed by #19
Closed

Initializations should be refcounted #16

Saancreed opened this issue Jun 6, 2021 · 1 comment · Fixed by #19

Comments

@Saancreed
Copy link
Collaborator

As mentioned in #15, NvAPI should be unloaded only after Unload has been called for each previous call to Initialize, otherwise call chains like Initialize, Initialize, Unload, $SomeFunction will lead to an error. Code sample below can be used to reproduce the issue (while it works fine on Windows), caused by the block that starts at line 74:

nvapi_test.cpp
#include <windows.h>
#include <libloaderapi.h>

#include <array>
#include <iostream>
#include <memory>
#include <stdexcept>
#include <string>

#include "nvapi.h"
#include "nvapi_interface.h"

using namespace std::literals;

static constexpr int nvapi_interface_table_size = sizeof(nvapi_interface_table) / sizeof(*nvapi_interface_table);

int main(void)
{
    std::unique_ptr<std::remove_pointer_t<HMODULE>, decltype(&FreeLibrary)> nvapi = {LoadLibraryA("nvapi64.dll"), FreeLibrary};

    if (!nvapi) throw std::runtime_error{"Failed to load NvAPI."s};

    std::cout << "NvAPI loaded." << std::endl;

    auto QueryInterface = reinterpret_cast<void *(*)(NvU32 id)>(GetProcAddress(nvapi.get(), "nvapi_QueryInterface"));

    if (!QueryInterface) throw std::runtime_error{"Failed to find nvapi_QueryInterface."s};

    auto QueryInterfaceByName = [&QueryInterface](const std::string& funcName) -> void*
    {
        void* result = nullptr;

        for (int i = 0; i < nvapi_interface_table_size; ++i)
        {
            auto& entry = nvapi_interface_table[i];

            if (entry.func == funcName)
            {
                *(void**)(&result) = QueryInterface(entry.id);

                break;
            }
        }

        if (!result) throw std::runtime_error{"Failed to find function: "s + funcName};

        return result;
    };

    #define QueryInterfaceTyped(x) reinterpret_cast<decltype(&x)>(QueryInterfaceByName(#x));

    auto Initialize = QueryInterfaceTyped(NvAPI_Initialize);
    auto Unload = QueryInterfaceTyped(NvAPI_Unload);
    auto GetErrorMessage = QueryInterfaceTyped(NvAPI_GetErrorMessage);
    auto EnumPhysicalGPUs = QueryInterfaceTyped(NvAPI_EnumPhysicalGPUs);
    auto GPU_GetFullName = QueryInterfaceTyped(NvAPI_GPU_GetFullName);
    auto GPU_GetThermalSettings = QueryInterfaceTyped(NvAPI_GPU_GetThermalSettings);
    auto GPU_GetDynamicPstatesInfoEx = QueryInterfaceTyped(NvAPI_GPU_GetDynamicPstatesInfoEx);

    NvAPI_ShortString errormsg = {0};

    #define NvAPI_Call(f, ...) \
        if (auto res = f(__VA_ARGS__); res != NVAPI_OK) \
        { \
            if (auto gem = GetErrorMessage(res, errormsg); gem != NVAPI_OK) \
                ("GetErrorMessage failed with status "s + std::to_string(gem)).copy(errormsg, NVAPI_SHORT_STRING_MAX - 1); \
            throw std::runtime_error{#f + " failed: "s + errormsg}; \
        }

    NvAPI_Call(Initialize);

    std::cout << "NvAPI initialized." << std::endl;

    {
        NvAPI_Call(Initialize);

        std::cout << "NvAPI initialized (2nd time)." << std::endl;

        NvAPI_Call(Unload);

        std::cout << "NvAPI unloaded (1 unload left)." << std::endl;
    }

    std::array<NvPhysicalGpuHandle, NVAPI_MAX_PHYSICAL_GPUS> nvGPUHandles;
    NvU32 gpuCount;

    NvAPI_Call(EnumPhysicalGPUs, nvGPUHandles.data(), &gpuCount);

    std::cout << "Found " << gpuCount << " physical GPUs." << std::endl;

    for (NvU32 i = 0; i < gpuCount; ++i)
    {
        NvAPI_ShortString gpuFullName;

        NvAPI_Call(GPU_GetFullName, nvGPUHandles[i], gpuFullName);

        std::cout << "GPU " << i << ": " << gpuFullName << std::endl;

        NV_GPU_THERMAL_SETTINGS_V1 thermals = {};
        thermals.version = NV_GPU_THERMAL_SETTINGS_VER_1;

        NvAPI_Call(GPU_GetThermalSettings, nvGPUHandles[i], NVAPI_THERMAL_TARGET_ALL, reinterpret_cast<NV_GPU_THERMAL_SETTINGS*>(&thermals));

        std::cout << "Sensors: " << thermals.count << std::endl;

        for (NvU32 s = 0; s < thermals.count; ++s)
        {
            std::cout << "  Controller: " << thermals.sensor[s].controller << std::endl;
            std::cout << " CurrentTemp: " << thermals.sensor[s].currentTemp << std::endl;
            std::cout << "  DefMaxTemp: " << thermals.sensor[s].defaultMaxTemp << std::endl;
            std::cout << "  DefMinTemp: " << thermals.sensor[s].defaultMinTemp << std::endl;
            std::cout << "      Target: " << thermals.sensor[s].target << std::endl;
        }

        NV_GPU_DYNAMIC_PSTATES_INFO_EX pstates = {};
        pstates.version = NV_GPU_DYNAMIC_PSTATES_INFO_EX_VER;

        NvAPI_Call(GPU_GetDynamicPstatesInfoEx, nvGPUHandles[i], &pstates);

        std::cout << "Pstates flags: " << pstates.flags << std::endl;

        for (NvU32 d = 0; d < NVAPI_MAX_GPU_UTILIZATIONS; ++d)
        {
            if (pstates.utilization[d].bIsPresent)
                std::cout << "Domain " << d << " is present, utilization: " << pstates.utilization[d].percentage << "%" << std::endl;
            else
                std::cout << "Domain " << d << " not present" << std::endl;
        }
    }

    NvAPI_Call(Unload);

    std::cout << "NvAPI unloaded." << std::endl;
}
@jp7677
Copy link
Owner

jp7677 commented Jun 12, 2021

Hi @Saancreed as discussed on discord, thanks a lot for identifying this issue and for providing an easy way to reproduce this. Yeah, this is a valid issue, we should pick this up, together with guarding nvapi-initialize against concurrency issues when building the registry.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants