diff --git a/util/mac/process_reader_test.cc b/util/mac/process_reader_test.cc index 4ea9ff08..795468f5 100644 --- a/util/mac/process_reader_test.cc +++ b/util/mac/process_reader_test.cc @@ -14,7 +14,6 @@ #include "util/mac/process_reader.h" -#include #include #include #include @@ -35,6 +34,7 @@ #include "util/mac/mach_o_image_reader.h" #include "util/mach/mach_extensions.h" #include "util/stdlib/pointer_container.h" +#include "util/synchronization/semaphore.h" #include "util/test/errors.h" #include "util/test/mac/dyld.h" #include "util/test/mac/mach_errors.h" @@ -170,8 +170,8 @@ class TestThreadPool { } } - for (const ThreadInfo* thread_info : thread_infos_) { - dispatch_semaphore_signal(thread_info->exit_semaphore); + for (ThreadInfo* thread_info : thread_infos_) { + thread_info->exit_semaphore.Signal(); } for (const ThreadInfo* thread_info : thread_infos_) { @@ -197,10 +197,8 @@ class TestThreadPool { ASSERT_EQ(0, rv); } - for (const ThreadInfo* thread_info : thread_infos_) { - long rv = dispatch_semaphore_wait(thread_info->ready_semaphore, - DISPATCH_TIME_FOREVER); - ASSERT_EQ(0, rv); + for (ThreadInfo* thread_info : thread_infos_) { + thread_info->ready_semaphore.Wait(); } // If present, suspend the thread at indices 1 through 3 the same number of @@ -238,15 +236,12 @@ class TestThreadPool { ThreadInfo() : pthread(NULL), stack_address(0), - ready_semaphore(dispatch_semaphore_create(0)), - exit_semaphore(dispatch_semaphore_create(0)), + ready_semaphore(0), + exit_semaphore(0), suspend_count(0) { } - ~ThreadInfo() { - dispatch_release(exit_semaphore); - dispatch_release(ready_semaphore); - } + ~ThreadInfo() {} // The thread’s ID, set at the time the thread is created. pthread_t pthread; @@ -259,12 +254,12 @@ class TestThreadPool { // setting up its ThreadInfo structure. The main thread waits on this // semaphore before using any data that the worker thread is responsible for // setting. - dispatch_semaphore_t ready_semaphore; + Semaphore ready_semaphore; // The worker thread waits on exit_semaphore to determine when it’s safe to // exit. The main thread signals exit_semaphore when it no longer needs the // worker thread. - dispatch_semaphore_t exit_semaphore; + Semaphore exit_semaphore; // The thread’s suspend count. int suspend_count; @@ -276,8 +271,8 @@ class TestThreadPool { thread_info->stack_address = reinterpret_cast(&thread_info); - dispatch_semaphore_signal(thread_info->ready_semaphore); - dispatch_semaphore_wait(thread_info->exit_semaphore, DISPATCH_TIME_FOREVER); + thread_info->ready_semaphore.Signal(); + thread_info->exit_semaphore.Wait(); // Check this here after everything’s known to be synchronized, otherwise // there’s a race between the parent thread storing this thread’s pthread_t diff --git a/util/mach/exception_ports_test.cc b/util/mach/exception_ports_test.cc index 8a459f40..a5424a58 100644 --- a/util/mach/exception_ports_test.cc +++ b/util/mach/exception_ports_test.cc @@ -14,7 +14,6 @@ #include "util/mach/exception_ports.h" -#include #include #include #include @@ -30,6 +29,7 @@ #include "util/mach/exc_server_variants.h" #include "util/mach/mach_extensions.h" #include "util/misc/scoped_forbid_return.h" +#include "util/synchronization/semaphore.h" #include "util/test/mac/mach_errors.h" #include "util/test/mac/mach_multiprocess.h" @@ -206,13 +206,10 @@ class TestExceptionPorts : public UniversalMachExcServer, explicit Child(TestExceptionPorts* test_exception_ports) : test_exception_ports_(test_exception_ports), thread_(), - init_semaphore_(dispatch_semaphore_create(0)), - crash_semaphore_(dispatch_semaphore_create(0)) {} + init_semaphore_(0), + crash_semaphore_(0) {} - ~Child() { - dispatch_release(crash_semaphore_); - dispatch_release(init_semaphore_); - } + ~Child() {} void Run() { ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask, @@ -239,9 +236,7 @@ class TestExceptionPorts : public UniversalMachExcServer, ASSERT_EQ(0, rv_int); // Wait for the new thread to be ready. - long rv_long = - dispatch_semaphore_wait(init_semaphore_, DISPATCH_TIME_FOREVER); - ASSERT_EQ(0, rv_long); + init_semaphore_.Wait(); // Tell the parent process that everything is set up. char c = '\0'; @@ -269,7 +264,7 @@ class TestExceptionPorts : public UniversalMachExcServer, } // Let the other thread know it’s safe to proceed. - dispatch_semaphore_signal(crash_semaphore_); + crash_semaphore_.Signal(); // If this thread is the one that crashes, do it. if (test_exception_ports_->who_crashes() == kMainThreadCrashes) { @@ -304,12 +299,10 @@ class TestExceptionPorts : public UniversalMachExcServer, } // Let the main thread know that this thread is ready. - dispatch_semaphore_signal(init_semaphore_); + init_semaphore_.Signal(); // Wait for the main thread to signal that it’s safe to proceed. - long rv = - dispatch_semaphore_wait(crash_semaphore_, DISPATCH_TIME_FOREVER); - CHECK_EQ(0, rv) << "dispatch_semaphore_wait"; + crash_semaphore_.Wait(); // Regardless of where ExceptionPorts::SetExceptionPort() ran, // ExceptionPorts::GetExceptionPorts() can always be tested in-process. @@ -344,11 +337,11 @@ class TestExceptionPorts : public UniversalMachExcServer, // The main thread waits on this for the other thread to start up and // perform its own initialization. - dispatch_semaphore_t init_semaphore_; + Semaphore init_semaphore_; // The child thread waits on this for the parent thread to indicate that the // child can test its exception ports and possibly crash, as appropriate. - dispatch_semaphore_t crash_semaphore_; + Semaphore crash_semaphore_; // Always zero. Crash() divides by this in order to trigger a crash. This is // structured as a static volatile int to ward off aggressive compiler diff --git a/util/synchronization/semaphore.cc b/util/synchronization/semaphore.cc new file mode 100644 index 00000000..d513fa0d --- /dev/null +++ b/util/synchronization/semaphore.cc @@ -0,0 +1,61 @@ +// Copyright 2014 The Crashpad 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 "util/synchronization/semaphore.h" + +#include "base/logging.h" +#include "base/posix/eintr_wrapper.h" + +namespace crashpad { + +#if defined(OS_MACOSX) + +Semaphore::Semaphore(int value) + : semaphore_(dispatch_semaphore_create(value)) { + CHECK(semaphore_) << "dispatch_semaphore_create"; +} + +Semaphore::~Semaphore() { + dispatch_release(semaphore_); +} + +void Semaphore::Wait() { + CHECK_EQ(dispatch_semaphore_wait(semaphore_, DISPATCH_TIME_FOREVER), 0); +} + +void Semaphore::Signal() { + dispatch_semaphore_signal(semaphore_); +} + +#else + +Semaphore::Semaphore(int value) { + PCHECK(sem_init(&semaphore_, 0, value) == 0) << "sem_init"; +} + +Semaphore::~Semaphore() { + PCHECK(sem_destroy(&semaphore_)) << "sem_destroy"; +} + +void Semaphore::Wait() { + PCHECK(HANDLE_EINTR(sem_wait(&semaphore_))) << "sem_wait"; +} + +void Semaphore::Signal() { + PCHECK(sem_post(&semaphore_)) << "sem_post"; +} + +#endif + +} // namespace crashpad diff --git a/util/synchronization/semaphore.h b/util/synchronization/semaphore.h new file mode 100644 index 00000000..4e36f8cc --- /dev/null +++ b/util/synchronization/semaphore.h @@ -0,0 +1,63 @@ +// Copyright 2014 The Crashpad 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. + +#ifndef CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_ +#define CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_ + +#include "build/build_config.h" + +#if defined(OS_MACOSX) +#include +#else +#include +#endif + +namespace crashpad { + +//! \brief An anonymous in-process counting sempahore. +class Semaphore { + public: + //! \brief Initializes the semaphore. + //! + //! \param[in] value The initial value of the semaphore. + //! + //! If the semaphore cannot be created, execution is terminated. + explicit Semaphore(int value); + + ~Semaphore(); + + //! \brief Performs the wait (or “procure”) operation on the semaphore. + //! + //! Atomically decrements the value of the semaphore by 1. If the new value is + //! negative, this function blocks and will not return until the semaphore’s + //! value is incremented to 0 by Signal(). + void Wait(); + + //! \brief Performs the signal (or “post”) operation on the semaphore. + //! + //! Atomically increments the value of the semaphore by 1. If the new value is + //! 0, a caller blocked in Wait() will be awakened. + void Signal(); + + private: +#if defined(OS_MACOSX) + dispatch_semaphore_t semaphore_; +#else + sem_t semaphore_; +#endif +}; + +} // namespace crashpad + +#endif // CRASHPAD_UTIL_SYNCHRONIZATION_SEMAPHORE_H_ diff --git a/util/synchronization/semaphore_test.cc b/util/synchronization/semaphore_test.cc new file mode 100644 index 00000000..3eb53da7 --- /dev/null +++ b/util/synchronization/semaphore_test.cc @@ -0,0 +1,88 @@ +// Copyright 2014 The Crashpad 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 "util/synchronization/semaphore.h" + +#include + +#include "gtest/gtest.h" + +namespace { + +using namespace crashpad; + +TEST(Semaphore, Simple) { + Semaphore semaphore(1); + semaphore.Wait(); + semaphore.Signal(); +} + +struct ThreadMainInfo { + pthread_t pthread; + Semaphore* semaphore; + size_t iterations; +}; + +void* ThreadMain(void* argument) { + ThreadMainInfo* info = reinterpret_cast(argument); + for (size_t iteration = 0; iteration < info->iterations; ++iteration) { + info->semaphore->Wait(); + } + return NULL; +} + +TEST(Semaphore, Threaded) { + Semaphore semaphore(0); + ThreadMainInfo info; + info.semaphore = &semaphore; + info.iterations = 1; + + int rv = pthread_create(&info.pthread, NULL, ThreadMain, &info); + ASSERT_EQ(0, rv) << "pthread_create"; + + semaphore.Signal(); + + rv = pthread_join(info.pthread, NULL); + ASSERT_EQ(0, rv) << "pthread_join"; +} + +TEST(Semaphore, TenThreaded) { + // This test has a smaller initial value (5) than threads contending for these + // resources (10), and the threads each try to obtain the resource a different + // number of times. + Semaphore semaphore(5); + const size_t kThreads = 10; + ThreadMainInfo info[kThreads]; + size_t iterations = 0; + int rv; + for (size_t index = 0; index < kThreads; ++index) { + info[index].semaphore = &semaphore; + info[index].iterations = index; + iterations += info[index].iterations; + + rv = pthread_create(&info[index].pthread, NULL, ThreadMain, &info[index]); + ASSERT_EQ(0, rv) << "pthread_create"; + } + + for (size_t iteration = 0; iteration < iterations; ++iteration) { + semaphore.Signal(); + } + + for (size_t index = 0; index < kThreads; ++index) { + rv = pthread_join(info[index].pthread, NULL); + ASSERT_EQ(0, rv) << "pthread_join"; + } +} + +} // namespace diff --git a/util/util.gyp b/util/util.gyp index ed177776..e96aa96c 100644 --- a/util/util.gyp +++ b/util/util.gyp @@ -107,6 +107,8 @@ 'stdlib/strlcpy.h', 'stdlib/strnlen.cc', 'stdlib/strnlen.h', + 'synchronization/semaphore.cc', + 'synchronization/semaphore.h', ], 'actions': [ { @@ -226,6 +228,7 @@ 'stdlib/string_number_conversion_test.cc', 'stdlib/strlcpy_test.cc', 'stdlib/strnlen_test.cc', + 'synchronization/semaphore_test.cc', 'test/executable_path_test.cc', 'test/mac/mach_multiprocess_test.cc', 'test/multiprocess_exec_test.cc',