blob: 4c5f9ee9d81ed3946276770b321f25d0d82fa07a [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <lib/syslog/cpp/macros.h>
#include <unistd.h>
#include <thread>
#include <perftest/perftest.h>
#include "test_runner.h"
#if defined(__Fuchsia__)
#include <zircon/syscalls.h>
#include "assert.h"
#elif defined(__linux__)
#include <sys/syscall.h>
#include <linux/futex.h>
#else
#error "Unsupported operating system"
#endif
namespace {
#if defined(__Fuchsia__)
void FutexWake(volatile int* ptr) { ASSERT_OK(zx_futex_wake(const_cast<int*>(ptr), 1)); }
void FutexWait(volatile int* ptr, int expected_value) {
zx_status_t status =
zx_futex_wait(const_cast<int*>(ptr), expected_value, ZX_HANDLE_INVALID, ZX_TIME_INFINITE);
// zx_futex_wait() returns ZX_ERR_BAD_STATE if *ptr != expected_value, or
// ZX_OK if woken by a zx_futex_wake() call.
FX_CHECK(status == ZX_OK || status == ZX_ERR_BAD_STATE);
}
#elif defined(__linux__)
void FutexWake(volatile int* ptr) {
long woken_count =
syscall(__NR_futex, ptr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, nullptr, nullptr, 0);
FX_CHECK(woken_count >= 0);
}
void FutexWait(volatile int* ptr, int expected_value) {
long result = syscall(__NR_futex, ptr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, expected_value, nullptr,
nullptr, 0);
// FUTEX_WAIT returns the error EAGAIN if *ptr != expected_value, or 0
// (success) if woken by a FUTEX_WAKE call.
FX_CHECK(result == 0 || (result < 0 && errno == EAGAIN));
}
#else
#error "Unsupported operating system"
#endif
// Test the round trip time for waking up threads using futexes.
//
// Note that Zircon does not support cross-process futexes, only
// within-process futexes, so there is no multi-process version of this
// test case.
class FutexTest {
public:
FutexTest() {
thread_ = std::thread([this]() { ThreadFunc(); });
}
~FutexTest() {
Wake(&futex1_, 2); // Tell the thread to shut down.
thread_.join();
}
void Run() {
Wake(&futex1_, 1);
FX_CHECK(!Wait(&futex2_));
}
private:
void ThreadFunc() {
for (;;) {
if (Wait(&futex1_))
break;
Wake(&futex2_, 1);
}
}
void Wake(volatile int* ptr, int wake_value) {
*ptr = wake_value;
FutexWake(ptr);
}
bool Wait(volatile int* ptr) {
for (;;) {
int val = *ptr;
if (val != 0) {
// We were signaled. Reset the state to unsignaled.
*ptr = 0;
// Return whether we got a request to shut down.
return val == 2;
}
FutexWait(ptr, val);
}
}
std::thread thread_;
volatile int futex1_ = 0; // Signals from client to server.
volatile int futex2_ = 0; // Signals from server to client.
};
void RegisterTests() { fbenchmark::RegisterTest<FutexTest>("RoundTrip_Futex_SingleProcess"); }
PERFTEST_CTOR(RegisterTests)
} // namespace