|
- //
- // Lol Engine
- //
- // Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
- // © 2014—2015 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
- //
- // This library is free software. It comes without any warranty, to
- // the extent permitted by applicable law. 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 the WTFPL Task Force.
- // See http://www.wtfpl.net/ for more details.
- //
-
- #pragma once
-
- //
- // The base classes for multithreading
- // -----------------------------------
- //
-
- #if defined HAVE_PTHREAD_H
- # include <pthread.h>
- #elif defined _XBOX
- # include <xtl.h>
- # undef near /* Fuck Microsoft */
- # undef far /* Fuck Microsoft again */
- #elif defined _WIN32
- # include <windows.h>
- # undef near /* Fuck Microsoft */
- # undef far /* Fuck Microsoft again */
- #else
- # error No threading support yet :(
- #endif
-
- namespace lol
- {
-
- class mutex_base
- {
- public:
- mutex_base()
- {
- #if defined HAVE_PTHREAD_H
- pthread_mutex_init(&m_mutex, nullptr);
- #elif defined _WIN32
- InitializeCriticalSection(&m_mutex);
- #endif
- }
-
- ~mutex_base()
- {
- #if defined HAVE_PTHREAD_H
- pthread_mutex_destroy(&m_mutex);
- #elif defined _WIN32
- DeleteCriticalSection(&m_mutex);
- #endif
- }
-
- void lock()
- {
- #if defined HAVE_PTHREAD_H
- pthread_mutex_lock(&m_mutex);
- #elif defined _WIN32
- EnterCriticalSection(&m_mutex);
- #endif
- }
-
- void unlock()
- {
- #if defined HAVE_PTHREAD_H
- pthread_mutex_unlock(&m_mutex);
- #elif defined _WIN32
- LeaveCriticalSection(&m_mutex);
- #endif
- }
-
- private:
- #if defined HAVE_PTHREAD_H
- pthread_mutex_t m_mutex;
- #elif defined _WIN32
- CRITICAL_SECTION m_mutex;
- #endif
- };
-
- template<typename T, int N>
- class queue_base
- {
- public:
- queue_base()
- {
- m_start = m_count = 0;
- #if defined HAVE_PTHREAD_H
- m_poppers = m_pushers = 0;
- pthread_mutex_init(&m_mutex, nullptr);
- pthread_cond_init(&m_empty_cond, nullptr);
- pthread_cond_init(&m_full_cond, nullptr);
- #elif defined _WIN32
- m_empty_sem = CreateSemaphore(nullptr, CAPACITY, CAPACITY, nullptr);
- m_full_sem = CreateSemaphore(nullptr, 0, CAPACITY, nullptr);
- InitializeCriticalSection(&m_mutex);
- #endif
- }
-
- ~queue_base()
- {
- #if defined HAVE_PTHREAD_H
- pthread_cond_destroy(&m_empty_cond);
- pthread_cond_destroy(&m_full_cond);
- pthread_mutex_destroy(&m_mutex);
- #elif defined _WIN32
- CloseHandle(m_empty_sem);
- CloseHandle(m_full_sem);
- DeleteCriticalSection(&m_mutex);
- #endif
- }
-
- ptrdiff_t count()
- {
- ptrdiff_t current_count = 0;
- #if defined HAVE_PTHREAD_H
- pthread_mutex_lock(&m_mutex);
- /* If queue is full, wait on the "full" cond var. */
- m_pushers++;
- while (m_count == CAPACITY)
- pthread_cond_wait(&m_full_cond, &m_mutex);
- m_pushers--;
- #elif defined _WIN32
- WaitForSingleObject(m_empty_sem, INFINITE);
- EnterCriticalSection(&m_mutex);
- #endif
-
- current_count = (ptrdiff_t)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 current_count;
- }
-
- void push(T value)
- {
- #if defined HAVE_PTHREAD_H
- pthread_mutex_lock(&m_mutex);
- /* If queue is full, wait on the "full" cond var. */
- m_pushers++;
- while (m_count == CAPACITY)
- pthread_cond_wait(&m_full_cond, &m_mutex);
- m_pushers--;
- #elif defined _WIN32
- WaitForSingleObject(m_empty_sem, INFINITE);
- 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
- }
-
- bool try_push(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
- pthread_mutex_lock(&m_mutex);
- /* Wait until there is something in the queue. Be careful, we
- * could get woken up but another thread may have eaten the
- * message in the meantime. */
- m_poppers++;
- while (m_count == 0)
- pthread_cond_wait(&m_empty_cond, &m_mutex);
- m_poppers--;
- #elif defined _WIN32
- WaitForSingleObject(m_full_sem, INFINITE);
- EnterCriticalSection(&m_mutex);
- #endif
-
- /* Pop value */
- T 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 ret;
- }
-
- bool try_pop(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];
- size_t m_start, m_count;
- #if defined HAVE_PTHREAD_H
- size_t m_poppers, m_pushers;
- pthread_mutex_t m_mutex;
- pthread_cond_t m_empty_cond, m_full_cond;
- #elif defined _WIN32
- HANDLE m_empty_sem, m_full_sem;
- CRITICAL_SECTION m_mutex;
- #endif
- };
-
- class thread_base
- {
- public:
- thread_base(std::function<void(void)> function)
- : m_function(function)
- {
- #if defined HAVE_PTHREAD_H
- /* Set the joinable attribute for systems who don't play nice */
- pthread_attr_t attr;
- pthread_attr_init(&attr);
- pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
- pthread_create(&m_thread, &attr, trampoline, this);
- #elif defined _WIN32
- m_thread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)trampoline,
- this, 0, &m_tid);
- #endif
- }
-
- virtual ~thread_base()
- {
- #if defined HAVE_PTHREAD_H
- pthread_join(m_thread, nullptr);
- #elif defined _WIN32
- WaitForSingleObject(m_thread, INFINITE);
- #endif
- }
-
- private:
- static void *trampoline(void *data)
- {
- thread_base *that = (thread_base *)data;
- that->m_function();
- return nullptr;
- }
-
- std::function<void(void)> m_function;
-
- #if defined HAVE_PTHREAD_H
- pthread_t m_thread;
- #elif defined _WIN32
- HANDLE m_thread;
- DWORD m_tid;
- #endif
- };
-
- } /* namespace lol */
|