tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

spin_test.cc (4176B)


      1 // Copyright 2025 Google LLC
      2 // SPDX-License-Identifier: Apache-2.0
      3 //
      4 // Licensed under the Apache License, Version 2.0 (the "License");
      5 // you may not use this file except in compliance with the License.
      6 // You may obtain a copy of the License at
      7 //
      8 //      http://www.apache.org/licenses/LICENSE-2.0
      9 //
     10 // Unless required by applicable law or agreed to in writing, software
     11 // distributed under the License is distributed on an "AS IS" BASIS,
     12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13 // See the License for the specific language governing permissions and
     14 // limitations under the License.
     15 
     16 #include "hwy/contrib/thread_pool/spin.h"
     17 
     18 #include <stddef.h>
     19 #include <stdint.h>
     20 #include <stdio.h>
     21 
     22 #include <atomic>
     23 
     24 #include "hwy/aligned_allocator.h"          // HWY_ALIGNMENT
     25 #include "hwy/contrib/thread_pool/futex.h"  // NanoSleep
     26 #include "hwy/contrib/thread_pool/thread_pool.h"
     27 #include "hwy/contrib/thread_pool/topology.h"
     28 #include "hwy/tests/hwy_gtest.h"
     29 #include "hwy/tests/test_util-inl.h"
     30 #include "hwy/timer.h"
     31 
     32 namespace hwy {
     33 namespace {
     34 
     35 struct TestPingPongT {
     36  template <class Spin>
     37  void operator()(const Spin& spin) const {
     38    constexpr size_t kU32PerLine = HWY_ALIGNMENT / 4;
     39    constexpr size_t kF64PerLine = HWY_ALIGNMENT / 8;
     40    alignas(HWY_ALIGNMENT) std::atomic<uint32_t> thread_active[kU32PerLine];
     41    alignas(HWY_ALIGNMENT) std::atomic<uint32_t> thread_done[kU32PerLine];
     42 
     43    thread_active[0].store(0, std::memory_order_release);
     44    thread_done[0].store(0, std::memory_order_release);
     45    hwy::ThreadPool pool(1);
     46    HWY_ASSERT(pool.NumWorkers() == 2);
     47 
     48    const double t0 = hwy::platform::Now();
     49    std::atomic_flag error = ATOMIC_FLAG_INIT;
     50 
     51    alignas(HWY_ALIGNMENT) std::atomic<size_t> reps1;
     52    alignas(HWY_ALIGNMENT) std::atomic<size_t> reps2;
     53 
     54    alignas(HWY_ALIGNMENT) std::atomic<double> before_thread_done[kF64PerLine];
     55    alignas(HWY_ALIGNMENT) std::atomic<double> before_thread_go[kF64PerLine];
     56    alignas(HWY_ALIGNMENT) std::atomic<double> ack_thread_done[kF64PerLine];
     57    alignas(HWY_ALIGNMENT) std::atomic<double> ack_thread_release[kF64PerLine];
     58 
     59    const auto kAcq = std::memory_order_acquire;
     60    const auto kRel = std::memory_order_release;
     61    pool.Run(0, 2, [&](uint64_t task, size_t thread) {
     62      HWY_ASSERT(task == thread);
     63      if (task == 0) {  // new thread
     64        SpinResult result = spin.UntilDifferent(0, thread_active[0]);
     65        ack_thread_release[0].store(hwy::platform::Now(), kRel);
     66        reps1.store(result.reps);
     67        if (!NanoSleep(20 * 1000 * 1000)) {
     68          error.test_and_set();
     69        }
     70        before_thread_done[0].store(hwy::platform::Now(), kRel);
     71        thread_done[0].store(1, kRel);
     72      } else {  // main thread
     73        if (!NanoSleep(30 * 1000 * 1000)) {
     74          error.test_and_set();
     75        }
     76        // Release the thread.
     77        before_thread_go[0].store(hwy::platform::Now(), kRel);
     78        thread_active[0].store(1, kRel);
     79        // Wait for it to finish.
     80        const size_t reps = spin.UntilEqual(1, thread_done[0]);
     81        ack_thread_done[0].store(hwy::platform::Now(), kRel);
     82        reps2.store(reps);
     83      }
     84    });
     85 
     86    const double t1 = hwy::platform::Now();
     87    const double elapsed = t1 - t0;
     88    const double latency1 =
     89        ack_thread_release[0].load(kAcq) - before_thread_go[0].load(kAcq);
     90    const double latency2 =
     91        ack_thread_done[0].load(kAcq) - before_thread_done[0].load(kAcq);
     92    fprintf(stderr,
     93            "Elapsed time: %f us; reps1=%zu, reps2=%zu, latency=%f %f us\n",
     94            elapsed * 1E6, reps1.load(), reps2.load(), latency1 * 1E6,
     95            latency2 * 1E6);
     96    // Unless NanoSleep failed to sleep, this should take 50ms+epsilon.
     97    HWY_ASSERT(error.test_and_set() || elapsed > 25E-3);
     98  }
     99 };  // namespace hwy
    100 
    101 // Simple mutex.
    102 TEST(SpinTest, TestPingPong) {
    103  if (!HaveThreadingSupport()) {
    104    HWY_WARN("Threads not supported, skipping test\n");
    105    return;
    106  }
    107 
    108  const SpinType spin_type = DetectSpin();
    109  fprintf(stderr, "Spin method : %s\n", ToString(spin_type));
    110  CallWithSpin(spin_type, TestPingPongT());
    111 }
    112 
    113 }  // namespace
    114 }  // namespace hwy
    115 
    116 HWY_TEST_MAIN();