// 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 #include #include #include #if defined(__linux__) # include #endif #if defined(__APPLE__) # include #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 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_