Skip to content

Commit 525e37f

Browse files
Remove folly::EvictingCacheMap (#52019)
Summary: Pull Request resolved: #52019 Replace folly::EvictionMap with pure std implementation changelog: [internal] Differential Revision: D76631579
1 parent 05a61e8 commit 525e37f

File tree

2 files changed

+86
-18
lines changed

2 files changed

+86
-18
lines changed

packages/react-native/ReactCommon/react/utils/SimpleThreadSafeCache.h

Lines changed: 41 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,9 @@
88
#pragma once
99

1010
#include <concepts>
11+
#include <list>
1112
#include <mutex>
12-
#include <optional>
13-
#include <type_traits>
14-
15-
#include <folly/container/EvictingCacheMap.h>
13+
#include <unordered_map>
1614

1715
namespace facebook::react {
1816

@@ -26,8 +24,8 @@ concept CacheGeneratorFunction = std::invocable<GeneratorT> &&
2624
template <typename KeyT, typename ValueT, int maxSize>
2725
class SimpleThreadSafeCache {
2826
public:
29-
SimpleThreadSafeCache() : map_{maxSize} {}
30-
SimpleThreadSafeCache(unsigned long size) : map_{size} {}
27+
SimpleThreadSafeCache() : maxSize_(maxSize) {}
28+
SimpleThreadSafeCache(unsigned long size) : maxSize_{size} {}
3129

3230
/*
3331
* Returns a value from the map with a given key.
@@ -38,14 +36,16 @@ class SimpleThreadSafeCache {
3836
ValueT get(const KeyT& key, CacheGeneratorFunction<ValueT> auto generator)
3937
const {
4038
std::lock_guard<std::mutex> lock(mutex_);
41-
auto iterator = map_.find(key);
42-
if (iterator == map_.end()) {
43-
auto value = generator();
44-
map_.set(key, value);
45-
return value;
39+
40+
if (auto it = map_.find(key); it != map_.end()) {
41+
// Move accessed item to front of list
42+
list_.splice(list_.begin(), list_, it->second);
43+
return it->second->second;
4644
}
4745

48-
return iterator->second;
46+
auto value = generator();
47+
insert(key, value);
48+
return value;
4949
}
5050

5151
/*
@@ -55,12 +55,14 @@ class SimpleThreadSafeCache {
5555
*/
5656
std::optional<ValueT> get(const KeyT& key) const {
5757
std::lock_guard<std::mutex> lock(mutex_);
58-
auto iterator = map_.find(key);
59-
if (iterator == map_.end()) {
60-
return {};
58+
59+
if (auto it = map_.find(key); it != map_.end()) {
60+
// Move accessed item to front of list
61+
list_.splice(list_.begin(), list_, it->second);
62+
return it->second->second;
6163
}
6264

63-
return iterator->second;
65+
return ValueT{};
6466
}
6567

6668
/*
@@ -69,12 +71,33 @@ class SimpleThreadSafeCache {
6971
*/
7072
void set(const KeyT& key, const ValueT& value) const {
7173
std::lock_guard<std::mutex> lock(mutex_);
72-
map_.set(std::move(key), std::move(value));
74+
if (auto it = map_.find(key); it != map_.end()) {
75+
// Update existing value and move to front of list
76+
it->second->second = value;
77+
list_.splice(list_.begin(), list_, it->second);
78+
} else {
79+
insert(key, value);
80+
}
7381
}
7482

7583
private:
76-
mutable folly::EvictingCacheMap<KeyT, ValueT> map_;
84+
void insert(const KeyT& key, const ValueT& value) const {
85+
// Add new value to front of list and map
86+
list_.emplace_front(key, value);
87+
map_[key] = list_.begin();
88+
if (list_.size() > maxSize_) {
89+
// Evict least recently used item (back of list)
90+
map_.erase(list_.back().first);
91+
list_.pop_back();
92+
}
93+
}
94+
95+
using iterator = typename std::list<std::pair<KeyT, ValueT>>::iterator;
96+
97+
size_t maxSize_;
7798
mutable std::mutex mutex_;
99+
mutable std::list<std::pair<KeyT, ValueT>> list_;
100+
mutable std::unordered_map<KeyT, iterator> map_;
78101
};
79102

80103
} // namespace facebook::react
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
#include <gtest/gtest.h>
9+
#include <react/utils/SimpleThreadSafeCache.h>
10+
11+
namespace facebook::react {
12+
13+
TEST(EvictingCacheMapTest, BasicInsertAndGet) {
14+
SimpleThreadSafeCache<int, std::string, 3> cache;
15+
cache.set(1, "one");
16+
cache.set(2, "two");
17+
cache.set(3, "three");
18+
EXPECT_EQ(cache.get(1), "one");
19+
EXPECT_EQ(cache.get(2), "two");
20+
EXPECT_EQ(cache.get(3), "three");
21+
}
22+
TEST(EvictingCacheMapTest, Eviction) {
23+
SimpleThreadSafeCache<int, std::string, 2> cache;
24+
cache.set(1, "one");
25+
cache.set(2, "two");
26+
cache.set(3, "three"); // should evict key 1
27+
EXPECT_EQ(
28+
cache.get(1),
29+
""); // key 1 should be evicted and default constructed value is returned
30+
EXPECT_EQ(cache.get(2), "two");
31+
EXPECT_EQ(cache.get(3), "three");
32+
}
33+
TEST(EvictingCacheMapTest, UpdateAndMoveToFront) {
34+
SimpleThreadSafeCache<int, std::string, 3> cache;
35+
cache.set(1, "one");
36+
cache.set(2, "two");
37+
cache.set(3, "three");
38+
cache.set(2, "two updated"); // should move key 2 to front
39+
40+
EXPECT_EQ(cache.get(1), "one");
41+
EXPECT_EQ(cache.get(2), "two updated");
42+
EXPECT_EQ(cache.get(3), "three");
43+
}
44+
45+
} // namespace facebook::react

0 commit comments

Comments
 (0)