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 <dispatch/dispatch.h>
|
||||
#include <mach-o/dyld.h>
|
||||
#include <mach-o/dyld_images.h>
|
||||
#include <mach/mach.h>
|
||||
@ -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<mach_vm_address_t>(&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
|
||||
|
@ -14,7 +14,6 @@
|
||||
|
||||
#include "util/mach/exception_ports.h"
|
||||
|
||||
#include <dispatch/dispatch.h>
|
||||
#include <mach/mach.h>
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
@ -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
|
||||
|
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/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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user