blob: d2f75a5f509d92563e80733f3fb6cffd6874f6c0 [file] [log] [blame]
// Copyright 2024 The Cobalt Authors. All Rights Reserved.
//
// 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 <pthread.h>
#include "starboard/configuration_constants.h"
#include "starboard/nplb/posix_compliance/posix_thread_helpers.h"
#include "starboard/thread.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace starboard {
namespace nplb {
namespace {
int s_global_value;
void IncrementGlobalValue() {
++s_global_value;
}
TEST(PosixOnceTest, SunnyDaySingleInit) {
pthread_once_t once_control = PTHREAD_ONCE_INIT;
s_global_value = 0;
EXPECT_EQ(pthread_once(&once_control, &IncrementGlobalValue), 0);
EXPECT_EQ(s_global_value, 1);
}
TEST(PosixOnceTest, SunnyDayMultipleInit) {
pthread_once_t once_control = PTHREAD_ONCE_INIT;
s_global_value = 0;
EXPECT_EQ(pthread_once(&once_control, &IncrementGlobalValue), 0);
EXPECT_EQ(s_global_value, 1);
s_global_value = 0;
EXPECT_EQ(pthread_once(&once_control, &IncrementGlobalValue), 0);
EXPECT_EQ(s_global_value, 0);
s_global_value = 0;
EXPECT_EQ(pthread_once(&once_control, &IncrementGlobalValue), 0);
EXPECT_EQ(s_global_value, 0);
}
struct RunPosixOnceContext {
RunPosixOnceContext() : once_control(PTHREAD_ONCE_INIT) {
pthread_mutex_init(&mutex, NULL);
pthread_cond_init(&condition, NULL);
}
~RunPosixOnceContext() {
pthread_cond_destroy(&condition);
pthread_mutex_destroy(&mutex);
}
posix::TestSemaphore semaphore;
pthread_mutex_t mutex;
pthread_cond_t condition;
pthread_once_t once_control;
};
void* RunPosixOnceEntryPoint(void* context) {
RunPosixOnceContext* run_sbonce_context =
reinterpret_cast<RunPosixOnceContext*>(context);
{
pthread_mutex_lock(&run_sbonce_context->mutex);
run_sbonce_context->semaphore.Put();
pthread_cond_wait(&run_sbonce_context->condition,
&run_sbonce_context->mutex);
pthread_mutex_unlock(&run_sbonce_context->mutex);
}
SbThreadYield();
static const int kIterationCount = 3;
for (int i = 0; i < kIterationCount; ++i) {
pthread_once(&run_sbonce_context->once_control, &IncrementGlobalValue);
}
return NULL;
}
// Here we spawn many threads each of which will call pthread_once multiple
// times using a shared pthread_once_t object. We then test that the
// initialization routine got called exactly one time.
TEST(PosixOnceTest, SunnyDayMultipleThreadsInit) {
const int kMany = kSbMaxThreads;
std::vector<SbThread> threads(kMany);
const int kIterationCount = 10;
for (int i = 0; i < kIterationCount; ++i) {
pthread_once_t once_control = PTHREAD_ONCE_INIT;
RunPosixOnceContext context;
s_global_value = 0;
for (int j = 0; j < kMany; ++j) {
threads[j] =
SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true,
posix::kThreadName, RunPosixOnceEntryPoint, &context);
}
// Wait for all threads to finish initializing and become ready, then
// broadcast the signal to begin. We do this to increase the chances that
// threads will call pthread_once() at the same time as each other.
for (int j = 0; j < kMany; ++j) {
context.semaphore.Take();
}
{
pthread_mutex_lock(&context.mutex);
pthread_cond_broadcast(&context.condition);
pthread_mutex_unlock(&context.mutex);
}
// Signal threads to beginWait for all threads to complete.
for (int i = 0; i < kMany; ++i) {
void* result;
SbThreadJoin(threads[i], &result);
}
EXPECT_EQ(s_global_value, 1);
}
}
int* GetIntSingleton() {
static pthread_once_t s_once_flag = PTHREAD_ONCE_INIT;
static int* s_singleton = NULL;
struct Local {
static void Init() { s_singleton = new int(); }
};
pthread_once(&s_once_flag, Local::Init);
return s_singleton;
}
TEST(PosixOnceTest, InitializeOnceMacroFunction) {
int* int_singelton = GetIntSingleton();
ASSERT_TRUE(int_singelton);
EXPECT_EQ(*int_singelton, 0)
<< "Singleton Macro does not default initialize.";
}
} // namespace.
} // namespace nplb.
} // namespace starboard.