214 lines
5.0 KiB
C++
214 lines
5.0 KiB
C++
// vim: set sts=8 ts=2 sw=2 tw=99 et:
|
|
//
|
|
// Copyright (C) 2013, David Anderson and AlliedModders LLC
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without
|
|
// modification, are permitted provided that the following conditions are met:
|
|
//
|
|
// * Redistributions of source code must retain the above copyright notice, this
|
|
// list of conditions and the following disclaimer.
|
|
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
// this list of conditions and the following disclaimer in the documentation
|
|
// and/or other materials provided with the distribution.
|
|
// * Neither the name of AlliedModders LLC nor the names of its contributors
|
|
// may be used to endorse or promote products derived from this software
|
|
// without specific prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
// POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#ifndef _include_amtl_thread_posix_h_
|
|
#define _include_amtl_thread_posix_h_
|
|
|
|
#include <pthread.h>
|
|
#include <sys/time.h>
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#if defined(__linux__)
|
|
# include <sys/prctl.h>
|
|
#endif
|
|
#if defined(__APPLE__)
|
|
# include <dlfcn.h>
|
|
#endif
|
|
|
|
namespace ke {
|
|
|
|
class Mutex : public Lockable
|
|
{
|
|
public:
|
|
Mutex() {
|
|
#if !defined(NDEBUG)
|
|
int rv =
|
|
#endif
|
|
pthread_mutex_init(&mutex_, nullptr);
|
|
assert(rv == 0);
|
|
}
|
|
~Mutex() {
|
|
pthread_mutex_destroy(&mutex_);
|
|
}
|
|
|
|
bool DoTryLock() KE_OVERRIDE {
|
|
return pthread_mutex_trylock(&mutex_) == 0;
|
|
}
|
|
|
|
void DoLock() KE_OVERRIDE {
|
|
pthread_mutex_lock(&mutex_);
|
|
}
|
|
|
|
void DoUnlock() KE_OVERRIDE {
|
|
pthread_mutex_unlock(&mutex_);
|
|
}
|
|
|
|
pthread_mutex_t *raw() {
|
|
return &mutex_;
|
|
}
|
|
|
|
private:
|
|
pthread_mutex_t mutex_;
|
|
};
|
|
|
|
// Currently, this class only supports single-listener CVs.
|
|
class ConditionVariable : public Lockable
|
|
{
|
|
public:
|
|
ConditionVariable() {
|
|
#if !defined(NDEBUG)
|
|
int rv =
|
|
#endif
|
|
pthread_cond_init(&cv_, nullptr);
|
|
assert(rv == 0);
|
|
}
|
|
~ConditionVariable() {
|
|
pthread_cond_destroy(&cv_);
|
|
}
|
|
|
|
bool DoTryLock() KE_OVERRIDE {
|
|
return mutex_.DoTryLock();
|
|
}
|
|
void DoLock() KE_OVERRIDE {
|
|
mutex_.DoLock();
|
|
}
|
|
void DoUnlock() KE_OVERRIDE {
|
|
mutex_.DoUnlock();
|
|
}
|
|
|
|
void Notify() {
|
|
AssertCurrentThreadOwns();
|
|
pthread_cond_signal(&cv_);
|
|
}
|
|
|
|
WaitResult Wait(size_t timeout_ms) {
|
|
AssertCurrentThreadOwns();
|
|
|
|
#if defined(__linux__)
|
|
struct timespec ts;
|
|
if (clock_gettime(CLOCK_REALTIME, &ts) == -1)
|
|
return Wait_Error;
|
|
#else
|
|
struct timeval tv;
|
|
gettimeofday(&tv, nullptr);
|
|
|
|
struct timespec ts;
|
|
ts.tv_sec = tv.tv_sec;
|
|
ts.tv_nsec = tv.tv_usec * 1000;
|
|
#endif
|
|
|
|
ts.tv_sec += timeout_ms / 1000;
|
|
ts.tv_nsec += (timeout_ms % 1000) * 1000000;
|
|
if (ts.tv_nsec >= 1000000000) {
|
|
ts.tv_sec++;
|
|
ts.tv_nsec -= 1000000000;
|
|
}
|
|
|
|
DebugSetUnlocked();
|
|
int rv = pthread_cond_timedwait(&cv_, mutex_.raw(), &ts);
|
|
DebugSetLocked();
|
|
|
|
if (rv == ETIMEDOUT)
|
|
return Wait_Timeout;
|
|
if (rv == 0)
|
|
return Wait_Signaled;
|
|
return Wait_Error;
|
|
}
|
|
|
|
WaitResult Wait() {
|
|
AssertCurrentThreadOwns();
|
|
|
|
DebugSetUnlocked();
|
|
int rv = pthread_cond_wait(&cv_, mutex_.raw());
|
|
DebugSetLocked();
|
|
|
|
if (rv == 0)
|
|
return Wait_Signaled;
|
|
return Wait_Error;
|
|
}
|
|
|
|
private:
|
|
Mutex mutex_;
|
|
pthread_cond_t cv_;
|
|
};
|
|
|
|
class Thread
|
|
{
|
|
struct ThreadData {
|
|
IRunnable *run;
|
|
char name[17];
|
|
};
|
|
public:
|
|
Thread(IRunnable *run, const char *name = nullptr) {
|
|
ThreadData *data = new ThreadData;
|
|
data->run = run;
|
|
snprintf(data->name, sizeof(data->name), "%s", name ? name : "");
|
|
|
|
initialized_ = (pthread_create(&thread_, nullptr, Main, data) == 0);
|
|
if (!initialized_)
|
|
delete data;
|
|
}
|
|
|
|
bool Succeeded() const {
|
|
return initialized_;
|
|
}
|
|
|
|
void Join() {
|
|
if (!Succeeded())
|
|
return;
|
|
pthread_join(thread_, nullptr);
|
|
}
|
|
|
|
private:
|
|
static void *Main(void *arg) {
|
|
AutoPtr<ThreadData> data((ThreadData *)arg);
|
|
|
|
if (data->name[0]) {
|
|
#if defined(__linux__)
|
|
prctl(PR_SET_NAME, (unsigned long)data->name);
|
|
#elif defined(__APPLE__)
|
|
int (*fn)(const char *) = (int (*)(const char *))dlsym(RTLD_DEFAULT, "pthread_setname_np");
|
|
if (fn)
|
|
fn(data->name);
|
|
#endif
|
|
}
|
|
data->run->Run();
|
|
return nullptr;
|
|
}
|
|
|
|
private:
|
|
bool initialized_;
|
|
pthread_t thread_;
|
|
};
|
|
|
|
} // namespace ke
|
|
|
|
#endif // _include_amtl_thread_posix_h_
|
|
|