mirror of https://github.com/grpc/grpc.git
Merge pull request #14516 from dgquintas/weakmap
Introduce a 'weak' and mutable version of slice hash tablepull/14542/head
commit
abffe598f5
15 changed files with 396 additions and 6 deletions
@ -0,0 +1,105 @@ |
||||
/*
|
||||
* Copyright 2016 gRPC authors. |
||||
* |
||||
* Licensed 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. |
||||
*/ |
||||
|
||||
#ifndef GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H |
||||
#define GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H |
||||
|
||||
#include <grpc/support/port_platform.h> |
||||
|
||||
#include "src/core/lib/gprpp/memory.h" |
||||
#include "src/core/lib/gprpp/ref_counted.h" |
||||
#include "src/core/lib/gprpp/ref_counted_ptr.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
|
||||
/// Weak hash table implementation.
|
||||
///
|
||||
/// This entries in this table are weak: an entry may be removed at any time due
|
||||
/// to a number of reasons: memory pressure, hash collisions, etc.
|
||||
///
|
||||
/// The keys are \a grpc_slice objects. The values are of arbitrary type.
|
||||
///
|
||||
/// This class is thread unsafe. It's the caller's responsibility to ensure
|
||||
/// proper locking when accessing its methods.
|
||||
|
||||
namespace grpc_core { |
||||
|
||||
template <typename T, size_t Size> |
||||
class SliceWeakHashTable : public RefCounted<SliceWeakHashTable<T, Size>> { |
||||
public: |
||||
/// Creates a new table of at most \a size entries.
|
||||
static RefCountedPtr<SliceWeakHashTable> Create() { |
||||
return MakeRefCounted<SliceWeakHashTable<T, Size>>(); |
||||
} |
||||
|
||||
/// Add a mapping from \a key to \a value, taking ownership of \a key. This
|
||||
/// operation will always succeed. It may discard older entries.
|
||||
void Add(grpc_slice key, T value) { |
||||
const size_t idx = grpc_slice_hash(key) % Size; |
||||
entries_[idx].Set(key, std::move(value)); |
||||
return; |
||||
} |
||||
|
||||
/// Returns the value from the table associated with / \a key or null if not
|
||||
/// found.
|
||||
const T* Get(const grpc_slice key) const { |
||||
const size_t idx = grpc_slice_hash(key) % Size; |
||||
const auto& entry = entries_[idx]; |
||||
return grpc_slice_eq(entry.key(), key) ? entry.value() : nullptr; |
||||
} |
||||
|
||||
private: |
||||
// So New() can call our private ctor.
|
||||
template <typename T2, typename... Args> |
||||
friend T2* New(Args&&... args); |
||||
|
||||
SliceWeakHashTable() = default; |
||||
~SliceWeakHashTable() = default; |
||||
|
||||
/// The type of the table "rows".
|
||||
class Entry { |
||||
public: |
||||
Entry() = default; |
||||
~Entry() { |
||||
if (is_set_) grpc_slice_unref_internal(key_); |
||||
} |
||||
grpc_slice key() const { return key_; } |
||||
|
||||
/// Return the entry's value, or null if unset.
|
||||
const T* value() const { |
||||
if (!is_set_) return nullptr; |
||||
return &value_; |
||||
} |
||||
|
||||
/// Set the \a key and \a value (which is moved) for the entry.
|
||||
void Set(grpc_slice key, T&& value) { |
||||
if (is_set_) grpc_slice_unref_internal(key_); |
||||
key_ = key; |
||||
value_ = std::move(value); |
||||
is_set_ = true; |
||||
} |
||||
|
||||
private: |
||||
grpc_slice key_; |
||||
T value_; |
||||
bool is_set_ = false; |
||||
}; |
||||
|
||||
Entry entries_[Size]; |
||||
}; |
||||
|
||||
} // namespace grpc_core
|
||||
|
||||
#endif /* GRPC_CORE_LIB_SLICE_SLICE_WEAK_HASH_TABLE_H */ |
@ -0,0 +1,105 @@ |
||||
/*
|
||||
* |
||||
* Copyright 2017 gRPC authors. |
||||
* |
||||
* Licensed 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 "src/core/lib/slice/slice_weak_hash_table.h" |
||||
|
||||
#include <cstring> |
||||
#include <sstream> |
||||
|
||||
#include <gtest/gtest.h> |
||||
|
||||
#include <grpc/support/alloc.h> |
||||
#include <grpc/support/log.h> |
||||
#include <grpc/support/string_util.h> |
||||
|
||||
#include "src/core/lib/gpr/useful.h" |
||||
#include "src/core/lib/slice/slice_internal.h" |
||||
#include "test/core/util/test_config.h" |
||||
|
||||
namespace grpc_core { |
||||
namespace { |
||||
|
||||
grpc_slice BuildRefCountedKey(const char* key_str) { |
||||
const size_t key_length = strlen(key_str); |
||||
grpc_slice key = grpc_slice_malloc_large(key_length); |
||||
memcpy(GRPC_SLICE_START_PTR(key), key_str, key_length); |
||||
return key; |
||||
} |
||||
|
||||
TEST(SliceWeakHashTable, Basic) { |
||||
auto table = SliceWeakHashTable<UniquePtr<char>, 10>::Create(); |
||||
// Single key-value insertion.
|
||||
grpc_slice key = BuildRefCountedKey("key"); |
||||
grpc_slice_ref(key); // Get doesn't own.
|
||||
table->Add(key, UniquePtr<char>(gpr_strdup("value"))); |
||||
ASSERT_NE(table->Get(key), nullptr); |
||||
ASSERT_STREQ(table->Get(key)->get(), "value"); |
||||
grpc_slice_unref(key); |
||||
// Unknown key.
|
||||
ASSERT_EQ(table->Get(grpc_slice_from_static_string("unknown_key")), nullptr); |
||||
} |
||||
|
||||
TEST(SliceWeakHashTable, ValueTypeConstructor) { |
||||
struct Value { |
||||
Value() : a(123) {} |
||||
int a; |
||||
}; |
||||
auto table = SliceWeakHashTable<Value, 1>::Create(); |
||||
grpc_slice key = BuildRefCountedKey("key"); |
||||
grpc_slice_ref(key); // Get doesn't own.
|
||||
table->Add(key, Value()); |
||||
ASSERT_EQ(table->Get(key)->a, 123); |
||||
grpc_slice_unref(key); |
||||
} |
||||
|
||||
TEST(SliceWeakHashTable, ForceOverload) { |
||||
constexpr int kTableSize = 10; |
||||
auto table = SliceWeakHashTable<UniquePtr<char>, kTableSize>::Create(); |
||||
// Insert a multiple of the maximum size table.
|
||||
for (int i = 0; i < kTableSize * 2; ++i) { |
||||
std::ostringstream oss; |
||||
oss << "key-" << i; |
||||
grpc_slice key = BuildRefCountedKey(oss.str().c_str()); |
||||
oss.clear(); |
||||
oss << "value-" << i; |
||||
table->Add(key, UniquePtr<char>(gpr_strdup(oss.str().c_str()))); |
||||
} |
||||
// Verify that some will have been replaced.
|
||||
int num_missing = 0; |
||||
for (int i = 0; i < kTableSize * 2; ++i) { |
||||
std::ostringstream oss; |
||||
oss << "key-" << i; |
||||
grpc_slice key = BuildRefCountedKey(oss.str().c_str()); |
||||
if (table->Get(key) == nullptr) num_missing++; |
||||
grpc_slice_unref(key); |
||||
} |
||||
// At least kTableSize elements will be missing.
|
||||
ASSERT_GE(num_missing, kTableSize); |
||||
} |
||||
|
||||
} // namespace
|
||||
} // namespace grpc_core
|
||||
|
||||
int main(int argc, char** argv) { |
||||
::testing::InitGoogleTest(&argc, argv); |
||||
grpc_test_init(argc, argv); |
||||
grpc_core::ExecCtx::GlobalInit(); |
||||
int result = RUN_ALL_TESTS(); |
||||
grpc_core::ExecCtx::GlobalShutdown(); |
||||
return result; |
||||
} |
Loading…
Reference in new issue