1/* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2/*
3 *     Copyright 2016 Couchbase, Inc
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *       http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18#include <condition_variable>
19#include <string>
20#include <thread>
21
22#include <benchmark/benchmark.h>
23#include <phosphor/category_registry.h>
24#include <utils/memory.h>
25
26#include "bench_common.h"
27
28using namespace phosphor;
29
30/*
31 * This benchmark continually makes new categories in a registry
32 * (roughly 1/<# threads> calls to getStatus will be a new category).
33 *
34 * It is primarily to trigger a TSan race but is useful as a general
35 * benchmark of status gathering.
36 */
37void NewCategories(benchmark::State& state) {
38    // Registry comes with 3 items already in it
39    static std::array<std::string, (CategoryRegistry::registry_size - 3)> categories;
40    static std::unique_ptr<CategoryRegistry> registry;
41
42    int generation = 0;
43    static int barrier_generation;
44    static int paused;
45    static std::mutex mutex;
46    static std::condition_variable cv;
47
48    if (state.thread_index == 0) {
49        size_t i = 1;
50        for (auto& category : categories) {
51            category = std::string("A", i++);
52        }
53        registry = utils::make_unique<CategoryRegistry>();
54        paused = 0;
55        barrier_generation = 0;
56}
57
58    while (state.KeepRunning()) {
59        for (const auto& category : categories) {
60            registry->getStatus(category.c_str());
61        }
62        state.PauseTiming();
63
64        // Wait for all the threads to sync up so we can reset
65        // the category registry.
66        {
67            std::unique_lock<std::mutex> lh(mutex);
68            ++paused;
69            if (paused == state.threads) {
70                registry = utils::make_unique<CategoryRegistry>();
71                paused = 0;
72                ++barrier_generation;
73                cv.notify_all();
74            } else {
75                cv.wait(lh, [&generation]() {
76                    // We use the barrier generation instead of the
77                    // number of paused threads to allow the barrier
78                    // to be reused.
79                    return barrier_generation == generation + 1;
80                });
81            }
82            ++generation;
83        }
84
85        state.ResumeTiming();
86    }
87}
88
89BENCHMARK(NewCategories)->ThreadRange(1, phosphor::benchNumThreads());
90
91BENCHMARK_MAIN()
92