// 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 #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 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 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 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(); 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 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); } }