// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. // // Copyright (C) 2020 Intel Corporation #include "../test_precomp.hpp" #include <unordered_set> #include <thread> #include "executor/last_value.hpp" namespace opencv_test { using namespace cv::gapi; TEST(LastValue, PushPop) { own::last_written_value<int> v; for (int i = 0; i < 100; i++) { v.push(i); int x = 1; v.pop(x); EXPECT_EQ(i, x); } } TEST(LastValue, TryPop) { own::last_written_value<int> v; int x = 0; EXPECT_FALSE(v.try_pop(x)); v.push(1); EXPECT_TRUE(v.try_pop(x)); EXPECT_EQ(1, x); } TEST(LastValue, Clear) { own::last_written_value<int> v; v.push(42); v.clear(); int x = 0; EXPECT_FALSE(v.try_pop(x)); } TEST(LastValue, Overwrite) { own::last_written_value<int> v; v.push(42); v.push(0); int x = -1; EXPECT_TRUE(v.try_pop(x)); EXPECT_EQ(0, x); } // In this test, every writer thread produces its own range of integer // numbers, writing those to a shared queue. // // Every reader thread pops elements from the queue (until -1 is // reached) and stores those in its own associated set. // // Finally, the master thread waits for completion of all other // threads and verifies that all the necessary data is // produced/obtained. namespace { using StressParam = std::tuple<int // Num writer threads ,int // Num elements per writer ,int>; // Num reader threads constexpr int STOP_SIGN = -1; constexpr int BASE = 1000; } struct LastValue_: public ::testing::TestWithParam<StressParam> { using V = own::last_written_value<int>; using S = std::unordered_set<int>; static void writer(int base, int writes, V& v) { for (int i = 0; i < writes; i++) { if (i % 2) { std::this_thread::sleep_for(std::chrono::milliseconds{1}); } v.push(base + i); } v.push(STOP_SIGN); } static void reader(V& v, S& s) { int x = 0; while (true) { v.pop(x); if (x == STOP_SIGN) { // If this thread was lucky enough to read this STOP_SIGN, // push it back to v to make other possible readers able // to read it again (note due to the last_written_value // semantic, those STOP_SIGN could be simply lost i.e. // overwritten. v.push(STOP_SIGN); return; } s.insert(x); } } }; TEST_P(LastValue_, Test) { int num_writers = 0; int num_writes = 0; int num_readers = 0; std::tie(num_writers, num_writes, num_readers) = GetParam(); CV_Assert(num_writers < 20); CV_Assert(num_writes < BASE); V v; // Start reader threads std::vector<S> storage(num_readers); std::vector<std::thread> readers; for (S& s : storage) { readers.emplace_back(reader, std::ref(v), std::ref(s)); } // Start writer threads, also pre-generate reference numbers S reference; std::vector<std::thread> writers; for (int w = 0; w < num_writers; w++) { writers.emplace_back(writer, w*BASE, num_writes, std::ref(v)); for (int r = 0; r < num_writes; r++) { reference.insert(w*BASE + r); } } // Wait for completions for (auto &t : readers) t.join(); for (auto &t : writers) t.join(); // Validate the result. Some values are read, and the values are // correct (i.e. such values have been written) std::size_t num_values_read = 0u; for (const auto &s : storage) { num_values_read += s.size(); for (auto &x : s) { EXPECT_TRUE(reference.count(x) > 0); } } // NOTE: Some combinations may end-up in 0 values read // it is normal, the main thing is that the test shouldn't hang! EXPECT_LE(0u, num_values_read); } INSTANTIATE_TEST_CASE_P(LastValueStress, LastValue_, Combine( Values(1, 2, 4, 8, 16) // writers , Values(32, 96, 256) // writes , Values(1, 2, 10))); // readers } // namespace opencv_test