mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:26:06 +00:00
Add Semaphore and its test, and use it where semaphores are needed.
TEST=util_test Semaphore.*:ProcessReader.*:ExceptionPorts.* R=rsesek@chromium.org Review URL: https://codereview.chromium.org/589243003
This commit is contained in:
parent
7e5c11f59a
commit
b7a1070335
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include "util/mac/process_reader.h"
|
#include "util/mac/process_reader.h"
|
||||||
|
|
||||||
#include <dispatch/dispatch.h>
|
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <mach-o/dyld_images.h>
|
#include <mach-o/dyld_images.h>
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
@ -35,6 +34,7 @@
|
|||||||
#include "util/mac/mach_o_image_reader.h"
|
#include "util/mac/mach_o_image_reader.h"
|
||||||
#include "util/mach/mach_extensions.h"
|
#include "util/mach/mach_extensions.h"
|
||||||
#include "util/stdlib/pointer_container.h"
|
#include "util/stdlib/pointer_container.h"
|
||||||
|
#include "util/synchronization/semaphore.h"
|
||||||
#include "util/test/errors.h"
|
#include "util/test/errors.h"
|
||||||
#include "util/test/mac/dyld.h"
|
#include "util/test/mac/dyld.h"
|
||||||
#include "util/test/mac/mach_errors.h"
|
#include "util/test/mac/mach_errors.h"
|
||||||
@ -170,8 +170,8 @@ class TestThreadPool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ThreadInfo* thread_info : thread_infos_) {
|
for (ThreadInfo* thread_info : thread_infos_) {
|
||||||
dispatch_semaphore_signal(thread_info->exit_semaphore);
|
thread_info->exit_semaphore.Signal();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ThreadInfo* thread_info : thread_infos_) {
|
for (const ThreadInfo* thread_info : thread_infos_) {
|
||||||
@ -197,10 +197,8 @@ class TestThreadPool {
|
|||||||
ASSERT_EQ(0, rv);
|
ASSERT_EQ(0, rv);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (const ThreadInfo* thread_info : thread_infos_) {
|
for (ThreadInfo* thread_info : thread_infos_) {
|
||||||
long rv = dispatch_semaphore_wait(thread_info->ready_semaphore,
|
thread_info->ready_semaphore.Wait();
|
||||||
DISPATCH_TIME_FOREVER);
|
|
||||||
ASSERT_EQ(0, rv);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If present, suspend the thread at indices 1 through 3 the same number of
|
// If present, suspend the thread at indices 1 through 3 the same number of
|
||||||
@ -238,15 +236,12 @@ class TestThreadPool {
|
|||||||
ThreadInfo()
|
ThreadInfo()
|
||||||
: pthread(NULL),
|
: pthread(NULL),
|
||||||
stack_address(0),
|
stack_address(0),
|
||||||
ready_semaphore(dispatch_semaphore_create(0)),
|
ready_semaphore(0),
|
||||||
exit_semaphore(dispatch_semaphore_create(0)),
|
exit_semaphore(0),
|
||||||
suspend_count(0) {
|
suspend_count(0) {
|
||||||
}
|
}
|
||||||
|
|
||||||
~ThreadInfo() {
|
~ThreadInfo() {}
|
||||||
dispatch_release(exit_semaphore);
|
|
||||||
dispatch_release(ready_semaphore);
|
|
||||||
}
|
|
||||||
|
|
||||||
// The thread’s ID, set at the time the thread is created.
|
// The thread’s ID, set at the time the thread is created.
|
||||||
pthread_t pthread;
|
pthread_t pthread;
|
||||||
@ -259,12 +254,12 @@ class TestThreadPool {
|
|||||||
// setting up its ThreadInfo structure. The main thread waits on this
|
// setting up its ThreadInfo structure. The main thread waits on this
|
||||||
// semaphore before using any data that the worker thread is responsible for
|
// semaphore before using any data that the worker thread is responsible for
|
||||||
// setting.
|
// setting.
|
||||||
dispatch_semaphore_t ready_semaphore;
|
Semaphore ready_semaphore;
|
||||||
|
|
||||||
// The worker thread waits on exit_semaphore to determine when it’s safe to
|
// 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
|
// exit. The main thread signals exit_semaphore when it no longer needs the
|
||||||
// worker thread.
|
// worker thread.
|
||||||
dispatch_semaphore_t exit_semaphore;
|
Semaphore exit_semaphore;
|
||||||
|
|
||||||
// The thread’s suspend count.
|
// The thread’s suspend count.
|
||||||
int suspend_count;
|
int suspend_count;
|
||||||
@ -276,8 +271,8 @@ class TestThreadPool {
|
|||||||
thread_info->stack_address =
|
thread_info->stack_address =
|
||||||
reinterpret_cast<mach_vm_address_t>(&thread_info);
|
reinterpret_cast<mach_vm_address_t>(&thread_info);
|
||||||
|
|
||||||
dispatch_semaphore_signal(thread_info->ready_semaphore);
|
thread_info->ready_semaphore.Signal();
|
||||||
dispatch_semaphore_wait(thread_info->exit_semaphore, DISPATCH_TIME_FOREVER);
|
thread_info->exit_semaphore.Wait();
|
||||||
|
|
||||||
// Check this here after everything’s known to be synchronized, otherwise
|
// 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
|
// there’s a race between the parent thread storing this thread’s pthread_t
|
||||||
|
@ -14,7 +14,6 @@
|
|||||||
|
|
||||||
#include "util/mach/exception_ports.h"
|
#include "util/mach/exception_ports.h"
|
||||||
|
|
||||||
#include <dispatch/dispatch.h>
|
|
||||||
#include <mach/mach.h>
|
#include <mach/mach.h>
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
@ -30,6 +29,7 @@
|
|||||||
#include "util/mach/exc_server_variants.h"
|
#include "util/mach/exc_server_variants.h"
|
||||||
#include "util/mach/mach_extensions.h"
|
#include "util/mach/mach_extensions.h"
|
||||||
#include "util/misc/scoped_forbid_return.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_errors.h"
|
||||||
#include "util/test/mac/mach_multiprocess.h"
|
#include "util/test/mac/mach_multiprocess.h"
|
||||||
|
|
||||||
@ -206,13 +206,10 @@ class TestExceptionPorts : public UniversalMachExcServer,
|
|||||||
explicit Child(TestExceptionPorts* test_exception_ports)
|
explicit Child(TestExceptionPorts* test_exception_ports)
|
||||||
: test_exception_ports_(test_exception_ports),
|
: test_exception_ports_(test_exception_ports),
|
||||||
thread_(),
|
thread_(),
|
||||||
init_semaphore_(dispatch_semaphore_create(0)),
|
init_semaphore_(0),
|
||||||
crash_semaphore_(dispatch_semaphore_create(0)) {}
|
crash_semaphore_(0) {}
|
||||||
|
|
||||||
~Child() {
|
~Child() {}
|
||||||
dispatch_release(crash_semaphore_);
|
|
||||||
dispatch_release(init_semaphore_);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Run() {
|
void Run() {
|
||||||
ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask,
|
ExceptionPorts self_task_ports(ExceptionPorts::kTargetTypeTask,
|
||||||
@ -239,9 +236,7 @@ class TestExceptionPorts : public UniversalMachExcServer,
|
|||||||
ASSERT_EQ(0, rv_int);
|
ASSERT_EQ(0, rv_int);
|
||||||
|
|
||||||
// Wait for the new thread to be ready.
|
// Wait for the new thread to be ready.
|
||||||
long rv_long =
|
init_semaphore_.Wait();
|
||||||
dispatch_semaphore_wait(init_semaphore_, DISPATCH_TIME_FOREVER);
|
|
||||||
ASSERT_EQ(0, rv_long);
|
|
||||||
|
|
||||||
// Tell the parent process that everything is set up.
|
// Tell the parent process that everything is set up.
|
||||||
char c = '\0';
|
char c = '\0';
|
||||||
@ -269,7 +264,7 @@ class TestExceptionPorts : public UniversalMachExcServer,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Let the other thread know it’s safe to proceed.
|
// 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 this thread is the one that crashes, do it.
|
||||||
if (test_exception_ports_->who_crashes() == kMainThreadCrashes) {
|
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.
|
// 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.
|
// Wait for the main thread to signal that it’s safe to proceed.
|
||||||
long rv =
|
crash_semaphore_.Wait();
|
||||||
dispatch_semaphore_wait(crash_semaphore_, DISPATCH_TIME_FOREVER);
|
|
||||||
CHECK_EQ(0, rv) << "dispatch_semaphore_wait";
|
|
||||||
|
|
||||||
// Regardless of where ExceptionPorts::SetExceptionPort() ran,
|
// Regardless of where ExceptionPorts::SetExceptionPort() ran,
|
||||||
// ExceptionPorts::GetExceptionPorts() can always be tested in-process.
|
// 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
|
// The main thread waits on this for the other thread to start up and
|
||||||
// perform its own initialization.
|
// 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
|
// 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.
|
// 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
|
// 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
|
// structured as a static volatile int to ward off aggressive compiler
|
||||||
|
61
util/synchronization/semaphore.cc
Normal file
61
util/synchronization/semaphore.cc
Normal file
@ -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
|
63
util/synchronization/semaphore.h
Normal file
63
util/synchronization/semaphore.h
Normal file
@ -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 <dispatch/dispatch.h>
|
||||||
|
#else
|
||||||
|
#include <semaphore.h>
|
||||||
|
#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_
|
88
util/synchronization/semaphore_test.cc
Normal file
88
util/synchronization/semaphore_test.cc
Normal file
@ -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 <pthread.h>
|
||||||
|
|
||||||
|
#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<ThreadMainInfo*>(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
|
@ -107,6 +107,8 @@
|
|||||||
'stdlib/strlcpy.h',
|
'stdlib/strlcpy.h',
|
||||||
'stdlib/strnlen.cc',
|
'stdlib/strnlen.cc',
|
||||||
'stdlib/strnlen.h',
|
'stdlib/strnlen.h',
|
||||||
|
'synchronization/semaphore.cc',
|
||||||
|
'synchronization/semaphore.h',
|
||||||
],
|
],
|
||||||
'actions': [
|
'actions': [
|
||||||
{
|
{
|
||||||
@ -226,6 +228,7 @@
|
|||||||
'stdlib/string_number_conversion_test.cc',
|
'stdlib/string_number_conversion_test.cc',
|
||||||
'stdlib/strlcpy_test.cc',
|
'stdlib/strlcpy_test.cc',
|
||||||
'stdlib/strnlen_test.cc',
|
'stdlib/strnlen_test.cc',
|
||||||
|
'synchronization/semaphore_test.cc',
|
||||||
'test/executable_path_test.cc',
|
'test/executable_path_test.cc',
|
||||||
'test/mac/mach_multiprocess_test.cc',
|
'test/mac/mach_multiprocess_test.cc',
|
||||||
'test/multiprocess_exec_test.cc',
|
'test/multiprocess_exec_test.cc',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user