LLVM_ATTRIBUTE_ALWAYS_INLINE static const Metadata *findHashableBaseTypeImpl(const Metadata *type) { // Check the cache first. if (HashableConformanceEntry *entry = HashableConformances.find(HashableConformanceKey{type})) { return entry->baseTypeThatConformsToHashable; } if (!KnownToConformToHashable && !swift_conformsToProtocol(type, &HashableProtocolDescriptor)) { // Don't cache the negative response because we don't invalidate // this cache when a new conformance is loaded dynamically. return nullptr; } // By this point, `type` is known to conform to `Hashable`. const Metadata *baseTypeThatConformsToHashable = type; while (true) { const Metadata *superclass = _swift_class_getSuperclass(baseTypeThatConformsToHashable); if (!superclass) break; if (!swift_conformsToProtocol(superclass, &HashableProtocolDescriptor)) break; baseTypeThatConformsToHashable = superclass; } HashableConformances.getOrInsert(HashableConformanceKey{type}, baseTypeThatConformsToHashable); return baseTypeThatConformsToHashable; }
TEST(Concurrent, ConcurrentMap) { const int numElem = 100; struct Entry { size_t Key; Entry(size_t key) : Key(key) {} int compareWithKey(size_t key) const { return (key == Key ? 0 : (key < Key ? -1 : 1)); } static size_t getExtraAllocationSize(size_t key) { return 0; } }; ConcurrentMap<Entry> Map; // Add a bunch of numbers to the map concurrently. auto results = RaceTest<int*>( [&]() -> int* { for (int i = 0; i < numElem; i++) { size_t hash = (i * 123512) % 0xFFFF ; Map.getOrInsert(hash); } return nullptr; } ); // Check that all of the values that we inserted are in the map. for (int i=0; i < numElem; i++) { size_t hash = (i * 123512) % 0xFFFF ; EXPECT_TRUE(Map.find(hash)); } }
TEST_F(ConcurrentContainersTest, concurrentMapTest) { ConcurrentMap<std::string, uint32_t> map; run_on_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { std::string s = std::to_string(sample[i]); map.insert({s, sample[i]}); EXPECT_EQ(1, map.count(s)); } }); EXPECT_EQ(m_data_set.size(), map.size()); for (uint32_t x : m_data) { std::string s = std::to_string(x); EXPECT_EQ(1, map.count(s)); auto it = map.find(s); EXPECT_NE(map.end(), it); EXPECT_EQ(s, it->first); EXPECT_EQ(x, it->second); } std::unordered_map<uint32_t, size_t> occurrences; for (uint32_t x : m_data) { ++occurrences[x]; } run_on_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { std::string s = std::to_string(sample[i]); map.update( s, [&s, i](const std::string& key, uint32_t& value, bool key_exists) { EXPECT_EQ(s, key); EXPECT_TRUE(key_exists); ++value; }); } }); EXPECT_EQ(m_data_set.size(), map.size()); for (uint32_t x : m_data) { std::string s = std::to_string(x); EXPECT_EQ(1, map.count(s)); auto it = map.find(s); EXPECT_NE(map.end(), it); EXPECT_EQ(s, it->first); EXPECT_EQ(x + occurrences[x], it->second); } run_on_subset_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { map.erase(std::to_string(sample[i])); } }); for (uint32_t x : m_subset_data) { std::string s = std::to_string(x); EXPECT_EQ(0, map.count(s)); EXPECT_EQ(map.end(), map.find(s)); } run_on_samples([&map](const std::vector<uint32_t>& sample) { for (size_t i = 0; i < sample.size(); ++i) { map.erase(std::to_string(sample[i])); } }); EXPECT_EQ(0, map.size()); for (uint32_t x : m_data) { std::string s = std::to_string(x); EXPECT_EQ(0, map.count(s)); EXPECT_EQ(map.end(), map.find(s)); } map.insert({{"a", 1}, {"b", 2}, {"c", 3}}); EXPECT_EQ(3, map.size()); map.clear(); EXPECT_EQ(0, map.size()); }
ConformanceCacheEntry *findCached(const void *type, const ProtocolDescriptor *proto) { return Cache.find(ConformanceCacheKey(type, proto)); }