blob: 618798e8c4363439da88f2d94ead437758afc1da [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you 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 <gtest/gtest.h>
#include "bthread/bthread.h"
#include "bthread/singleton_on_bthread_once.h"
#include "bthread/task_control.h"
namespace bthread {
extern TaskControl* g_task_control;
}
namespace {
bthread_once_t g_bthread_once_control;
bool g_bthread_once_started = false;
butil::atomic<int> g_bthread_once_count(0);
void init_routine() {
bthread_usleep(2000 * 1000);
g_bthread_once_count.fetch_add(1, butil::memory_order_relaxed);
}
void bthread_once_task() {
bthread_once(&g_bthread_once_control, init_routine);
// `init_routine' only be called once.
ASSERT_EQ(1, g_bthread_once_count.load(butil::memory_order_relaxed));
}
void* first_bthread_once_task(void*) {
g_bthread_once_started = true;
bthread_once_task();
return NULL;
}
void* other_bthread_once_task(void*) {
bthread_once_task();
return NULL;
}
TEST(BthreadOnceTest, once) {
bthread_t bid;
ASSERT_EQ(0, bthread_start_background(
&bid, NULL, first_bthread_once_task, NULL));
while (!g_bthread_once_started) {
bthread_usleep(1000);
}
ASSERT_NE(nullptr, bthread::g_task_control);
int concurrency = bthread::g_task_control->concurrency();
LOG(INFO) << "concurrency: " << concurrency;
ASSERT_GT(concurrency, 0);
std::vector<bthread_t> bids(concurrency * 100);
for (auto& id : bids) {
ASSERT_EQ(0, bthread_start_background(
&id, NULL, other_bthread_once_task, NULL));
}
bthread_once_task();
for (auto& id : bids) {
bthread_join(id, NULL);
}
bthread_join(bid, NULL);
}
bool g_bthread_started = false;
butil::atomic<int> g_bthread_singleton_count(0);
class BthreadSingleton {
public:
BthreadSingleton() {
bthread_usleep(2000 * 1000);
g_bthread_singleton_count.fetch_add(1, butil::memory_order_relaxed);
}
};
void get_bthread_singleton() {
auto instance = bthread::get_leaky_singleton<BthreadSingleton>();
ASSERT_NE(nullptr, instance);
// Only one BthreadSingleton instance has been created.
ASSERT_EQ(1, g_bthread_singleton_count.load(butil::memory_order_relaxed));
}
void* first_get_bthread_singleton(void*) {
g_bthread_started = true;
get_bthread_singleton();
return NULL;
}
void* get_bthread_singleton(void*) {
get_bthread_singleton();
return NULL;
}
// Singleton will definitely not cause deadlock,
// even if constructor of T will hang the bthread.
TEST(BthreadOnceTest, singleton) {
bthread_t bid;
ASSERT_EQ(0, bthread_start_background(
&bid, NULL, first_get_bthread_singleton, NULL));
while (!g_bthread_started) {
bthread_usleep(1000);
}
ASSERT_NE(nullptr, bthread::g_task_control);
int concurrency = bthread::g_task_control->concurrency();
LOG(INFO) << "concurrency: " << concurrency;
ASSERT_GT(concurrency, 0);
std::vector<bthread_t> bids(concurrency * 100);
for (auto& id : bids) {
ASSERT_EQ(0, bthread_start_background(
&id, NULL, get_bthread_singleton, NULL));
}
get_bthread_singleton();
for (auto& id : bids) {
bthread_join(id, NULL);
}
bthread_join(bid, NULL);
}
}