From ca230a60ad716a42699ea79651fb9d97460df79e Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Thu, 15 Aug 2013 17:31:24 +0000 Subject: [PATCH] thread: implement Queue::TryPush and Queue::TryPop. --- src/platform/ps3/threadbase.h | 33 +++++++++++++++++ src/sys/threadbase.h | 68 +++++++++++++++++++++++++++++++++++ test/Makefile.am | 2 +- test/testsuite.vcxproj | 1 + test/unit/thread.cpp | 56 +++++++++++++++++++++++++++++ 5 files changed, 159 insertions(+), 1 deletion(-) create mode 100644 test/unit/thread.cpp diff --git a/src/platform/ps3/threadbase.h b/src/platform/ps3/threadbase.h index b362f66e..409230bb 100644 --- a/src/platform/ps3/threadbase.h +++ b/src/platform/ps3/threadbase.h @@ -96,6 +96,22 @@ public: sys_lwmutex_unlock(&m_mutex); } + bool TryPush(T value) + { + sys_lwmutex_lock(&m_mutex, 0); + if (m_count == CAPACITY) + { + sys_lwmutex_unlock(&m_mutex); + return false; + } + m_values[(m_start + m_count) % CAPACITY] = value; + m_count++; + if (m_poppers) + sys_lwcond_signal(&m_empty_cond); + sys_lwmutex_unlock(&m_mutex); + return true; + } + T Pop() { sys_lwmutex_lock(&m_mutex, 0); @@ -112,6 +128,23 @@ public: return ret; } + bool TryPop(T &ret) + { + sys_lwmutex_lock(&m_mutex, 0); + if (m_count == 0) + { + sys_lwmutex_unlock(&m_mutex); + return false; + } + ret = m_values[m_start]; + m_start = (m_start + 1) % CAPACITY; + m_count--; + if (m_pushers) + sys_lwcond_signal(&m_full_cond); + sys_lwmutex_unlock(&m_mutex); + return true; + } + private: static size_t const CAPACITY = N; T m_values[CAPACITY]; diff --git a/src/sys/threadbase.h b/src/sys/threadbase.h index 3d210459..ec887b50 100644 --- a/src/sys/threadbase.h +++ b/src/sys/threadbase.h @@ -140,6 +140,40 @@ public: #endif } + bool TryPush(T value) + { +#if defined HAVE_PTHREAD_H + pthread_mutex_lock(&m_mutex); + /* If queue is full, wait on the "full" cond var. */ + if (m_count == CAPACITY) + { + pthread_mutex_unlock(&m_mutex); + return false; + } +#elif defined _WIN32 + DWORD status = WaitForSingleObject(m_empty_sem, 0); + if (status == WAIT_TIMEOUT) + return false; + EnterCriticalSection(&m_mutex); +#endif + + /* Push value */ + m_values[(m_start + m_count) % CAPACITY] = value; + m_count++; + +#if defined HAVE_PTHREAD_H + /* If there were poppers waiting, signal the "empty" cond var. */ + if (m_poppers) + pthread_cond_signal(&m_empty_cond); + pthread_mutex_unlock(&m_mutex); +#elif defined _WIN32 + LeaveCriticalSection(&m_mutex); + ReleaseSemaphore(m_full_sem, 1, nullptr); +#endif + + return true; + } + T Pop() { #if defined HAVE_PTHREAD_H @@ -174,6 +208,40 @@ public: return ret; } + bool TryPop(T &ret) + { +#if defined HAVE_PTHREAD_H + pthread_mutex_lock(&m_mutex); + if (m_count == 0) + { + pthread_mutex_unlock(&m_mutex); + return false; + } +#elif defined _WIN32 + DWORD status = WaitForSingleObject(m_full_sem, 0); + if (status == WAIT_TIMEOUT) + return false; + EnterCriticalSection(&m_mutex); +#endif + + /* Pop value */ + ret = m_values[m_start]; + m_start = (m_start + 1) % CAPACITY; + m_count--; + +#if defined HAVE_PTHREAD_H + /* If there were pushers waiting, signal the "full" cond var. */ + if (m_pushers) + pthread_cond_signal(&m_full_cond); + pthread_mutex_unlock(&m_mutex); +#else + LeaveCriticalSection(&m_mutex); + ReleaseSemaphore(m_empty_sem, 1, nullptr); +#endif + + return true; + } + private: static size_t const CAPACITY = N; T m_values[CAPACITY]; diff --git a/test/Makefile.am b/test/Makefile.am index 6b21786c..e9f6e65b 100644 --- a/test/Makefile.am +++ b/test/Makefile.am @@ -20,7 +20,7 @@ testsuite_SOURCES = testsuite.cpp \ unit/build.cpp unit/real.cpp unit/image.cpp unit/quat.cpp unit/cmplx.cpp \ unit/array.cpp unit/rotation.cpp unit/string.cpp unit/map.cpp \ unit/color.cpp unit/atomic.cpp unit/interp.cpp unit/box.cpp \ - unit/rand.cpp + unit/rand.cpp unit/thread.cpp testsuite_CPPFLAGS = $(AM_CPPFLAGS) testsuite_DEPENDENCIES = @LOL_DEPS@ noinst_DATA = data/gradient.png diff --git a/test/testsuite.vcxproj b/test/testsuite.vcxproj index 137cc6ed..55214223 100644 --- a/test/testsuite.vcxproj +++ b/test/testsuite.vcxproj @@ -59,6 +59,7 @@ + diff --git a/test/unit/thread.cpp b/test/unit/thread.cpp new file mode 100644 index 00000000..fad7fa5e --- /dev/null +++ b/test/unit/thread.cpp @@ -0,0 +1,56 @@ +// +// Lol Engine +// +// Copyright: (c) 2010-2013 Sam Hocevar +// This program is free software; you can redistribute it and/or +// modify it under the terms of the Do What The Fuck You Want To +// Public License, Version 2, as published by Sam Hocevar. See +// http://www.wtfpl.net/ for more details. +// + +#if defined HAVE_CONFIG_H +# include "config.h" +#endif + +#include "core.h" +#include "lol/unit.h" + +namespace lol +{ + +LOLUNIT_FIXTURE(ThreadTest) +{ + void SetUp() {} + + void TearDown() {} + + LOLUNIT_TEST(QueueTryPush) + { + Queue q; + + bool b1 = q.TryPush(0); + LOLUNIT_ASSERT_EQUAL(true, b1); + + bool b2 = q.TryPush(1); + LOLUNIT_ASSERT_EQUAL(false, b2); + } + + LOLUNIT_TEST(QueueTryPop) + { + Queue q; + int tmp; + + q.Push(42); + + bool b1 = q.TryPop(tmp); + LOLUNIT_ASSERT_EQUAL(true, b1); + LOLUNIT_ASSERT_EQUAL(42, tmp); + + bool b2 = q.TryPop(tmp); + LOLUNIT_ASSERT_EQUAL(false, b2); + LOLUNIT_ASSERT_EQUAL(42, tmp); + } +}; + +} /* namespace lol */ +