| // Copyright 2015 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. |
| |
| // Broadcast is Sunny Day tested in most of the other SbConditionVariable tests. |
| |
| #include "starboard/nplb/thread_helpers.h" |
| #include "starboard/once.h" |
| #include "starboard/thread.h" |
| #include "starboard/thread_types.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| namespace starboard { |
| namespace nplb { |
| namespace { |
| |
| int s_global_value; |
| |
| void IncrementGlobalValue() { |
| ++s_global_value; |
| } |
| |
| TEST(SbOnceTest, SunnyDaySingleInit) { |
| SbOnceControl once_control = SB_ONCE_INITIALIZER; |
| |
| s_global_value = 0; |
| EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue)); |
| |
| EXPECT_EQ(1, s_global_value); |
| } |
| |
| TEST(SbOnceTest, SunnyDayMultipleInit) { |
| SbOnceControl once_control = SB_ONCE_INITIALIZER; |
| |
| s_global_value = 0; |
| EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue)); |
| EXPECT_EQ(1, s_global_value); |
| |
| s_global_value = 0; |
| EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue)); |
| EXPECT_EQ(0, s_global_value); |
| |
| s_global_value = 0; |
| EXPECT_TRUE(SbOnce(&once_control, &IncrementGlobalValue)); |
| EXPECT_EQ(0, s_global_value); |
| } |
| |
| struct RunSbOnceContext { |
| RunSbOnceContext() : once_control(SB_ONCE_INITIALIZER) { |
| SbMutexCreate(&mutex); |
| SbConditionVariableCreate(&condition, &mutex); |
| } |
| ~RunSbOnceContext() { |
| SbConditionVariableDestroy(&condition); |
| SbMutexDestroy(&mutex); |
| } |
| |
| Semaphore semaphore; |
| SbMutex mutex; |
| SbConditionVariable condition; |
| |
| SbOnceControl once_control; |
| }; |
| |
| void* RunSbOnceEntryPoint(void* context) { |
| RunSbOnceContext* run_sbonce_context = |
| reinterpret_cast<RunSbOnceContext*>(context); |
| |
| { |
| SbMutexAcquire(&run_sbonce_context->mutex); |
| run_sbonce_context->semaphore.Put(); |
| SbConditionVariableWait(&run_sbonce_context->condition, |
| &run_sbonce_context->mutex); |
| SbMutexRelease(&run_sbonce_context->mutex); |
| } |
| |
| SbThreadYield(); |
| static const int kIterationCount = 3; |
| for (int i = 0; i < kIterationCount; ++i) { |
| SbOnce(&run_sbonce_context->once_control, &IncrementGlobalValue); |
| } |
| |
| return NULL; |
| } |
| |
| // Here we spawn many threads each of which will call SbOnce multiple times |
| // using a shared SbOnceControl object. We then test that the initialization |
| // routine got called exactly one time. |
| TEST(SbOnceTest, SunnyDayMultipleThreadsInit) { |
| const int kMany = SB_MAX_THREADS; |
| SbThread threads[kMany]; |
| |
| const int kIterationCount = 10; |
| for (int i = 0; i < kIterationCount; ++i) { |
| SbOnceControl once_control = SB_ONCE_INITIALIZER; |
| RunSbOnceContext context; |
| |
| s_global_value = 0; |
| for (int j = 0; j < kMany; ++j) { |
| threads[j] = |
| SbThreadCreate(0, kSbThreadNoPriority, kSbThreadNoAffinity, true, |
| kThreadName, RunSbOnceEntryPoint, &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 SbOnce() at the same time as each other. |
| for (int j = 0; j < kMany; ++j) { |
| context.semaphore.Take(); |
| } |
| { |
| SbMutexAcquire(&context.mutex); |
| SbConditionVariableBroadcast(&context.condition); |
| SbMutexRelease(&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(1, s_global_value); |
| } |
| } |
| |
| TEST(SbOnceTest, RainyDayBadOnceControl) { |
| s_global_value = 0; |
| EXPECT_FALSE(SbOnce(NULL, &IncrementGlobalValue)); |
| |
| EXPECT_EQ(0, s_global_value); |
| } |
| |
| TEST(SbOnceTest, RainyDayBadInitRoutine) { |
| SbOnceControl once_control = SB_ONCE_INITIALIZER; |
| |
| s_global_value = 0; |
| EXPECT_FALSE(SbOnce(&once_control, NULL)); |
| |
| EXPECT_EQ(0, s_global_value); |
| } |
| |
| SB_ONCE_INITIALIZE_FUNCTION(int, GetIntSingleton); |
| TEST(SbOnceTest, InitializeOnceMacroFunction) { |
| int* int_singelton = GetIntSingleton(); |
| ASSERT_TRUE(int_singelton); |
| EXPECT_EQ(0, *int_singelton) |
| << "Singleton Macro does not default initialize."; |
| } |
| |
| } // namespace. |
| } // namespace nplb. |
| } // namespace starboard. |