Browse Source

update: adjust coding style for AllCases

master
Dnomd343 9 months ago
parent
commit
f09c8b2c2f
  1. 53
      src/core/all_cases/all_cases.cc
  2. 76
      src/core/all_cases/all_cases.h
  3. 29
      src/core/all_cases/basic_ranges.cc
  4. 22
      src/core/ffi/all_cases.cc
  5. 4
      src/core/short_code/convert.cc
  6. 11
      src/core_test/CMakeLists.txt
  7. 97
      src/core_test/cases/all_cases.cc
  8. 86
      src/core_test/ffi/all_cases.cc

53
src/core/all_cases/all_cases.cc

@ -5,7 +5,7 @@ namespace klotski {
namespace cases { namespace cases {
/// Calculate all possible klotski heads. /// Calculate all possible klotski heads.
static consteval std::array<int, 12> case_heads() { consteval static std::array<int, 12> case_heads() {
std::array<int, 12> heads = {}; std::array<int, 12> heads = {};
for (int i = 0, head = 0; head < 15; ++head) { for (int i = 0, head = 0; head < 15; ++head) {
if (head % 4 != 3) { if (head % 4 != 3) {
@ -17,12 +17,12 @@ static consteval std::array<int, 12> case_heads() {
/// Check whether the combination of head and range is valid. /// Check whether the combination of head and range is valid.
static int check_range(int head, uint32_t range) noexcept { static int check_range(int head, uint32_t range) noexcept {
constexpr uint32_t MASK_1x1 = 0b00000001; constexpr uint32_t M_1x1 = 0b00000001;
constexpr uint32_t MASK_1x2 = 0b00000011; constexpr uint32_t M_1x2 = 0b00000011;
constexpr uint32_t MASK_2x1 = 0b00010001; constexpr uint32_t M_2x1 = 0b00010001;
constexpr uint32_t MASK_2x2 = 0b00110011; constexpr uint32_t M_2x2 = 0b00110011;
uint32_t flags = MASK_2x2 << head; // fill 2x2 block uint32_t flags = M_2x2 << head; // fill 2x2 block
for (int addr = 0, offset = 1; range; range >>= 2, ++offset) { // traverse every 2-bit for (int addr = 0, offset = 1; range; range >>= 2, ++offset) { // traverse every 2-bit
auto num = low_zero_num(~flags); auto num = low_zero_num(~flags);
addr += num; // next unfilled block addr += num; // next unfilled block
@ -30,19 +30,19 @@ static int check_range(int head, uint32_t range) noexcept {
switch (range & 0b11) { switch (range & 0b11) {
case 0b00: // space case 0b00: // space
case 0b11: // 1x1 block case 0b11: // 1x1 block
flags |= MASK_1x1; flags |= M_1x1;
continue; continue;
case 0b10: // 2x1 block case 0b10: // 2x1 block
if ((flags >> 4) & 0b1 || addr > 15) { // invalid case if ((flags >> 4) & 0b1 || addr > 15) { // invalid case
return offset; // broken offset return offset; // broken offset
} }
flags |= MASK_2x1; flags |= M_2x1;
continue; continue;
case 0b01: // 1x2 block case 0b01: // 1x2 block
if ((flags >> 1) & 0b1 || (addr & 0b11) == 0b11) { // invalid case if ((flags >> 1) & 0b1 || (addr & 0b11) == 0b11) { // invalid case
return offset; // broken offset return offset; // broken offset
} }
flags |= MASK_1x2; flags |= M_1x2;
continue; continue;
} }
} }
@ -50,10 +50,10 @@ static int check_range(int head, uint32_t range) noexcept {
} }
/// Build all valid ranges of the specified head. /// Build all valid ranges of the specified head.
void AllCases::BuildCases(int head, Ranges &release) noexcept { void AllCases::build_cases(int head, Ranges &release) noexcept {
release.clear(); release.clear();
release.reserve(ALL_CASES_NUM[head]); release.reserve(ALL_CASES_NUM[head]);
auto &basic_ranges = BasicRanges::Instance().Fetch(); auto &basic_ranges = BasicRanges::instance().fetch();
for (uint32_t index = 0; index < basic_ranges.size(); ++index) { for (uint32_t index = 0; index < basic_ranges.size(); ++index) {
auto offset = check_range(head, basic_ranges[index]); auto offset = check_range(head, basic_ranges[index]);
if (offset) { // invalid case if (offset) { // invalid case
@ -71,14 +71,14 @@ void AllCases::BuildCases(int head, Ranges &release) noexcept {
} }
/// Execute the build process and ensure thread safety. /// Execute the build process and ensure thread safety.
void AllCases::Build() noexcept { void AllCases::build() noexcept {
BuildParallel([](auto &&func) { build_parallel([](auto &&func) {
func(); func();
}); });
} }
/// Execute the build process with parallel support and ensure thread safety. /// Execute the build process with parallel support and ensure thread safety.
void AllCases::BuildParallel(Executor &&executor) noexcept { void AllCases::build_parallel(Executor &&executor) noexcept {
if (available_) { if (available_) {
return; // reduce consumption of mutex return; // reduce consumption of mutex
} }
@ -93,7 +93,7 @@ void AllCases::BuildParallel(Executor &&executor) noexcept {
auto promise = std::make_shared<std::promise<void>>(); auto promise = std::make_shared<std::promise<void>>();
futures.emplace_back(promise->get_future()); futures.emplace_back(promise->get_future());
executor([head, promise = std::move(promise)]() { executor([head, promise = std::move(promise)]() {
BuildCases(head, GetCases()[head]); build_cases(head, get_cases()[head]);
promise->set_value(); // subtask completed notification promise->set_value(); // subtask completed notification
}); });
} }
@ -104,7 +104,7 @@ void AllCases::BuildParallel(Executor &&executor) noexcept {
} }
/// Execute the build process in parallel without blocking. /// Execute the build process in parallel without blocking.
void AllCases::BuildParallelAsync(Executor &&executor, Notifier &&callback) noexcept { void AllCases::build_parallel_async(Executor &&executor, Notifier &&callback) noexcept {
if (available_) { if (available_) {
callback(); callback();
return; // reduce consumption of mutex return; // reduce consumption of mutex
@ -119,7 +119,7 @@ void AllCases::BuildParallelAsync(Executor &&executor, Notifier &&callback) noex
auto all_done = std::make_shared<Notifier>(std::move(callback)); auto all_done = std::make_shared<Notifier>(std::move(callback));
for (auto head : case_heads()) { for (auto head : case_heads()) {
executor([this, head, counter, all_done]() { executor([this, head, counter, all_done]() {
BuildCases(head, GetCases()[head]); build_cases(head, get_cases()[head]);
if (counter->fetch_add(1) == case_heads().size() - 1) { // all tasks done if (counter->fetch_add(1) == case_heads().size() - 1) { // all tasks done
available_ = true; available_ = true;
building_.unlock(); // release building mutex building_.unlock(); // release building mutex
@ -129,24 +129,5 @@ void AllCases::BuildParallelAsync(Executor &&executor, Notifier &&callback) noex
} }
} }
RangesUnion& AllCases::GetCases() noexcept {
static RangesUnion cases;
return cases;
}
AllCases& AllCases::Instance() noexcept {
static AllCases instance;
return instance;
}
const RangesUnion& AllCases::Fetch() noexcept {
this->Build();
return GetCases();
}
bool AllCases::IsAvailable() const noexcept {
return available_;
}
} // namespace cases } // namespace cases
} // namespace klotski } // namespace klotski

76
src/core/all_cases/all_cases.h

@ -53,6 +53,8 @@ typedef std::array<Ranges, 16> RangesUnion;
typedef std::function<void()> Notifier; typedef std::function<void()> Notifier;
typedef std::function<void(std::function<void()>&&)> Executor; typedef std::function<void(std::function<void()>&&)> Executor;
// ----------------------------------------------------------------------------------------- //
constexpr auto BASIC_RANGES_NUM = 7311921; constexpr auto BASIC_RANGES_NUM = 7311921;
constexpr std::array<int, 16> ALL_CASES_NUM { constexpr std::array<int, 16> ALL_CASES_NUM {
@ -66,45 +68,89 @@ constexpr auto ALL_CASES_NUM_ = std::accumulate(
ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0 ALL_CASES_NUM.begin(), ALL_CASES_NUM.end(), 0
); );
// ----------------------------------------------------------------------------------------- //
class BasicRanges { class BasicRanges {
public: public:
void Build() noexcept; void build() noexcept;
const Ranges& Fetch() noexcept; const Ranges& fetch() noexcept;
[[nodiscard]] bool IsAvailable() const noexcept; [[nodiscard]] bool is_available() const noexcept;
DISALLOW_COPY_AND_ASSIGN(BasicRanges); DISALLOW_COPY_AND_ASSIGN(BasicRanges);
static BasicRanges& Instance() noexcept; static BasicRanges& instance() noexcept;
private: private:
std::mutex building_; std::mutex building_;
bool available_ = false; bool available_ = false;
BasicRanges() = default; BasicRanges() = default;
static Ranges& GetRanges() noexcept; static Ranges& get_ranges() noexcept;
static void BuildRanges(Ranges &ranges) noexcept; static void build_ranges(Ranges &ranges) noexcept;
static void SpawnRanges(Ranges &ranges, int, int, int, int) noexcept; static void spawn_ranges(Ranges &ranges, int, int, int, int) noexcept;
}; };
inline BasicRanges& BasicRanges::instance() noexcept {
static BasicRanges instance;
return instance;
}
inline Ranges& BasicRanges::get_ranges() noexcept {
static Ranges ranges;
return ranges;
}
inline const Ranges& BasicRanges::fetch() noexcept {
this->build();
return get_ranges();
}
inline bool BasicRanges::is_available() const noexcept {
return available_; // no mutex required in one-way state
}
// ----------------------------------------------------------------------------------------- //
class AllCases { class AllCases {
public: public:
void Build() noexcept; void build() noexcept;
void BuildParallel(Executor &&executor) noexcept; void build_parallel(Executor &&executor) noexcept;
void BuildParallelAsync(Executor &&executor, Notifier &&callback) noexcept; void build_parallel_async(Executor &&executor, Notifier &&callback) noexcept;
const RangesUnion& Fetch() noexcept; const RangesUnion& fetch() noexcept;
[[nodiscard]] bool IsAvailable() const noexcept; [[nodiscard]] bool is_available() const noexcept;
DISALLOW_COPY_AND_ASSIGN(AllCases); DISALLOW_COPY_AND_ASSIGN(AllCases);
static AllCases& Instance() noexcept; static AllCases& instance() noexcept;
private: private:
std::mutex building_; std::mutex building_;
bool available_ = false; bool available_ = false;
AllCases() = default; AllCases() = default;
static RangesUnion& GetCases() noexcept; static RangesUnion& get_cases() noexcept;
static void BuildCases(int head, Ranges &release) noexcept; static void build_cases(int head, Ranges &release) noexcept;
}; };
inline AllCases& AllCases::instance() noexcept {
static AllCases instance;
return instance;
}
inline RangesUnion& AllCases::get_cases() noexcept {
static RangesUnion cases;
return cases;
}
inline const RangesUnion& AllCases::fetch() noexcept {
this->build();
return get_cases();
}
inline bool AllCases::is_available() const noexcept {
return available_; // no mutex required in one-way state
}
// ----------------------------------------------------------------------------------------- //
} // namespace cases } // namespace cases
} // namespace klotski } // namespace klotski

29
src/core/all_cases/basic_ranges.cc

@ -39,7 +39,7 @@ static void combine_sort(RangeIter begin, RangeIter mid, RangeIter end) noexcept
} }
/// Spawn all ranges of specified conditions. /// Spawn all ranges of specified conditions.
void BasicRanges::SpawnRanges(Ranges &ranges, int n1, int n2, int n3, int n4) noexcept { void BasicRanges::spawn_ranges(Ranges &ranges, int n1, int n2, int n3, int n4) noexcept {
auto num = n1 + n2 + n3 + n4; auto num = n1 + n2 + n3 + n4;
auto offset = (16 - num) << 1; // offset of low bits auto offset = (16 - num) << 1; // offset of low bits
@ -59,13 +59,13 @@ void BasicRanges::SpawnRanges(Ranges &ranges, int n1, int n2, int n3, int n4) no
} }
/// Search and sort all possible basic-ranges permutations. /// Search and sort all possible basic-ranges permutations.
void BasicRanges::BuildRanges(Ranges &ranges) noexcept { void BasicRanges::build_ranges(Ranges &ranges) noexcept {
ranges.clear(); ranges.clear();
ranges.reserve(BASIC_RANGES_NUM); ranges.reserve(BASIC_RANGES_NUM);
std::list<RangeIter> flags {ranges.begin()}; // mark ordered interval std::list<RangeIter> flags {ranges.begin()}; // mark ordered interval
for (auto &t : range_types()) { for (auto &t : range_types()) {
SpawnRanges(ranges, std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t)); spawn_ranges(ranges, std::get<0>(t), std::get<1>(t), std::get<2>(t), std::get<3>(t));
flags.emplace_back(ranges.end()); flags.emplace_back(ranges.end());
} }
do { do {
@ -83,7 +83,7 @@ void BasicRanges::BuildRanges(Ranges &ranges) noexcept {
} }
/// Execute the build process and ensure thread safety. /// Execute the build process and ensure thread safety.
void BasicRanges::Build() noexcept { void BasicRanges::build() noexcept {
if (available_) { if (available_) {
return; // reduce consumption of mutex return; // reduce consumption of mutex
} }
@ -91,28 +91,9 @@ void BasicRanges::Build() noexcept {
if (available_) { if (available_) {
return; // data is already available return; // data is already available
} }
BuildRanges(GetRanges()); build_ranges(get_ranges());
available_ = true; available_ = true;
} }
Ranges& BasicRanges::GetRanges() noexcept {
static Ranges ranges;
return ranges;
}
BasicRanges& BasicRanges::Instance() noexcept {
static BasicRanges instance;
return instance;
}
const Ranges& BasicRanges::Fetch() noexcept {
this->Build();
return GetRanges();
}
bool BasicRanges::IsAvailable() const noexcept {
return available_; // no mutex required in one-way state
}
} // namespace cases } // namespace cases
} // namespace klotski } // namespace klotski

22
src/core/ffi/all_cases.cc

@ -6,7 +6,7 @@ using klotski::cases::BasicRanges;
using klotski::cases::ALL_CASES_NUM; using klotski::cases::ALL_CASES_NUM;
void all_cases_prebuild() { void all_cases_prebuild() {
BasicRanges::Instance().Build(); BasicRanges::instance().build();
} }
void all_cases_prebuild_async(executor_t executor, notifier_t callback) { void all_cases_prebuild_async(executor_t executor, notifier_t callback) {
@ -17,15 +17,11 @@ void all_cases_prebuild_async(executor_t executor, notifier_t callback) {
} }
int all_cases_prebuild_available() { int all_cases_prebuild_available() {
if (BasicRanges::Instance().IsAvailable()) { return BasicRanges::instance().is_available() ? KLOTSKI_TRUE : KLOTSKI_FALSE;
return KLOTSKI_TRUE;
} else {
return KLOTSKI_FALSE;
}
} }
void all_cases_build() { void all_cases_build() {
AllCases::Instance().Build(); AllCases::instance().build();
} }
void all_cases_build_async(executor_t executor, notifier_t callback) { void all_cases_build_async(executor_t executor, notifier_t callback) {
@ -37,7 +33,7 @@ void all_cases_build_async(executor_t executor, notifier_t callback) {
void all_cases_build_parallel(executor_t executor) { void all_cases_build_parallel(executor_t executor) {
typedef std::function<void()> Runner; typedef std::function<void()> Runner;
AllCases::Instance().BuildParallel([executor](Runner &&runner) { AllCases::instance().build_parallel([executor](Runner &&runner) {
executor([](void *fn) { executor([](void *fn) {
(*(Runner*)fn)(); (*(Runner*)fn)();
delete (Runner*)fn; delete (Runner*)fn;
@ -50,7 +46,7 @@ void all_cases_build_parallel_async(executor_t executor, notifier_t callback) {
auto all_done = [callback]() { auto all_done = [callback]() {
callback(); callback();
}; };
AllCases::Instance().BuildParallelAsync([executor](Runner &&runner) { AllCases::instance().build_parallel_async([executor](Runner &&runner) {
executor([](void *fn) { executor([](void *fn) {
(*(Runner*)fn)(); (*(Runner*)fn)();
delete (Runner*)fn; delete (Runner*)fn;
@ -59,11 +55,7 @@ void all_cases_build_parallel_async(executor_t executor, notifier_t callback) {
} }
int all_cases_available() { int all_cases_available() {
if (AllCases::Instance().IsAvailable()) { return AllCases::instance().is_available() ? KLOTSKI_TRUE : KLOTSKI_FALSE;
return KLOTSKI_TRUE;
} else {
return KLOTSKI_FALSE;
}
} }
int all_cases_num(int head) { int all_cases_num(int head) {
@ -77,5 +69,5 @@ const klotski_u32* all_cases_export(int head) {
if (all_cases_num(head) < 0) { if (all_cases_num(head) < 0) {
return nullptr; return nullptr;
} }
return AllCases::Instance().Fetch()[head].data(); return AllCases::instance().fetch()[head].data();
} }

4
src/core/short_code/convert.cc

@ -59,7 +59,7 @@ ShortCode ShortCode::from_common_code(CommonCode common_code) noexcept {
/// NOTE: ensure that input common code is valid! /// NOTE: ensure that input common code is valid!
uint32_t ShortCode::fast_encode(uint64_t common_code) noexcept { // common code --> short code uint32_t ShortCode::fast_encode(uint64_t common_code) noexcept { // common code --> short code
auto head = common_code >> 32; // head index auto head = common_code >> 32; // head index
const auto &ranges = AllCases::Instance().Fetch()[head]; // available ranges const auto &ranges = AllCases::instance().fetch()[head]; // available ranges
auto offset = std::lower_bound(ranges.begin(), ranges.end(), (uint32_t)common_code) - ranges.begin(); auto offset = std::lower_bound(ranges.begin(), ranges.end(), (uint32_t)common_code) - ranges.begin();
return ALL_CASES_OFFSET[head] + offset; // release short code return ALL_CASES_OFFSET[head] + offset; // release short code
} }
@ -70,7 +70,7 @@ uint64_t ShortCode::fast_decode(uint32_t short_code) noexcept { // short code --
ALL_CASES_OFFSET, ALL_CASES_OFFSET + 16, short_code ALL_CASES_OFFSET, ALL_CASES_OFFSET + 16, short_code
) - 1; ) - 1;
uint64_t head = offset - ALL_CASES_OFFSET; // head index uint64_t head = offset - ALL_CASES_OFFSET; // head index
return (head << 32) | AllCases::Instance().Fetch()[head][short_code - *offset]; // release common code return (head << 32) | AllCases::instance().fetch()[head][short_code - *offset]; // release common code
} }
/// NOTE: ensure that input common code is valid! /// NOTE: ensure that input common code is valid!

11
src/core_test/CMakeLists.txt

@ -11,6 +11,7 @@ include_directories(utils)
include_directories(${KLOTSKI_ROOT}/src/core/ffi) include_directories(${KLOTSKI_ROOT}/src/core/ffi)
include_directories(${KLOTSKI_ROOT}/src/core/utils) include_directories(${KLOTSKI_ROOT}/src/core/utils)
include_directories(${KLOTSKI_ROOT}/src/core/all_cases) include_directories(${KLOTSKI_ROOT}/src/core/all_cases)
include_directories(${KLOTSKI_ROOT}/src/core/common_code)
############################################################################################### ###############################################################################################
@ -33,3 +34,13 @@ target_link_libraries(test_klotski_ffi PRIVATE ${KLOTSKI_TEST_DEPS})
add_test(NAME klotski_ffi COMMAND test_klotski_ffi) add_test(NAME klotski_ffi COMMAND test_klotski_ffi)
############################################################################################### ###############################################################################################
set(KLOTSKI_TEST_CODEC_SRC
codec/common_code.cc
)
add_executable(test_klotski_codec ${KLOTSKI_TEST_CODEC_SRC})
target_link_libraries(test_klotski_codec PRIVATE ${KLOTSKI_TEST_DEPS})
add_test(NAME klotski_codec COMMAND test_klotski_codec)
###############################################################################################

97
src/core_test/cases/all_cases.cc

@ -27,24 +27,24 @@ PRIVATE_ACCESS(BasicRanges, available_, bool)
/// Reset basic ranges build state, note it is thread-unsafe. /// Reset basic ranges build state, note it is thread-unsafe.
void basic_ranges_reset() { void basic_ranges_reset() {
access_BasicRanges_available_(BasicRanges::Instance()) = false; access_BasicRanges_available_(BasicRanges::instance()) = false;
} }
/// Reset all cases build state, note it is thread-unsafe. /// Reset all cases build state, note it is thread-unsafe.
void all_cases_reset() { void all_cases_reset() {
access_AllCases_available_(AllCases::Instance()) = false; access_AllCases_available_(AllCases::instance()) = false;
} }
/// Verify that whether basic ranges data is correct. /// Verify that whether basic ranges data is correct.
void basic_ranges_verify() { void basic_ranges_verify() {
auto &basic_ranges = BasicRanges::Instance().Fetch(); auto &basic_ranges = BasicRanges::instance().fetch();
EXPECT_EQ(basic_ranges.size(), BASIC_RANGES_NUM); // verify basic ranges size EXPECT_EQ(basic_ranges.size(), BASIC_RANGES_NUM); // verify basic ranges size
EXPECT_EQ(xxhsum(basic_ranges), BASIC_RANGES_XXHASH); // verify basic ranges checksum EXPECT_EQ(xxhsum(basic_ranges), BASIC_RANGES_XXHASH); // verify basic ranges checksum
} }
/// Verify that whether all cases data is correct. /// Verify that whether all cases data is correct.
void all_cases_verify() { void all_cases_verify() {
auto &all_cases = AllCases::Instance().Fetch(); auto &all_cases = AllCases::instance().fetch();
for (int head = 0; head < 16; ++head) { for (int head = 0; head < 16; ++head) {
EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size EXPECT_EQ(all_cases[head].size(), ALL_CASES_NUM[head]); // verify all cases size
} }
@ -57,18 +57,18 @@ void all_cases_verify() {
std::string all_cases_xxh; std::string all_cases_xxh;
for (uint64_t head = 0; head < 16; ++head) { for (uint64_t head = 0; head < 16; ++head) {
all_cases_xxh += xxhsum(AllCases::Instance().Fetch()[head]); all_cases_xxh += xxhsum(AllCases::instance().fetch()[head]);
} }
EXPECT_EQ(xxhsum(all_cases_xxh), ALL_CASES_XXHASH); // verify all cases checksum EXPECT_EQ(xxhsum(all_cases_xxh), ALL_CASES_XXHASH); // verify all cases checksum
} }
TEST(Cases, basic_ranges) { TEST(Cases, basic_ranges) {
basic_ranges_reset(); basic_ranges_reset();
EXPECT_FALSE(BasicRanges::Instance().IsAvailable()); EXPECT_FALSE(BasicRanges::instance().is_available());
BasicRanges::Instance().Build(); BasicRanges::instance().build();
EXPECT_TRUE(BasicRanges::Instance().IsAvailable()); EXPECT_TRUE(BasicRanges::instance().is_available());
BasicRanges::Instance().Build(); BasicRanges::instance().build();
EXPECT_TRUE(BasicRanges::Instance().IsAvailable()); EXPECT_TRUE(BasicRanges::instance().is_available());
basic_ranges_verify(); basic_ranges_verify();
} }
@ -77,21 +77,21 @@ TEST(Cases, basic_ranges_mutex) {
BS::thread_pool pool(TEST_THREAD_NUM); BS::thread_pool pool(TEST_THREAD_NUM);
for (int i = 0; i < TEST_THREAD_NUM; ++i) { for (int i = 0; i < TEST_THREAD_NUM; ++i) {
auto _ = pool.submit(&BasicRanges::Build, &BasicRanges::Instance()); auto _ = pool.submit(&BasicRanges::build, &BasicRanges::instance());
} }
EXPECT_FALSE(BasicRanges::Instance().IsAvailable()); EXPECT_FALSE(BasicRanges::instance().is_available());
pool.wait_for_tasks(); pool.wait_for_tasks();
EXPECT_TRUE(BasicRanges::Instance().IsAvailable()); EXPECT_TRUE(BasicRanges::instance().is_available());
basic_ranges_verify(); basic_ranges_verify();
} }
TEST(Cases, all_cases) { TEST(Cases, all_cases) {
all_cases_reset(); all_cases_reset();
EXPECT_FALSE(AllCases::Instance().IsAvailable()); EXPECT_FALSE(AllCases::instance().is_available());
AllCases::Instance().Build(); AllCases::instance().build();
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
AllCases::Instance().Build(); AllCases::instance().build();
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
all_cases_verify(); all_cases_verify();
} }
@ -100,26 +100,26 @@ TEST(Cases, all_cases_mutex) {
BS::thread_pool pool(TEST_THREAD_NUM); BS::thread_pool pool(TEST_THREAD_NUM);
for (int i = 0; i < TEST_THREAD_NUM; ++i) { for (int i = 0; i < TEST_THREAD_NUM; ++i) {
auto _ = pool.submit(&AllCases::Build, &AllCases::Instance()); auto _ = pool.submit(&AllCases::build, &AllCases::instance());
} }
EXPECT_FALSE(AllCases::Instance().IsAvailable()); EXPECT_FALSE(AllCases::instance().is_available());
pool.wait_for_tasks(); pool.wait_for_tasks();
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
all_cases_verify(); all_cases_verify();
} }
TEST(Cases, all_cases_parallel) { TEST(Cases, all_cases_parallel) {
all_cases_reset(); all_cases_reset();
BS::thread_pool executor; BS::thread_pool executor;
EXPECT_FALSE(AllCases::Instance().IsAvailable()); EXPECT_FALSE(AllCases::instance().is_available());
AllCases::Instance().BuildParallel([&executor](auto &&func) { AllCases::instance().build_parallel([&executor](auto &&func) {
executor.push_task(func); executor.push_task(func);
}); });
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
AllCases::Instance().BuildParallel([&executor](auto &&func) { AllCases::instance().build_parallel([&executor](auto &&func) {
executor.push_task(func); executor.push_task(func);
}); });
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
all_cases_verify(); all_cases_verify();
} }
@ -129,42 +129,43 @@ TEST(Cases, all_cases_parallel_mutex) {
BS::thread_pool pool(TEST_THREAD_NUM); BS::thread_pool pool(TEST_THREAD_NUM);
for (int i = 0; i < TEST_THREAD_NUM; ++i) { for (int i = 0; i < TEST_THREAD_NUM; ++i) {
auto _ = pool.submit(&AllCases::BuildParallel, &AllCases::Instance(), [&executor](auto &&func) { auto _ = pool.submit(&AllCases::build_parallel, &AllCases::instance(), [&executor](auto &&func) {
executor.push_task(func); executor.push_task(func);
}); });
} }
EXPECT_FALSE(AllCases::Instance().IsAvailable()); EXPECT_FALSE(AllCases::instance().is_available());
pool.wait_for_tasks(); pool.wait_for_tasks();
executor.wait_for_tasks(); executor.wait_for_tasks();
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
all_cases_verify(); all_cases_verify();
} }
TEST(Cases, all_cases_async) { TEST(Cases, all_cases_async) {
all_cases_reset(); all_cases_reset();
std::atomic_flag flag;
BS::thread_pool executor; BS::thread_pool executor;
std::promise<void> promise_1; flag.clear();
auto future_1 = promise_1.get_future(); AllCases::instance().build_parallel_async([&executor](auto &&func) {
AllCases::Instance().BuildParallelAsync([&executor](auto &&func) {
executor.push_task(func); executor.push_task(func);
}, [&promise_1]() { }, [&flag]() { // callback function
promise_1.set_value(); flag.test_and_set();
flag.notify_all();
}); });
EXPECT_FALSE(AllCases::Instance().IsAvailable()); EXPECT_FALSE(AllCases::instance().is_available());
future_1.wait(); flag.wait(false);
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
std::promise<void> promise_2; flag.clear();
auto future_2 = promise_2.get_future(); AllCases::instance().build_parallel_async([&executor](auto &&func) {
AllCases::Instance().BuildParallelAsync([&executor](auto &&func) {
executor.push_task(func); executor.push_task(func);
}, [&promise_2]() { }, [&flag]() { // callback function
promise_2.set_value(); flag.test_and_set();
flag.notify_all();
}); });
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
future_2.wait(); flag.wait(false);
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
all_cases_verify(); all_cases_verify();
} }
@ -175,16 +176,16 @@ TEST(Cases, all_cases_async_mutex) {
BS::thread_pool pool(TEST_THREAD_NUM); BS::thread_pool pool(TEST_THREAD_NUM);
for (int i = 0; i < TEST_THREAD_NUM; ++i) { for (int i = 0; i < TEST_THREAD_NUM; ++i) {
auto _ = pool.submit(&AllCases::BuildParallelAsync, &AllCases::Instance(), [&executor](auto &&func) { auto _ = pool.submit(&AllCases::build_parallel_async, &AllCases::instance(), [&executor](auto &&func) {
executor.push_task(func); executor.push_task(func);
}, [&callback_num]() { }, [&callback_num]() {
callback_num.fetch_add(1); callback_num.fetch_add(1);
}); });
} }
EXPECT_FALSE(AllCases::Instance().IsAvailable()); EXPECT_FALSE(AllCases::instance().is_available());
pool.wait_for_tasks(); pool.wait_for_tasks();
executor.wait_for_tasks(); executor.wait_for_tasks();
EXPECT_TRUE(AllCases::Instance().IsAvailable()); EXPECT_TRUE(AllCases::instance().is_available());
EXPECT_EQ(callback_num.load(), TEST_THREAD_NUM); EXPECT_EQ(callback_num.load(), TEST_THREAD_NUM);
all_cases_verify(); all_cases_verify();
} }

86
src/core_test/ffi/all_cases.cc

@ -25,15 +25,15 @@ PRIVATE_ACCESS(BasicRanges, available_, bool)
/// Reset basic ranges build state, note it is thread-unsafe. /// Reset basic ranges build state, note it is thread-unsafe.
void basic_ranges_reset() { void basic_ranges_reset() {
access_BasicRanges_available_(BasicRanges::Instance()) = false; access_BasicRanges_available_(BasicRanges::instance()) = false;
} }
/// Reset all cases build state, note it is thread-unsafe. /// Reset all cases build state, note it is thread-unsafe.
void all_cases_reset() { void all_cases_reset() {
access_AllCases_available_(AllCases::Instance()) = false; access_AllCases_available_(AllCases::instance()) = false;
} }
TEST(FFI, all_cases_prebuild) { TEST(AllCases, all_cases_prebuild) {
basic_ranges_reset(); basic_ranges_reset();
EXPECT_FALSE(all_cases_prebuild_available()); EXPECT_FALSE(all_cases_prebuild_available());
all_cases_prebuild(); all_cases_prebuild();
@ -42,36 +42,36 @@ TEST(FFI, all_cases_prebuild) {
EXPECT_TRUE(all_cases_prebuild_available()); EXPECT_TRUE(all_cases_prebuild_available());
} }
TEST(FFI, all_cases_prebuild_async) { TEST(AllCases, all_cases_prebuild_async) {
basic_ranges_reset(); basic_ranges_reset();
static std::atomic_flag chan; static std::atomic_flag flag;
chan.clear(); flag.clear();
all_cases_prebuild_async([](void (*fn)(void*), void *arg) { all_cases_prebuild_async([](void (*fn)(void*), void *arg) {
std::thread worker(fn, arg); std::thread worker(fn, arg);
worker.detach(); worker.detach();
}, []() { }, []() { // callback function
chan.test_and_set(); flag.test_and_set();
chan.notify_all(); flag.notify_all();
}); });
EXPECT_FALSE(all_cases_prebuild_available()); EXPECT_FALSE(all_cases_prebuild_available());
chan.wait(false); flag.wait(false);
EXPECT_TRUE(all_cases_prebuild_available()); EXPECT_TRUE(all_cases_prebuild_available());
chan.clear(); flag.clear();
all_cases_prebuild_async([](void (*fn)(void*), void *arg) { all_cases_prebuild_async([](void (*fn)(void*), void *arg) {
std::thread worker(fn, arg); std::thread worker(fn, arg);
worker.detach(); worker.detach();
}, []() { }, []() { // callback function
chan.test_and_set(); flag.test_and_set();
chan.notify_all(); flag.notify_all();
}); });
EXPECT_TRUE(all_cases_prebuild_available()); EXPECT_TRUE(all_cases_prebuild_available());
chan.wait(false); flag.wait(false);
EXPECT_TRUE(all_cases_prebuild_available()); EXPECT_TRUE(all_cases_prebuild_available());
} }
TEST(FFI, all_cases_build) { TEST(AllCases, all_cases_build) {
all_cases_reset(); all_cases_reset();
EXPECT_FALSE(all_cases_available()); EXPECT_FALSE(all_cases_available());
all_cases_build(); all_cases_build();
@ -80,36 +80,36 @@ TEST(FFI, all_cases_build) {
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
} }
TEST(FFI, all_cases_build_async) { TEST(AllCases, all_cases_build_async) {
all_cases_reset(); all_cases_reset();
static std::atomic_flag chan; static std::atomic_flag flag;
chan.clear(); flag.clear();
all_cases_build_async([](void (*fn)(void*), void *arg) { all_cases_build_async([](void (*fn)(void*), void *arg) {
std::thread worker(fn, arg); std::thread worker(fn, arg);
worker.detach(); worker.detach();
}, []() { }, []() { // callback function
chan.test_and_set(); flag.test_and_set();
chan.notify_all(); flag.notify_all();
}); });
EXPECT_FALSE(all_cases_available()); EXPECT_FALSE(all_cases_available());
chan.wait(false); flag.wait(false);
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
chan.clear(); flag.clear();
all_cases_build_async([](void (*fn)(void*), void *arg) { all_cases_build_async([](void (*fn)(void*), void *arg) {
std::thread worker(fn, arg); std::thread worker(fn, arg);
worker.detach(); worker.detach();
}, []() { }, []() { // callback function
chan.test_and_set(); flag.test_and_set();
chan.notify_all(); flag.notify_all();
}); });
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
chan.wait(false); flag.wait(false);
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
} }
TEST(FFI, all_cases_build_parallel) { TEST(AllCases, all_cases_build_parallel) {
all_cases_reset(); all_cases_reset();
EXPECT_FALSE(all_cases_available()); EXPECT_FALSE(all_cases_available());
all_cases_build_parallel([](void (*fn)(void*), void *arg) { all_cases_build_parallel([](void (*fn)(void*), void *arg) {
@ -124,36 +124,36 @@ TEST(FFI, all_cases_build_parallel) {
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
} }
TEST(FFI, all_cases_build_parallel_async) { TEST(AllCases, all_cases_build_parallel_async) {
all_cases_reset(); all_cases_reset();
static std::atomic_flag chan; static std::atomic_flag flag;
chan.clear(); flag.clear();
all_cases_build_parallel_async([](void (*fn)(void*), void *arg) { all_cases_build_parallel_async([](void (*fn)(void*), void *arg) {
std::thread worker(fn, arg); std::thread worker(fn, arg);
worker.detach(); worker.detach();
}, []() { }, []() { // callback function
chan.test_and_set(); flag.test_and_set();
chan.notify_all(); flag.notify_all();
}); });
EXPECT_FALSE(all_cases_available()); EXPECT_FALSE(all_cases_available());
chan.wait(false); flag.wait(false);
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
chan.clear(); flag.clear();
all_cases_build_parallel_async([](void (*fn)(void*), void *arg) { all_cases_build_parallel_async([](void (*fn)(void*), void *arg) {
std::thread worker(fn, arg); std::thread worker(fn, arg);
worker.detach(); worker.detach();
}, []() { }, []() { // callback function
chan.test_and_set(); flag.test_and_set();
chan.notify_all(); flag.notify_all();
}); });
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
chan.wait(false); flag.wait(false);
EXPECT_TRUE(all_cases_available()); EXPECT_TRUE(all_cases_available());
} }
TEST(FFI, all_cases_num) { TEST(AllCases, all_cases_num) {
EXPECT_LT(all_cases_num(-2), 0); EXPECT_LT(all_cases_num(-2), 0);
EXPECT_LT(all_cases_num(-1), 0); EXPECT_LT(all_cases_num(-1), 0);
for (int i = 0; i < 15; ++i) { for (int i = 0; i < 15; ++i) {
@ -163,7 +163,7 @@ TEST(FFI, all_cases_num) {
EXPECT_LT(all_cases_num(17), 0); EXPECT_LT(all_cases_num(17), 0);
} }
TEST(FFI, all_cases_export) { TEST(AllCases, all_cases_export) {
EXPECT_EQ(all_cases_export(-2), nullptr); EXPECT_EQ(all_cases_export(-2), nullptr);
EXPECT_EQ(all_cases_export(-1), nullptr); EXPECT_EQ(all_cases_export(-1), nullptr);
EXPECT_EQ(all_cases_export(16), nullptr); EXPECT_EQ(all_cases_export(16), nullptr);

Loading…
Cancel
Save