blob: b4d29f148149093bf7df2271b7802fcdf6fbea19 [file] [log] [blame]
/*
* Copyright © 2020 Google, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice (including the next
* paragraph) shall be included in all copies or substantial portions of the
* Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#include <assert.h>
#include <filesystem>
#include <time.h>
#include <lib/fdio/directory.h>
#include <lib/zx/channel.h>
#include "util/magma/magma_wait.h"
#include "gtest/gtest.h"
inline constexpr int64_t ms_to_ns(int64_t ms) { return ms * 1000000ull; }
static uint64_t gettime_ns(void)
{
struct timespec current;
clock_gettime(CLOCK_MONOTONIC, &current);
return (uint64_t)current.tv_sec * ms_to_ns(1000) + current.tv_nsec;
}
class TestMagmaWait : public ::testing::Test {
public:
void SetUp() override
{
for (auto& entry : std::filesystem::directory_iterator("/dev/class/gpu")) {
zx::channel local, remote;
ASSERT_EQ(ZX_OK, zx::channel::create(0 /*flags*/, &local, &remote));
ASSERT_EQ(ZX_OK, fdio_service_connect(entry.path().c_str(), remote.release()));
ASSERT_EQ(MAGMA_STATUS_OK, magma_device_import(local.release(), &device_));
ASSERT_EQ(MAGMA_STATUS_OK, magma_device_create_connection(device_, &connection_));
}
ASSERT_NE(0, device_);
ASSERT_EQ(ZX_OK, zx::channel::create(0 /*flags*/, &channel_[0], &channel_[1]));
}
void TearDown() override
{
magma_connection_release(connection_);
magma_device_release(device_);
}
void WriteNotification()
{
uint64_t dummy = 0;
EXPECT_EQ(ZX_OK, channel_[1].write(0 /*flags*/, &dummy, sizeof(dummy), nullptr /*handles*/,
0 /*num_handles*/));
}
static void NotificationCallback(void* context)
{
uint64_t dummy;
EXPECT_EQ(ZX_OK,
reinterpret_cast<TestMagmaWait*>(context)->channel_[0].read(
0 /*flags*/, &dummy, nullptr /*handles*/, sizeof(dummy), 0 /*num_handles*/,
nullptr /*actual_bytes*/, nullptr /*actual_handles*/));
reinterpret_cast<TestMagmaWait*>(context)->callback_count_ += 1;
}
void Test(bool wait_all)
{
constexpr uint32_t kSemaphoreCount = 5;
constexpr uint64_t kTimeoutNs = ms_to_ns(100);
std::vector<magma_semaphore_t> semaphores(kSemaphoreCount);
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
magma_semaphore_id_t id;
ASSERT_EQ(MAGMA_STATUS_OK, magma_connection_create_semaphore(connection_, &semaphores[i], &id));
}
auto start = gettime_ns();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT,
magma_wait(channel_[0].get(), semaphores.data(), semaphores.size(),
start + kTimeoutNs, wait_all, NotificationCallback, this));
EXPECT_LE(kTimeoutNs - ms_to_ns(1), gettime_ns() - start); // TODO(fxb/49103) remove rounding
EXPECT_EQ(0u, callback_count_);
WriteNotification();
start = gettime_ns();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT,
magma_wait(channel_[0].get(), semaphores.data(), semaphores.size(),
start + kTimeoutNs, wait_all, NotificationCallback, this));
EXPECT_LE(kTimeoutNs - ms_to_ns(1), gettime_ns() - start); // TODO(fxb/49103) remove rounding
EXPECT_EQ(1u, callback_count_);
WriteNotification();
magma_semaphore_signal(semaphores[0]);
start = gettime_ns();
EXPECT_EQ(wait_all ? MAGMA_STATUS_TIMED_OUT : MAGMA_STATUS_OK,
magma_wait(channel_[0].get(), semaphores.data(), semaphores.size(),
start + kTimeoutNs, wait_all, NotificationCallback, this));
EXPECT_EQ(2u, callback_count_);
magma_semaphore_reset(semaphores[0]);
start = gettime_ns();
EXPECT_EQ(MAGMA_STATUS_TIMED_OUT,
magma_wait(channel_[0].get(), semaphores.data(), semaphores.size(),
start + kTimeoutNs, wait_all, NotificationCallback, this));
EXPECT_LE(kTimeoutNs - ms_to_ns(1), gettime_ns() - start); // TODO(fxb/49103) remove rounding
EXPECT_EQ(2u, callback_count_);
WriteNotification();
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
magma_semaphore_signal(semaphores[i]);
}
EXPECT_EQ(MAGMA_STATUS_OK, magma_wait(channel_[0].get(), semaphores.data(), semaphores.size(),
0, wait_all, NotificationCallback, this));
EXPECT_EQ(3u, callback_count_);
for (uint32_t i = 0; i < kSemaphoreCount; i++) {
magma_connection_release_semaphore(connection_, semaphores[i]);
}
}
protected:
magma_device_t device_ = 0;
magma_connection_t connection_ = 0;
uint64_t callback_count_ = 0;
zx::channel channel_[2];
};
TEST_F(TestMagmaWait, WaitOne) { Test(false); }
TEST_F(TestMagmaWait, WaitAll) { Test(true); }