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:
Mark Mentovai 2014-09-24 13:32:31 -04:00
parent 7e5c11f59a
commit b7a1070335
6 changed files with 237 additions and 34 deletions

View File

@ -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 threads 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 its 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 threads 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 everythings known to be synchronized, otherwise
// theres a race between the parent thread storing this threads pthread_t

View File

@ -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 its 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 its 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

View 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

View 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 semaphores
//! 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_

View 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

View File

@ -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',