threads: add posix/win32 thread wrappers

This commit is contained in:
Danny Robson 2015-08-10 15:48:30 +10:00
parent 1a2aa64af6
commit 243716a121
20 changed files with 917 additions and 0 deletions

View File

@ -231,6 +231,12 @@ UTIL_FILES = \
tap.cpp \
tap.hpp \
tap.ipp \
threads/barrier.hpp \
threads/condition.hpp \
threads/mutex.hpp \
threads/semaphore.hpp \
threads/spinlock.hpp \
threads/thread.hpp \
time.cpp \
time.hpp \
tuple.cpp \
@ -263,6 +269,12 @@ UTIL_FILES += \
io_posix.hpp \
posix/dir.hpp \
posix/dir.cpp \
threads/barrier_posix.cpp \
threads/condition_posix.cpp \
threads/mutex_posix.cpp \
threads/semaphore_posix.cpp \
threads/spinlock_posix.cpp \
threads/thread_posix.cpp \
time_posix.cpp
endif
@ -270,6 +282,10 @@ if PLATFORM_WIN32
UTIL_FILES += \
backtrace_null.cpp \
io_win32.cpp \
threads/mutex_win32.cpp \
threads/mutex_win32.hpp \
threads/thread_win32.hpp \
threads/thread_win32.cpp \
time_win32.cpp \
win32/registry.hpp \
win32/registry.cpp

35
threads/barrier.hpp Normal file
View File

@ -0,0 +1,35 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_BARRIER_HPP
#define __UTIL_THREADS_BARRIER_HPP
#include <pthread.h>
namespace util { namespace threads {
class barrier {
public:
barrier (unsigned count);
~barrier ();
void wait (void);
private:
pthread_barrier_t m_id;
};
} }
#endif

44
threads/barrier_posix.cpp Normal file
View File

@ -0,0 +1,44 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "barrier.hpp"
#include "except.hpp"
using util::threads::barrier;
///////////////////////////////////////////////////////////////////////////////
barrier::barrier (unsigned count)
{
pthread_barrier_init (&m_id, nullptr, count);
}
//-----------------------------------------------------------------------------
barrier::~barrier ()
{
pthread_barrier_destroy (&m_id);
}
///////////////////////////////////////////////////////////////////////////////
void
barrier::wait (void)
{
auto err = pthread_barrier_wait (&m_id);
errno_error::try_code (err);
}

43
threads/condition.hpp Normal file
View File

@ -0,0 +1,43 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_CONDITION_HPP
#define __UTIL_THREADS_CONDITION_HPP
#include "mutex.hpp"
#include <chrono>
namespace util { namespace threads {
class condition {
public:
condition ();
~condition ();
void wait (mutex&);
void wait (mutex&, std::chrono::nanoseconds);
void signal (void);
void broadcast (void);
pthread_cond_t native (void);
private:
pthread_cond_t m_id;
};
} }
#endif

View File

@ -0,0 +1,81 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "condition.hpp"
#include "except.hpp"
using util::threads::condition;
using util::threads::mutex;
///////////////////////////////////////////////////////////////////////////////
condition::condition ()
{
auto err = pthread_cond_init (&m_id, nullptr);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
condition::~condition ()
{
auto err = pthread_cond_destroy (&m_id);
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
void
condition::signal (void)
{
auto err = pthread_cond_signal (&m_id);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
void
condition::broadcast (void)
{
auto err = pthread_cond_broadcast (&m_id);
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
void
condition::wait (mutex &m)
{
auto mutid = m.native ();
auto err = pthread_cond_wait (&m_id, &mutid);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
void
condition::wait (mutex &m, std::chrono::nanoseconds t)
{
struct timespec abstime;
abstime.tv_sec = std::chrono::duration_cast<std::chrono::seconds> (t).count ();
abstime.tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds> (t).count ();
auto mutid = m.native ();
auto err = pthread_cond_timedwait (&m_id, &mutid, &abstime);
errno_error::try_code (err);
}

39
threads/mutex.hpp Normal file
View File

@ -0,0 +1,39 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_MUTEX_HPP
#define __UTIL_THREADS_MUTEX_HPP
#include <pthread.h>
namespace util { namespace threads {
class mutex {
public:
mutex ();
~mutex ();
void lock (void);
bool try_lock (void);
void unlock (void);
pthread_mutex_t native (void);
private:
pthread_mutex_t m_id;
};
} }
#endif

63
threads/mutex_posix.cpp Normal file
View File

@ -0,0 +1,63 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "mutex.hpp"
#include "../except.hpp"
#include "../debug.hpp"
using util::threads::mutex;
///////////////////////////////////////////////////////////////////////////////
mutex::mutex ()
{
auto err = pthread_mutex_init (&m_id, nullptr);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
mutex::~mutex ()
{
auto err = pthread_mutex_destroy (&m_id);
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
void
mutex::lock (void)
{
auto err = pthread_mutex_lock (&m_id);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
bool
mutex::try_lock (void)
{
switch (auto err = pthread_mutex_trylock (&m_id)) {
case 0: return true;
case EBUSY: return false;
default:
errno_error::throw_code (err);
}
unreachable ();
}

71
threads/mutex_win32.cpp Normal file
View File

@ -0,0 +1,71 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "mutex_win32.hpp"
#include "except.hpp"
using util::threads::win32::mutex;
///////////////////////////////////////////////////////////////////////////////
mutex::mutex ():
m_id (CreateMutex (nullptr, false, nullptr))
{ ; }
///////////////////////////////////////////////////////////////////////////////
bool
mutex::lock (void)
{
switch (WaitForSingleObject (m_id, INFINITE)) {
case WAIT_OBJECT_0:
return true;
case WAIT_FAILED:
win32_error::throw_code ();
default:
unreachable ();
}
}
//-----------------------------------------------------------------------------
bool
mutex::lock (std::chrono::nanoseconds ns)
{
auto ms = std::chrono::duration_cast<std::chrono::milliseconds> (ns).count ();
switch (WaitForSingleObject (m_id, ms)) {
case WAIT_OBJECT_0:
return true;
case WAIT_TIMEOUT:
return false;
case WAIT_FAILED:
win32_error::throw_code ();
default:
unreachable ();
}
}
///////////////////////////////////////////////////////////////////////////////
void
mutex::unlock (void)
{
if (ReleaseMutex (m_id))
win32_error::throw_code ();
}

41
threads/mutex_win32.hpp Normal file
View File

@ -0,0 +1,41 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_MUTEX_WIN32_HPP
#define __UTIL_THREADS_MUTEX_WIN32_HPP
#include "io.hpp"
#include <chrono>
namespace util { namespace threads { namespace win32 {
class mutex {
public:
mutex ();
bool lock (void);
bool lock (std::chrono::nanoseconds);
void unlock (void);
handle& native (void);
private:
handle m_id;
};
} } }
#endif

40
threads/semaphore.hpp Normal file
View File

@ -0,0 +1,40 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_SEMAPHORE_HPP
#define __UTIL_THREADS_SEMAPHORE_HPP
#include <semaphore.h>
namespace util { namespace threads {
class semaphore {
public:
semaphore (unsigned value = 0);
~semaphore ();
void wait (void);
bool try_wait (void);
void post (void);
sem_t native (void);
private:
sem_t m_id;
};
} }
#endif

View File

@ -0,0 +1,72 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "semaphore.hpp"
#include "debug.hpp"
#include "except.hpp"
using util::threads::semaphore;
///////////////////////////////////////////////////////////////////////////////
semaphore::semaphore (unsigned value)
{
auto err = sem_init (&m_id, false, value);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
semaphore::~semaphore ()
{
auto err = sem_destroy (&m_id);
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
void
semaphore::wait (void)
{
auto err = sem_wait (&m_id);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
bool
semaphore::try_wait (void)
{
switch (auto err = sem_wait (&m_id)) {
case 0: return true;
case EAGAIN: return false;
default:
errno_error::throw_code (err);
}
unreachable ();
}
//-----------------------------------------------------------------------------
void
semaphore::post (void)
{
auto err = sem_post (&m_id);
errno_error::try_code (err);
}

1
threads/spinlock.cpp Normal file
View File

@ -0,0 +1 @@
#include "spinlock.hpp"

38
threads/spinlock.hpp Normal file
View File

@ -0,0 +1,38 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_SPINLOCK_HPP
#define __UTIL_THREADS_SPINLOCK_HPP
#include <pthread.h>
namespace util { namespace threads {
class spinlock {
public:
spinlock ();
~spinlock ();
void lock (void);
void unlock (void);
pthread_spinlock_t native (void);
private:
pthread_spinlock_t m_id;
};
} }
#endif

View File

@ -0,0 +1,55 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "spinlock.hpp"
#include "except.hpp"
using util::threads::spinlock;
///////////////////////////////////////////////////////////////////////////////
spinlock::spinlock ()
{
auto err = pthread_spin_init (&m_id, PTHREAD_PROCESS_PRIVATE);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
spinlock::~spinlock ()
{
auto err = pthread_spin_destroy (&m_id);
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
void
spinlock::lock (void)
{
auto err = pthread_spin_lock (&m_id);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
void
spinlock::unlock (void)
{
auto err = pthread_spin_unlock (&m_id);
errno_error::try_code (err);
}

30
threads/thread.hpp Normal file
View File

@ -0,0 +1,30 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_THREAD_HPP
#define __UTIL_THREADS_THREAD_HPP
#include "platform.hpp"
#if defined(PLATFORM_LINUX)
#include "thread_posix.hpp"
#elif defined(PLATFORM_WIN32)
#include "thread_win32.hpp"
#else
#error "Unhandled platform"
#endif
#endif

74
threads/thread_posix.cpp Normal file
View File

@ -0,0 +1,74 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "thread.hpp"
#include "except.hpp"
#include <unistd.h>
using util::threads::thread;
///////////////////////////////////////////////////////////////////////////////
void
thread::join (void)
{
auto err = pthread_join (m_id, nullptr);
errno_error::try_code (err);
}
//-----------------------------------------------------------------------------
void
thread::kill (void)
{
auto err = pthread_cancel (m_id);
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
void
thread::yield (void)
{
auto err = pthread_yield ();
errno_error::try_code (err);
}
///////////////////////////////////////////////////////////////////////////////
unsigned
thread::concurrency (void)
{
auto val = sysconf (_SC_NPROCESSORS_ONLN);
if (val < 0)
errno_error::throw_code ();
return val;
}
///////////////////////////////////////////////////////////////////////////////
// pthread entry point
void*
thread::dispatch (void *func)
{
typedef decltype(thread::m_func) func_t;
(*reinterpret_cast<func_t*> (func)) ();
return nullptr;
}

54
threads/thread_posix.hpp Normal file
View File

@ -0,0 +1,54 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_THREAD_POSIX_HPP
#define __UTIL_THREADS_THREAD_POSIX_HPP
#include <functional>
#include <pthread.h>
namespace util { namespace threads {
class thread {
public:
template <typename Func, typename ...Args>
thread (Func &&_func, Args &&..._args);
thread (thread&&);
~thread ();
thread (const thread&) = delete;
thread& operator= (const thread&) = delete;
void join (void);
void kill (void);
static void yield (void);
static unsigned concurrency (void);
pthread_t native (void);
private:
static void* dispatch (void*);
std::function<void(void)> m_func;
pthread_t m_id;
};
} }
#include "thread_posix.ipp"
#endif

38
threads/thread_posix.ipp Normal file
View File

@ -0,0 +1,38 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifdef __UTIL_THREAD_IPP
#error
#endif
#define __UTIL_THREAD_IPP
#include "tuple.hpp"
#include "except.hpp"
///////////////////////////////////////////////////////////////////////////////
template <typename Func, typename ...Args>
util::threads::thread::thread (Func &&_func, Args&&..._args):
m_func ([
func = std::forward<Func> (_func),
args = std::make_tuple (std::forward<Args> (_args)...)
] (void) {
return tuple::call (func, args);
})
{
auto err = pthread_create (&m_id, nullptr, dispatch, &m_func);
errno_error::try_code (err);
}

31
threads/thread_win32.cpp Normal file
View File

@ -0,0 +1,31 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#include "thread_win32.hpp"
#include <windows.h>
using util::threads::win32::thread;
///////////////////////////////////////////////////////////////////////////////
unsigned
thread::concurrency (void)
{
SYSTEM_INFO info;
GetSystemInfo (&info);
return info.dwNumberOfProcessors;
}

51
threads/thread_win32.hpp Normal file
View File

@ -0,0 +1,51 @@
/*
* 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.
*
* Copyright 2015 Danny Robson <danny@nerdcruft.net>
*/
#ifndef __UTIL_THREADS_THREAD_WIN32_HPP
#define __UTIL_THREADS_THREAD_WIN32_HPP
#include <functional>
#include <pthread.h>
#include "io.hpp"
namespace util { namespace threads { inline namespace win32 {
class thread {
public:
template <typename Func, typename ...Args>
thread (Func &&_func, Args &&..._args) { }
thread (thread&&);
~thread ();
thread (const thread&) = delete;
thread& operator= (const thread&) = delete;
void join (void);
void kill (void);
static void yield (void);
static unsigned concurrency (void);
handle native (void);
private:
std::function<void(void)> m_func;
handle m_id;
};
} } }
#endif