| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| #include "paimon/common/utils/concurrent_hash_map.h" |
| |
| #include <unistd.h> |
| |
| #include <cstdlib> |
| #include <functional> |
| #include <thread> |
| |
| #include "gtest/gtest.h" |
| #include "paimon/testing/utils/testharness.h" |
| |
| namespace paimon::test { |
| |
| TEST(ConcurrentHashMapTest, TestSimple) { |
| ConcurrentHashMap<int32_t, std::string> hash_map; |
| ASSERT_EQ(hash_map.Find(10), std::nullopt); |
| hash_map.Insert(1, "a"); |
| hash_map.Insert(2, "b"); |
| hash_map.Insert(3, "c"); |
| |
| ASSERT_EQ(hash_map.Find(1).value(), "a"); |
| ASSERT_EQ(hash_map.Find(2).value(), "b"); |
| ASSERT_EQ(hash_map.Find(3).value(), "c"); |
| ASSERT_EQ(hash_map.Find(10), std::nullopt); |
| ASSERT_EQ(hash_map.Size(), 3); |
| |
| hash_map.Erase(2); |
| ASSERT_EQ(hash_map.Find(2), std::nullopt); |
| ASSERT_EQ(hash_map.Size(), 2); |
| |
| // non-exist key |
| hash_map.Erase(4); |
| ASSERT_EQ(hash_map.Find(1).value(), "a"); |
| ASSERT_EQ(hash_map.Find(3).value(), "c"); |
| ASSERT_EQ(hash_map.Size(), 2); |
| } |
| |
| TEST(ConcurrentHashMapTest, TestVectorStringHashCompare) { |
| ConcurrentHashMap<std::vector<std::string>, int32_t, VectorStringHashCompare> hash_map; |
| ASSERT_EQ(hash_map.Find({}), std::nullopt); |
| |
| hash_map.Insert({"a", "b"}, 1); |
| hash_map.Insert({"a", "c"}, 2); |
| hash_map.Insert({"b", "c"}, 3); |
| hash_map.Insert({}, 4); |
| |
| ASSERT_EQ(hash_map.Find({"a", "b"}).value(), 1); |
| ASSERT_EQ(hash_map.Find({"a", "c"}).value(), 2); |
| ASSERT_EQ(hash_map.Find({"b", "c"}).value(), 3); |
| ASSERT_EQ(hash_map.Find({}), 4); |
| ASSERT_EQ(hash_map.Find({"non"}), std::nullopt); |
| ASSERT_EQ(hash_map.Size(), 4); |
| } |
| |
| TEST(ConcurrentHashMapTest, TestMultiThreadInsertAndFindAndDelete) { |
| int32_t map_size = 1000; |
| auto insert_task = [&](ConcurrentHashMap<int32_t, std::string>& hash_map) { |
| for (int32_t i = 0; i < map_size; i++) { |
| usleep(paimon::test::RandomNumber(0, 9)); |
| hash_map.Insert(i, std::to_string(i + 1)); |
| } |
| }; |
| auto find_task = [&](ConcurrentHashMap<int32_t, std::string>& hash_map) { |
| int32_t found = 0, not_found = 0; |
| for (int32_t i = 0; i < map_size; i++) { |
| usleep(paimon::test::RandomNumber(0, 9)); |
| auto value = hash_map.Find(i); |
| if (value) { |
| ASSERT_EQ(value.value(), std::to_string(i + 1)); |
| found++; |
| } else { |
| not_found++; |
| } |
| } |
| ASSERT_EQ(found + not_found, map_size); |
| }; |
| |
| auto delete_task = [&](ConcurrentHashMap<int32_t, std::string>& hash_map) { |
| for (int32_t i = 0; i < map_size; i++) { |
| usleep(paimon::test::RandomNumber(0, 9)); |
| hash_map.Erase(i); |
| } |
| }; |
| |
| { |
| ConcurrentHashMap<int32_t, std::string> hash_map; |
| // insert |
| insert_task(hash_map); |
| // multi-thread find and delete |
| std::thread thread1(find_task, std::ref(hash_map)); |
| std::thread thread2(delete_task, std::ref(hash_map)); |
| |
| thread1.join(); |
| thread2.join(); |
| |
| // check final states |
| ASSERT_EQ(hash_map.Size(), 0); |
| } |
| { |
| ConcurrentHashMap<int32_t, std::string> hash_map; |
| // multi-thread insert and find |
| std::thread thread1(insert_task, std::ref(hash_map)); |
| std::thread thread2(find_task, std::ref(hash_map)); |
| |
| thread1.join(); |
| thread2.join(); |
| |
| // check final states |
| ASSERT_EQ(hash_map.Size(), map_size); |
| for (int32_t i = 0; i < map_size; i++) { |
| auto value = hash_map.Find(i); |
| ASSERT_TRUE(value); |
| ASSERT_EQ(value.value(), std::to_string(i + 1)); |
| } |
| } |
| { |
| ConcurrentHashMap<int32_t, std::string> hash_map; |
| // multi-thread insert and find and delete |
| std::thread thread1(insert_task, std::ref(hash_map)); |
| std::thread thread2(find_task, std::ref(hash_map)); |
| std::thread thread3(delete_task, std::ref(hash_map)); |
| |
| thread1.join(); |
| thread2.join(); |
| thread3.join(); |
| |
| // check final states |
| ASSERT_TRUE(hash_map.Size() >= 0 && hash_map.Size() <= static_cast<size_t>(map_size)); |
| for (int32_t i = 0; i < map_size; i++) { |
| auto value = hash_map.Find(i); |
| if (value) { |
| ASSERT_EQ(value.value(), std::to_string(i + 1)); |
| } |
| } |
| } |
| } |
| |
| } // namespace paimon::test |