This is an automated email from the ASF dual-hosted git repository.
guangmingchen pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/brpc.git
The following commit(s) were added to refs/heads/master by this push:
new ef82950d Support shared mbvar (#3129)
ef82950d is described below
commit ef82950d172346209db728d8e68522bb5fa489ed
Author: Bright Chen <[email protected]>
AuthorDate: Sat Nov 1 13:07:21 2025 +0800
Support shared mbvar (#3129)
* Support shared mbvar
* Update document
---
docs/cn/mbvar_c++.md | 21 +++++
src/bvar/detail/combiner.h | 10 +-
src/bvar/multi_dimension.h | 57 ++++++++---
src/bvar/multi_dimension_inl.h | 131 ++++++++++++-------------
test/bvar_multi_dimension_unittest.cpp | 168 ++++++++++++++++++++++++++++++++-
test/bvar_mvariable_unittest.cpp | 113 ----------------------
6 files changed, 303 insertions(+), 197 deletions(-)
diff --git a/docs/cn/mbvar_c++.md b/docs/cn/mbvar_c++.md
index 8a81e714..1ce1364f 100644
--- a/docs/cn/mbvar_c++.md
+++ b/docs/cn/mbvar_c++.md
@@ -412,6 +412,7 @@ public:
```
### get_stats
+
根据指定label获取对应的单维度统计项bvar。
get_stats除了支持(默认)std::list<std::string>参数类型,也支持自定义参数类型,满足以下条件:
@@ -421,6 +422,12 @@ get_stats除了支持(默认)std::list<std::string>参数类型,也支持
推荐使用不需要分配内存的容器(例如,std::array、absl::InlinedVector)和不需要拷贝字符串的数据结构(例如,const
char*、std::string_view、butil::StringPieces),可以提高性能。
+bvar::MultiDimension的模板参数Shared,默认为false:
+1. 如果Shared等于false,get_stats返回模板参数T的指针,例如bvar::Adder<int>*。
+2.
如果Shared等于true,get_stats返回模板参数T的shared_ptr,例如std::shared_ptr\<bvar::Adder<int>\>。
+
+**注意**:因为shared_ptr的开销,Shared等于true的性能会比Shared等于false的性能差一些。
+
```c++
#include <bvar/bvar.h>
#include <bvar/multi_dimension.h>
@@ -523,6 +530,20 @@ int request_count = get_request_count(request_label_list);
* store(bvar)
* return bvar
+### delete_stats
+
+根据指定label删除对应的单维度统计项bvar。
+
+bvar::MultiDimension的模板参数Shared,默认为false:
+1. 如果Shared等于false,get_stats返回的是模板参数T的指针,delete_stats无法保证没有使用者,所以无法安全删除bvar。
+2. 如果Shared等于true,get_stats返回的是模板参数T的shared_ptr,delete_stats可以安全删除bvar。
+
+### clear_stats
+
+清理所有单维度统计项bvar。
+
+安全性同样受bvar::MultiDimension的模板参数Shared的影响,见delete_stats一节说明。
+
**bvar的生命周期**
label对应的单维度统计项bvar存储在多维度统计项(mbvar)中,当mbvar析构的时候会释放自身所有bvar,所以用户必须保证在mbvar的生命周期之内操作bvar,在mbvar生命周期外访问bvar的行为未定义,极有可能出core。
diff --git a/src/bvar/detail/combiner.h b/src/bvar/detail/combiner.h
index 3b442180..cae1b8ea 100644
--- a/src/bvar/detail/combiner.h
+++ b/src/bvar/detail/combiner.h
@@ -205,12 +205,10 @@ friend class GlobalValue<self_type>;
//
// NOTE: Only available to non-atomic types.
template <typename Op>
- void merge_global(const Op &op, self_shared_type c = NULL) {
- if (NULL == c) {
- c = combiner.lock();
- }
- if (NULL != c) {
- GlobalValue<self_type> g(this, c.get());
+ void merge_global(const Op &op, self_shared_type& c) {
+ const self_shared_type& c_ref = NULL != c ? c : combiner.lock();
+ if (NULL != c_ref) {
+ GlobalValue<self_type> g(this, c_ref.get());
element.merge_global(op, g);
}
}
diff --git a/src/bvar/multi_dimension.h b/src/bvar/multi_dimension.h
index 9163e346..ad352a02 100644
--- a/src/bvar/multi_dimension.h
+++ b/src/bvar/multi_dimension.h
@@ -20,6 +20,8 @@
#ifndef BVAR_MULTI_DIMENSION_H
#define BVAR_MULTI_DIMENSION_H
+#include <memory>
+#include <type_traits>
#include "butil/logging.h" // LOG
#include "butil/macros.h" // BAIDU_CASSERT
#include "butil/scoped_lock.h" // BAIDU_SCOPE_LOCK
@@ -35,8 +37,15 @@ namespace bvar {
// 2. KeyType::value_type must be std::string.
// 3. KeyType::size() returns the number of labels.
// 4. KeyType::push_back() adds a label to the end of the container.
-template <typename T, typename KeyType = std::list<std::string>>
+//
+// If `Shared' is false, `get_stats' returns a raw pointer,
+// `delete_stats' and `clear_stats' are not thread safe.
+// If `Shared' is true, `get_stats` returns a shared_ptr,
+// `delete_stats' and `clear_stats' are thread safe.
+// Note: The shared mode may be less performant than the non-shared mode.
+template <typename T, typename KeyType = std::list<std::string>, bool Shared =
false>
class MultiDimension : public MVariable<KeyType> {
+ typedef std::shared_ptr<T> shared_value_type;
public:
enum STATS_OP {
READ_ONLY,
@@ -45,7 +54,7 @@ public:
typedef KeyType key_type;
typedef T value_type;
- typedef T* value_ptr_type;
+ typedef typename std::conditional<Shared, shared_value_type, T*>::type
value_ptr_type;
typedef MVariable<key_type> Base;
struct KeyHash {
@@ -102,11 +111,15 @@ public:
// 2. K::value_type must be able to convert to std::string and
butil::StringPiece
// through operator std::string() function and operator
butil::StringPiece() function.
// 3. K::value_type must be able to compare with std::string.
+ //
+ // Returns a shared_ptr if `Shared' is true, otherwise returns a raw
pointer.
template <typename K = key_type>
- T* get_stats(const K& labels_value) {
+ value_ptr_type get_stats(const K& labels_value) {
return get_stats_impl(labels_value, READ_OR_INSERT);
}
+ // `delete_stats' and `clear_stats' are thread safe
+ // if `Shared' is true, otherwise not.
// Remove stat so those not count and dump
template <typename K = key_type>
void delete_stats(const K& labels_value);
@@ -133,7 +146,7 @@ public:
// Return real bvar pointer if labels_name exist, NULL otherwise.
// CAUTION!!! Just For Debug!!!
template <typename K = key_type>
- T* get_stats_read_only(const K& labels_value) {
+ value_ptr_type get_stats_read_only(const K& labels_value) {
return get_stats_impl(labels_value);
}
@@ -141,17 +154,18 @@ public:
// Return real bvar pointer if labels_name exist, otherwise(not exist)
create bvar pointer.
// CAUTION!!! Just For Debug!!!
template <typename K = key_type>
- T* get_stats_read_or_insert(const K& labels_value, bool* do_write = NULL) {
+ value_ptr_type get_stats_read_or_insert(const K& labels_value, bool*
do_write = NULL) {
return get_stats_impl(labels_value, READ_OR_INSERT, do_write);
}
#endif
private:
template <typename K>
- T* get_stats_impl(const K& labels_value);
+ value_ptr_type get_stats_impl(const K& labels_value);
template <typename K>
- T* get_stats_impl(const K& labels_value, STATS_OP stats_op, bool* do_write
= NULL);
+ value_ptr_type get_stats_impl(
+ const K& labels_value, STATS_OP stats_op, bool* do_write = NULL);
template <typename K>
static typename std::enable_if<butil::is_same<K, key_type>::value>::type
@@ -162,11 +176,8 @@ private:
template <typename K>
static typename std::enable_if<!butil::is_same<K, key_type>::value>::type
insert_metrics_map(MetricMap& bg, const K& labels_value, op_value_type
metric) {
- key_type labels_value_str;
- for (auto& label : labels_value) {
- // key_type::value_type must be able to convert to std::string.
- labels_value_str.push_back(std::string(label));
- }
+ // key_type::value_type must be able to convert to std::string.
+ key_type labels_value_str(labels_value.cbegin(), labels_value.cend());
bg.insert(labels_value_str, metric);
}
@@ -192,7 +203,27 @@ private:
void delete_stats();
static size_t init_flatmap(MetricMap& bg);
-
+
+ // If Shared is true, return std::shared_ptr, otherwise return raw pointer.
+ template <bool S = Shared>
+ typename std::enable_if<S, value_ptr_type>::type new_value() {
+ return std::make_shared<value_type>();
+ }
+ template <bool S = Shared>
+ typename std::enable_if<!S, value_ptr_type>::type new_value() {
+ return new value_type();
+ }
+
+ // If Shared is true, reset std::shared_ptr, otherwise delete raw pointer.
+ template <bool S = Shared>
+ typename std::enable_if<S>::type delete_value(value_ptr_type& v) {
+ v.reset();
+ }
+ template <bool S = Shared>
+ typename std::enable_if<!S>::type delete_value(value_ptr_type& v) {
+ delete v;
+ }
+
size_t _max_stats_count;
MetricMapDBD _metric_map;
};
diff --git a/src/bvar/multi_dimension_inl.h b/src/bvar/multi_dimension_inl.h
index 15378248..c407567c 100644
--- a/src/bvar/multi_dimension_inl.h
+++ b/src/bvar/multi_dimension_inl.h
@@ -21,6 +21,7 @@
#define BVAR_MULTI_DIMENSION_INL_H
#include <gflags/gflags_declare.h>
+#include "butil/compiler_specific.h"
namespace bvar {
@@ -34,43 +35,43 @@ static const std::string ALLOW_UNUSED METRIC_TYPE_SUMMARY =
"summary";
static const std::string ALLOW_UNUSED METRIC_TYPE_HISTOGRAM = "histogram";
static const std::string ALLOW_UNUSED METRIC_TYPE_GAUGE = "gauge";
-template <typename T, typename KeyType>
-MultiDimension<T, KeyType>::MultiDimension(const key_type& labels)
+template <typename T, typename KeyType, bool Shared>
+MultiDimension<T, KeyType, Shared>::MultiDimension(const key_type& labels)
: Base(labels)
, _max_stats_count(FLAGS_max_multi_dimension_stats_count) {
_metric_map.Modify(init_flatmap);
}
-template <typename T, typename KeyType>
-MultiDimension<T, KeyType>::MultiDimension(const butil::StringPiece& name,
+template <typename T, typename KeyType, bool Shared>
+MultiDimension<T, KeyType, Shared>::MultiDimension(const butil::StringPiece&
name,
const key_type& labels)
: MultiDimension(labels) {
this->expose(name);
}
-template <typename T, typename KeyType>
-MultiDimension<T, KeyType>::MultiDimension(const butil::StringPiece& prefix,
+template <typename T, typename KeyType, bool Shared>
+MultiDimension<T, KeyType, Shared>::MultiDimension(const butil::StringPiece&
prefix,
const butil::StringPiece& name,
const key_type& labels)
: MultiDimension(labels) {
this->expose_as(prefix, name);
}
-template <typename T, typename KeyType>
-MultiDimension<T, KeyType>::~MultiDimension() {
+template <typename T, typename KeyType, bool Shared>
+MultiDimension<T, KeyType, Shared>::~MultiDimension() {
this->hide();
delete_stats();
}
-template <typename T, typename KeyType>
-size_t MultiDimension<T, KeyType>::init_flatmap(MetricMap& bg) {
+template <typename T, typename KeyType, bool Shared>
+size_t MultiDimension<T, KeyType, Shared>::init_flatmap(MetricMap& bg) {
// size = 1 << 13
CHECK_EQ(0, bg.init(8192, 80));
- return (size_t)1;
+ return 1;
}
-template <typename T, typename KeyType>
-size_t MultiDimension<T, KeyType>::count_stats() {
+template <typename T, typename KeyType, bool Shared>
+size_t MultiDimension<T, KeyType, Shared>::count_stats() {
MetricMapScopedPtr metric_map_ptr;
if (_metric_map.Read(&metric_map_ptr) != 0) {
LOG(ERROR) << "Fail to read dbd";
@@ -79,9 +80,9 @@ size_t MultiDimension<T, KeyType>::count_stats() {
return metric_map_ptr->size();
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename K>
-void MultiDimension<T, KeyType>::delete_stats(const K& labels_value) {
+void MultiDimension<T, KeyType, Shared>::delete_stats(const K& labels_value) {
if (is_valid_lables_value(labels_value)) {
// Because there are two copies(foreground and background) in DBD,
// we need to use an empty tmp_metric, get the deleted value of
@@ -93,35 +94,35 @@ void MultiDimension<T, KeyType>::delete_stats(const K&
labels_value) {
};
_metric_map.Modify(erase_fn);
if (tmp_metric) {
- delete tmp_metric;
+ delete_value(tmp_metric);
}
}
}
-template <typename T, typename KeyType>
-void MultiDimension<T, KeyType>::delete_stats() {
+template <typename T, typename KeyType, bool Shared>
+void MultiDimension<T, KeyType, Shared>::delete_stats() {
// Because there are two copies(foreground and background) in DBD, we need
to use an empty tmp_map,
// swap two copies with empty, and get the value of second copy into
tmp_map,
// then traversal tmp_map and delete bvar object,
// which can prevent the bvar object from being deleted twice.
MetricMap tmp_map;
CHECK_EQ(0, tmp_map.init(8192, 80));
- auto clear_fn = [&tmp_map](MetricMap& map) {
+ auto clear_fn = [&tmp_map](MetricMap& map) -> size_t {
if (!tmp_map.empty()) {
tmp_map.clear();
}
tmp_map.swap(map);
- return (size_t)1;
+ return 1;
};
int ret = _metric_map.Modify(clear_fn);
CHECK_EQ(1, ret);
- for (auto &kv : tmp_map) {
- delete kv.second;
+ for (auto& kv : tmp_map) {
+ delete_value(kv.second);
}
}
-template <typename T, typename KeyType>
-void MultiDimension<T, KeyType>::list_stats(std::vector<key_type>* names) {
+template <typename T, typename KeyType, bool Shared>
+void MultiDimension<T, KeyType, Shared>::list_stats(std::vector<key_type>*
names) {
if (names == NULL) {
return;
}
@@ -137,58 +138,60 @@ void MultiDimension<T,
KeyType>::list_stats(std::vector<key_type>* names) {
}
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename K>
-T* MultiDimension<T, KeyType>::get_stats_impl(const K& labels_value) {
+typename MultiDimension<T, KeyType, Shared>::value_ptr_type
+MultiDimension<T, KeyType, Shared>::get_stats_impl(const K& labels_value) {
if (!is_valid_lables_value(labels_value)) {
- return nullptr;
+ return NULL;
}
MetricMapScopedPtr metric_map_ptr;
if (_metric_map.Read(&metric_map_ptr) != 0) {
LOG(ERROR) << "Fail to read dbd";
- return nullptr;
+ return NULL;
}
auto it = metric_map_ptr->seek(labels_value);
- if (it == nullptr) {
- return nullptr;
+ if (NULL == it) {
+ return NULL;
}
return (*it);
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename K>
-T* MultiDimension<T, KeyType>::get_stats_impl(
+typename MultiDimension<T, KeyType, Shared>::value_ptr_type
+MultiDimension<T, KeyType, Shared>::get_stats_impl(
const K& labels_value, STATS_OP stats_op, bool* do_write) {
if (!is_valid_lables_value(labels_value)) {
- return nullptr;
+ return NULL;
}
{
MetricMapScopedPtr metric_map_ptr;
if (0 != _metric_map.Read(&metric_map_ptr)) {
LOG(ERROR) << "Fail to read dbd";
- return nullptr;
+ return NULL;
}
auto it = metric_map_ptr->seek(labels_value);
if (NULL != it) {
return (*it);
} else if (READ_ONLY == stats_op) {
- return nullptr;
+ return NULL;
}
if (metric_map_ptr->size() > _max_stats_count) {
LOG(ERROR) << "Too many stats seen, overflow detected, max stats
count="
<< _max_stats_count;
- return nullptr;
+ return NULL;
}
}
- // Because DBD has two copies(foreground and background) MetricMap, both
copies need to be modify,
+ // Because DBD has two copies(foreground and background) MetricMap, both
copies need to be modified,
// In order to avoid new duplicate bvar object, need use cache_metric to
cache the new bvar object,
// In this way, when modifying the second copy, can directly use the
cache_metric bvar object.
op_value_type cache_metric = NULL;
- auto insert_fn = [&labels_value, &cache_metric, &do_write](MetricMap& bg) {
+ auto insert_fn = [this, &labels_value, &cache_metric,
&do_write](MetricMap& bg) {
auto bg_metric = bg.seek(labels_value);
if (NULL != bg_metric) {
cache_metric = *bg_metric;
@@ -199,7 +202,7 @@ T* MultiDimension<T, KeyType>::get_stats_impl(
}
if (NULL == cache_metric) {
- cache_metric = new T();
+ cache_metric = new_value();
}
insert_metrics_map(bg, labels_value, cache_metric);
return 1;
@@ -208,21 +211,21 @@ T* MultiDimension<T, KeyType>::get_stats_impl(
return cache_metric;
}
-template <typename T, typename KeyType>
-void MultiDimension<T, KeyType>::clear_stats() {
+template <typename T, typename KeyType, bool Shared>
+void MultiDimension<T, KeyType, Shared>::clear_stats() {
delete_stats();
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename K>
-bool MultiDimension<T, KeyType>::has_stats(const K& labels_value) {
- return get_stats_impl(labels_value) != nullptr;
+bool MultiDimension<T, KeyType, Shared>::has_stats(const K& labels_value) {
+ return get_stats_impl(labels_value) != NULL;
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename U>
typename std::enable_if<!butil::is_same<LatencyRecorder, U>::value,
size_t>::type
-MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const DumpOptions*
options) {
+MultiDimension<T, KeyType, Shared>::dump_impl(Dumper* dumper, const
DumpOptions* options) {
std::vector<key_type> label_names;
list_stats(&label_names);
if (label_names.empty() || !dumper->dump_comment(this->name(),
METRIC_TYPE_GAUGE)) {
@@ -230,8 +233,8 @@ MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const
DumpOptions* options
}
size_t n = 0;
for (auto &label_name : label_names) {
- T* bvar = get_stats_impl(label_name);
- if (!bvar) {
+ value_ptr_type bvar = get_stats_impl(label_name);
+ if (NULL == bvar) {
continue;
}
std::ostringstream oss;
@@ -246,10 +249,10 @@ MultiDimension<T, KeyType>::dump_impl(Dumper* dumper,
const DumpOptions* options
return n;
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename U>
typename std::enable_if<butil::is_same<LatencyRecorder, U>::value,
size_t>::type
-MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const DumpOptions*) {
+MultiDimension<T, KeyType, Shared>::dump_impl(Dumper* dumper, const
DumpOptions*) {
std::vector<key_type> label_names;
list_stats(&label_names);
if (label_names.empty()) {
@@ -299,8 +302,8 @@ MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const
DumpOptions*) {
// max_latency comment
dumper->dump_comment(this->name() + "_max_latency", METRIC_TYPE_GAUGE);
for (auto &label_name : label_names) {
- bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
- if (!bvar) {
+ LatencyRecorder* bvar = get_stats_impl(label_name);
+ if (NULL == bvar) {
continue;
}
std::ostringstream oss_max_latency_key;
@@ -313,8 +316,8 @@ MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const
DumpOptions*) {
// qps comment
dumper->dump_comment(this->name() + "_qps", METRIC_TYPE_GAUGE);
for (auto &label_name : label_names) {
- bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
- if (!bvar) {
+ LatencyRecorder* bvar = get_stats_impl(label_name);
+ if (NULL == bvar) {
continue;
}
std::ostringstream oss_qps_key;
@@ -327,8 +330,8 @@ MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const
DumpOptions*) {
// count comment
dumper->dump_comment(this->name() + "_count", METRIC_TYPE_COUNTER);
for (auto &label_name : label_names) {
- bvar::LatencyRecorder* bvar = get_stats_impl(label_name);
- if (!bvar) {
+ LatencyRecorder* bvar = get_stats_impl(label_name);
+ if (NULL == bvar) {
continue;
}
std::ostringstream oss_count_key;
@@ -340,8 +343,8 @@ MultiDimension<T, KeyType>::dump_impl(Dumper* dumper, const
DumpOptions*) {
return n;
}
-template <typename T, typename KeyType>
-void MultiDimension<T, KeyType>::make_dump_key(std::ostream& os, const
key_type& labels_value,
+template <typename T, typename KeyType, bool Shared>
+void MultiDimension<T, KeyType, Shared>::make_dump_key(std::ostream& os, const
key_type& labels_value,
const std::string& suffix, int
quantile) {
os << this->name();
if (!suffix.empty()) {
@@ -350,8 +353,8 @@ void MultiDimension<T,
KeyType>::make_dump_key(std::ostream& os, const key_type&
make_labels_kvpair_string(os, labels_value, quantile);
}
-template <typename T, typename KeyType>
-void MultiDimension<T, KeyType>::make_labels_kvpair_string(
+template <typename T, typename KeyType, bool Shared>
+void MultiDimension<T, KeyType, Shared>::make_labels_kvpair_string(
std::ostream& os, const key_type& labels_value, int quantile) {
os << "{";
auto label_key = this->_labels.cbegin();
@@ -368,9 +371,9 @@ void MultiDimension<T, KeyType>::make_labels_kvpair_string(
os << "}";
}
-template <typename T, typename KeyType>
+template <typename T, typename KeyType, bool Shared>
template <typename K>
-bool MultiDimension<T, KeyType>::is_valid_lables_value(const K& labels_value)
const {
+bool MultiDimension<T, KeyType, Shared>::is_valid_lables_value(const K&
labels_value) const {
if (this->count_labels() != labels_value.size()) {
LOG(ERROR) << "Invalid labels count" << this->count_labels()
<< " != " << labels_value.size();
@@ -379,8 +382,8 @@ bool MultiDimension<T,
KeyType>::is_valid_lables_value(const K& labels_value) co
return true;
}
-template <typename T, typename KeyType>
-void MultiDimension<T, KeyType>::describe(std::ostream& os) {
+template <typename T, typename KeyType, bool Shared>
+void MultiDimension<T, KeyType, Shared>::describe(std::ostream& os) {
os << "{\"name\" : \"" << this->name() << "\", \"labels\" : [";
char comma[3] = {'\0', ' ', '\0'};
for (auto& label : this->_labels) {
diff --git a/test/bvar_multi_dimension_unittest.cpp
b/test/bvar_multi_dimension_unittest.cpp
index 8d0e479d..a04ab4b7 100644
--- a/test/bvar_multi_dimension_unittest.cpp
+++ b/test/bvar_multi_dimension_unittest.cpp
@@ -21,7 +21,7 @@
#include <cstddef>
#include <memory>
#include <iostream>
-#include <set>
+#include <array>
#include <string>
#include <gflags/gflags.h>
#include <gtest/gtest.h>
@@ -497,3 +497,169 @@ TEST_F(MultiDimensionTest, test_hash) {
LOG(INFO) << "Hash fun performance:\n" << oss.str();
}
+
+class MyStringView {
+public:
+ MyStringView() : _ptr(NULL), _len(0) {}
+ MyStringView(const char* str)
+ : _ptr(str),
+ _len(str == NULL ? 0 : strlen(str)) {}
+#if __cplusplus >= 201703L
+ MyStringView(const std::string_view& str)
+ : _ptr(str.data()), _len(str.size()) {}
+#endif // __cplusplus >= 201703L
+ MyStringView(const std::string& str)
+ : _ptr(str.data()), _len(str.size()) {}
+ MyStringView(const char* offset, size_t len)
+ : _ptr(offset), _len(len) {}
+
+ const char* data() const { return _ptr; }
+ size_t size() const { return _len; }
+
+ // Converts to `std::basic_string`.
+ explicit operator std::string() const {
+ if (NULL == _ptr) {
+ return {};
+ }
+ return {_ptr, size()};
+ }
+
+ // Converts to butil::StringPiece.
+ explicit operator butil::StringPiece() const {
+ if (NULL == _ptr) {
+ return {};
+ }
+ return {_ptr, size()};
+ }
+
+private:
+ const char* _ptr;
+ size_t _len;
+};
+
+bool operator==(const MyStringView& x, const std::string& y) {
+ if (x.size() != y.size()) {
+ return false;
+ }
+
+ return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+bool operator==(const std::string& x, const MyStringView& y) {
+ if (x.size() != y.size()) {
+ return false;
+ }
+
+ return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
+}
+
+static int g_exposed_count = 0;
+
+template <typename KeyType, typename ValueType>
+static void TestLabels() {
+ std::string mbvar_name = butil::string_printf("my_madder_%d",
g_exposed_count);
+ KeyType labels{"idc", "method", "status"};
+ bvar::MultiDimension<bvar::Adder<int>, KeyType> my_madder(mbvar_name,
labels);
+ ASSERT_EQ(labels.size(), my_madder.count_labels());
+ ASSERT_STREQ(mbvar_name.c_str(), my_madder.name().c_str());
+ ASSERT_EQ(labels, my_madder.labels());
+
+ using ItemType = typename ValueType::value_type;
+ ValueType labels_value{ItemType("cv"), ItemType("post"), ItemType("200")};
+ bvar::Adder<int>* adder = my_madder.get_stats(labels_value);
+ ASSERT_NE(nullptr, adder);
+ ASSERT_TRUE(my_madder.has_stats(labels_value));
+ ASSERT_EQ((size_t)1, my_madder.count_stats());
+ {
+ // Compatible with old API.
+ bvar::Adder<int>* temp = my_madder.get_stats({"cv", "post", "200"});
+ ASSERT_EQ(adder, temp);
+ }
+ *adder << g_exposed_count;
+ ASSERT_EQ(g_exposed_count, adder->get_value());
+ my_madder.delete_stats(labels_value);
+ ASSERT_FALSE(my_madder.has_stats(labels_value));
+ ASSERT_EQ((size_t)0, my_madder.count_stats());
+}
+
+TEST_F(MultiDimensionTest, labels) {
+ TestLabels<std::list<std::string>, std::list<std::string>>();
+ TestLabels<std::list<std::string>, std::vector<std::string>>();
+ TestLabels<std::list<std::string>, std::array<std::string, 3>>();
+
+ TestLabels<std::vector<std::string>, std::list<std::string>>();
+ TestLabels<std::vector<std::string>, std::vector<std::string>>();
+ TestLabels<std::vector<std::string>, std::array<std::string, 3>>();
+
+#if __cplusplus >= 201703L
+ TestLabels<std::list<std::string>, std::list<std::string_view>>();
+ TestLabels<std::list<std::string>, std::vector<std::string_view>>();
+ TestLabels<std::list<std::string>, std::array<std::string_view, 3>>();
+#endif // __cplusplus >= 201703L
+
+ TestLabels<std::vector<std::string>, std::list<butil::StringPiece>>();
+ TestLabels<std::vector<std::string>, std::vector<butil::StringPiece>>();
+ TestLabels<std::vector<std::string>, std::array<butil::StringPiece, 3>>();
+
+ TestLabels<std::list<std::string>, std::list<MyStringView>>();
+ TestLabels<std::list<std::string>, std::vector<MyStringView>>();
+ TestLabels<std::list<std::string>, std::array<MyStringView, 3>>();
+
+ TestLabels<std::vector<std::string>, std::list<MyStringView>>();
+ TestLabels<std::vector<std::string>, std::vector<MyStringView>>();
+ TestLabels<std::vector<std::string>, std::array<MyStringView, 3>>();
+}
+
+std::array<const char*, 3> g_labels_value{"idc", "post", "200"};
+bool g_shared_stop = false;
+
+void* get_shared_adder_thread(void* arg) {
+ auto my_madder =
+ (bvar::MultiDimension<bvar::Adder<int>, std::vector<std::string>,
true>*)arg;
+ while (!g_shared_stop) {
+ auto adder = my_madder->get_stats(g_labels_value);
+ EXPECT_NE(nullptr, adder);
+ *adder << 1;
+ }
+ return NULL;
+}
+
+void* delete_shared_adder_thread(void* arg) {
+ auto my_madder =
+ (bvar::MultiDimension<bvar::Adder<int>, std::vector<std::string>,
true>*)arg;
+ while (!g_shared_stop) {
+ my_madder->delete_stats(g_labels_value);
+ }
+ return NULL;
+}
+
+TEST_F(MultiDimensionTest, shared) {
+ bvar::MultiDimension<bvar::Adder<int>, std::vector<std::string>, true>
my_madder(
+ "my_adder", {"idc", "method", "status"});
+ std::shared_ptr<bvar::Adder<int>> adder =
my_madder.get_stats(g_labels_value);
+ ASSERT_NE(nullptr, adder);
+ ASSERT_TRUE(my_madder.has_stats(g_labels_value));
+ ASSERT_EQ((size_t)1, my_madder.count_stats());
+
+ *adder << 1;
+ ASSERT_EQ(1, adder->get_value());
+ my_madder.delete_stats(g_labels_value);
+ *adder << 1;
+ ASSERT_EQ(2, adder->get_value());
+ ASSERT_FALSE(my_madder.has_stats(g_labels_value));
+
+ const int get_num = 8;
+ std::vector<pthread_t> get_threads(get_num);
+ for (int i = 0; i < get_num; ++i) {
+ ASSERT_EQ(0, pthread_create(&get_threads[i], NULL,
get_shared_adder_thread, &my_madder));
+ }
+ pthread_t delete_thread;
+ ASSERT_EQ(0, pthread_create(&delete_thread, NULL,
delete_shared_adder_thread, &my_madder));
+ usleep(100 * 1000); // 100ms
+ g_shared_stop = true;
+ for (int i = 0; i < get_num; ++i) {
+ ASSERT_EQ(0, pthread_join(get_threads[i], NULL));
+ }
+ ASSERT_EQ(0, pthread_join(delete_thread, NULL));
+}
+
diff --git a/test/bvar_mvariable_unittest.cpp b/test/bvar_mvariable_unittest.cpp
index d72a2e5d..aca50d5f 100644
--- a/test/bvar_mvariable_unittest.cpp
+++ b/test/bvar_mvariable_unittest.cpp
@@ -117,118 +117,6 @@ TEST_F(MVariableTest, expose) {
ASSERT_EQ(2, exposed_vars.size());
}
-class MyStringView {
-public:
- MyStringView() : _ptr(NULL), _len(0) {}
- MyStringView(const char* str)
- : _ptr(str),
- _len(str == NULL ? 0 : strlen(str)) {}
-#if __cplusplus >= 201703L
- MyStringView(const std::string_view& str)
- : _ptr(str.data()), _len(str.size()) {}
-#endif // __cplusplus >= 201703L
- MyStringView(const std::string& str)
- : _ptr(str.data()), _len(str.size()) {}
- MyStringView(const char* offset, size_t len)
- : _ptr(offset), _len(len) {}
-
- const char* data() const { return _ptr; }
- size_t size() const { return _len; }
-
- // Converts to `std::basic_string`.
- explicit operator std::string() const {
- if (NULL == _ptr) {
- return {};
- }
- return {_ptr, size()};
- }
-
- // Converts to butil::StringPiece.
- explicit operator butil::StringPiece() const {
- if (NULL == _ptr) {
- return {};
- }
- return {_ptr, size()};
- }
-
-private:
- const char* _ptr;
- size_t _len;
-};
-
-bool operator==(const MyStringView& x, const std::string& y) {
- if (x.size() != y.size()) {
- return false;
- }
-
- return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
-}
-
-bool operator==(const std::string& x, const MyStringView& y) {
- if (x.size() != y.size()) {
- return false;
- }
-
- return butil::StringPiece::wordmemcmp(x.data(), y.data(), x.size()) == 0;
-}
-
-static int g_exposed_count = 0;
-
-template <typename KeyType, typename ValueType>
-static void TestLabels() {
- std::string mbvar_name = butil::string_printf("my_madder_%d",
g_exposed_count);
- KeyType labels{"idc", "method", "status"};
- bvar::MultiDimension<bvar::Adder<int>, KeyType> my_madder(mbvar_name,
labels);
- ASSERT_EQ(labels.size(), my_madder.count_labels());
- ASSERT_STREQ(mbvar_name.c_str(), my_madder.name().c_str());
- ASSERT_EQ(labels, my_madder.labels());
-
- using ItemType = typename ValueType::value_type;
- ValueType labels_value{ItemType("cv"), ItemType("post"), ItemType("200")};
- bvar::Adder<int>* adder = my_madder.get_stats(labels_value);
- ASSERT_NE(nullptr, adder);
- ASSERT_TRUE(my_madder.has_stats(labels_value));
- ASSERT_EQ((size_t)1, my_madder.count_stats());
- {
- // Compatible with old API.
- bvar::Adder<int>* temp = my_madder.get_stats({"cv", "post", "200"});
- ASSERT_EQ(adder, temp);
- }
- *adder << g_exposed_count;
- ASSERT_EQ(g_exposed_count, adder->get_value());
- my_madder.delete_stats(labels_value);
- ASSERT_FALSE(my_madder.has_stats(labels_value));
- ASSERT_EQ((size_t)0, my_madder.count_stats());
-}
-
-TEST_F(MVariableTest, labels) {
- TestLabels<std::list<std::string>, std::list<std::string>>();
- TestLabels<std::list<std::string>, std::vector<std::string>>();
- TestLabels<std::list<std::string>, std::array<std::string, 3>>();
-
- TestLabels<std::vector<std::string>, std::list<std::string>>();
- TestLabels<std::vector<std::string>, std::vector<std::string>>();
- TestLabels<std::vector<std::string>, std::array<std::string, 3>>();
-
-#if __cplusplus >= 201703L
- TestLabels<std::list<std::string>, std::list<std::string_view>>();
- TestLabels<std::list<std::string>, std::vector<std::string_view>>();
- TestLabels<std::list<std::string>, std::array<std::string_view, 3>>();
-#endif // __cplusplus >= 201703L
-
- TestLabels<std::vector<std::string>, std::list<butil::StringPiece>>();
- TestLabels<std::vector<std::string>, std::vector<butil::StringPiece>>();
- TestLabels<std::vector<std::string>, std::array<butil::StringPiece, 3>>();
-
- TestLabels<std::list<std::string>, std::list<MyStringView>>();
- TestLabels<std::list<std::string>, std::vector<MyStringView>>();
- TestLabels<std::list<std::string>, std::array<MyStringView, 3>>();
-
- TestLabels<std::vector<std::string>, std::list<MyStringView>>();
- TestLabels<std::vector<std::string>, std::vector<MyStringView>>();
- TestLabels<std::vector<std::string>, std::array<MyStringView, 3>>();
-}
-
TEST_F(MVariableTest, dump) {
std::string old_bvar_dump_interval;
std::string old_mbvar_dump;
@@ -302,7 +190,6 @@ TEST_F(MVariableTest, dump) {
}
TEST_F(MVariableTest, test_describe_exposed) {
- std::list<std::string> labels_value1 {"bj", "get", "200"};
std::string bvar_name("request_count_describe");
bvar::MultiDimension<bvar::Adder<int> > my_madder1(bvar_name, labels);
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]