- more threading things
TODO: documentation
This commit is contained in:
@@ -25,6 +25,7 @@
|
||||
namespace fennec::detail
|
||||
{
|
||||
template<typename TypeT> struct _add_pointer : type_identity<TypeT*> {};
|
||||
template<typename TypeT> struct _add_pointer<TypeT&> : type_identity<TypeT*> {};
|
||||
template<typename TypeT> struct _remove_pointer : type_identity<TypeT> {};
|
||||
template<typename TypeT> struct _remove_pointer<TypeT*> : type_identity<TypeT> {};
|
||||
|
||||
@@ -43,36 +44,48 @@ namespace fennec::detail
|
||||
template<typename TypeT> struct _remove_cv<const volatile TypeT> : type_identity<TypeT> {};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _decay : conditional<_is_const<const TypeT>{}, _remove_cv<TypeT>, _add_pointer<TypeT>> {};
|
||||
struct _decay_selector : conditional_t<_is_const<const TypeT>{}, _remove_cv<TypeT>, _add_pointer<TypeT>> {};
|
||||
|
||||
template<typename TypeT> requires requires { typename TypeT::element_t; }
|
||||
struct _decay<TypeT> : type_identity<typename TypeT::decay_t> {};
|
||||
template<typename TypeT> requires requires { typename TypeT::decay_t; }
|
||||
struct _decay_selector<TypeT> : type_identity<typename TypeT::decay_t> {};
|
||||
|
||||
template<typename TypeT, size_t N>
|
||||
struct _decay<TypeT[N]> : type_identity<TypeT*> {};
|
||||
struct _decay_selector<TypeT[N]> : type_identity<TypeT*> {};
|
||||
|
||||
template<typename TypeT>
|
||||
struct _decay<TypeT[]> : type_identity<TypeT*> {};
|
||||
struct _decay_selector<TypeT[]> : type_identity<TypeT*> {};
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
template<typename TypeT> struct _decay {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT> struct _decay<TypeT&> {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT> struct _decay<TypeT&&> {
|
||||
using type = typename _decay_selector<TypeT>::type;
|
||||
};
|
||||
|
||||
template<typename TypeT, typename = void>
|
||||
struct _add_lvalue_reference {
|
||||
using type = _Tp;
|
||||
using type = TypeT;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct _add_lvalue_reference<_Tp, void_t<_Tp&>> {
|
||||
using type = _Tp&;
|
||||
template<typename TypeT>
|
||||
struct _add_lvalue_reference<TypeT, void_t<TypeT&>> {
|
||||
using type = TypeT&;
|
||||
};
|
||||
|
||||
|
||||
template<typename _Tp, typename = void>
|
||||
template<typename TypeT, typename = void>
|
||||
struct _add_rvalue_reference {
|
||||
using type = _Tp;
|
||||
using type = TypeT;
|
||||
};
|
||||
|
||||
template<typename _Tp>
|
||||
struct _add_rvalue_reference<_Tp, void_t<_Tp&&>> {
|
||||
using type = _Tp&&;
|
||||
template<typename TypeT>
|
||||
struct _add_rvalue_reference<TypeT, void_t<TypeT&&>> {
|
||||
using type = TypeT&&;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -53,7 +53,9 @@ class function<ReturnT(ArgsT...)> {
|
||||
public:
|
||||
///
|
||||
/// \brief default constructor
|
||||
constexpr function() noexcept = default;
|
||||
constexpr function() noexcept
|
||||
: call(nullptr) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief destructor
|
||||
@@ -62,7 +64,7 @@ public:
|
||||
///
|
||||
/// \brief copy constructor
|
||||
/// \param func the function to copy
|
||||
constexpr function(const function& func) noexcept = default;
|
||||
constexpr function(const function& func) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief move constructor
|
||||
|
||||
@@ -84,6 +84,15 @@ template<typename T> constexpr T&& forward(remove_reference_t<T>&& x) noexcept {
|
||||
#endif
|
||||
|
||||
|
||||
/// \brief Copies \f$v\f$ to a new object of `decay_t<T>`
|
||||
/// \tparam T The type
|
||||
/// \param v The object
|
||||
/// \returns A stack allocated copy of \f$v\f$ in `decay_t<T>`
|
||||
template<typename T> constexpr decay_t<T> decay_copy(T&& v) {
|
||||
return fennec::forward<T>(v);
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// \brief produces an x-value type to indicate \p x may be "moved"
|
||||
///
|
||||
|
||||
@@ -47,18 +47,6 @@ namespace fennec
|
||||
|
||||
class gfxcontext {
|
||||
public:
|
||||
enum texture_ : uint8_t {
|
||||
texture_1d = 0,
|
||||
texture_1d_array,
|
||||
texture_2d,
|
||||
texture_2d_array,
|
||||
texture_multisample,
|
||||
texture_multisample_array,
|
||||
texture_cubemap,
|
||||
texture_cubemap_array,
|
||||
texture_3d,
|
||||
};
|
||||
|
||||
using handle_t = uint32_t;
|
||||
|
||||
gfxresourcepool resources;
|
||||
|
||||
@@ -235,7 +235,7 @@ public:
|
||||
/// \param layout The layout of the components in the pixel
|
||||
/// \param bytes The size of the image data in bytes, for compressed pixel formats
|
||||
texture(GLsizei size, GLsizei mips,
|
||||
const void* faces[6], GLenum component = BYTE, GLenum layout = R, GLsizei = 0) requires is_2d and cubemap
|
||||
const void* faces[6], GLenum component = BYTE, GLenum layout = R, GLsizei bytes = 0) requires is_2d and cubemap
|
||||
: _handle(NULL)
|
||||
, _width(size), _height(size), _depth(1)
|
||||
, _samples(1), _mips(mips) {
|
||||
@@ -247,6 +247,9 @@ public:
|
||||
glTexSubImage2D(base_cubemap_face + i, 0, 0, 0, _width, _height, layout, component, faces[i]);
|
||||
}
|
||||
} else if constexpr(compressed) {
|
||||
for (int i = 0; i < cubemap_faces; ++i) {
|
||||
glCompressedTexImage2D(base_cubemap_face + i, 0, format, _width, _height, 0, bytes, faces[i]);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < cubemap_faces; ++i) {
|
||||
glTexImage2D(base_cubemap_face + i, 0, format, _width, _height, 0, layout, component, faces[i]);
|
||||
@@ -266,7 +269,7 @@ public:
|
||||
///
|
||||
/// \details Requires OES_texture_cube_map_array
|
||||
texture(GLsizei size, GLsizei depth, GLsizei mips,
|
||||
const void* data, GLenum component = BYTE, GLenum layout = R, GLsizei = 0) requires is_2d and cubemap
|
||||
const void* data, GLenum component = BYTE, GLenum layout = R, GLsizei bytes = 0) requires is_2d and cubemap
|
||||
: _handle(NULL)
|
||||
, _width(size), _height(size), _depth(depth)
|
||||
, _samples(1), _mips(mips) {
|
||||
@@ -276,6 +279,9 @@ public:
|
||||
glTexStorage3D(type, _mips, format, _width, _height, _depth * 6);
|
||||
glTexSubImage3D(type, 0, 0, 0, 0, _width, _height, _depth * 6, layout, component, data);
|
||||
} else if constexpr(compressed) {
|
||||
for (int i = 0; i < cubemap_faces; ++i) {
|
||||
glCompressedTexImage3D(type, 0, format, _width, _height, _depth * 6, 0, bytes, data);
|
||||
}
|
||||
} else {
|
||||
glTexImage3D(type, 0, format, _width, _height, _depth * 6, 0, layout, component, data);
|
||||
}
|
||||
|
||||
@@ -102,6 +102,11 @@ private:
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
static type_data* get_data() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static type_data* get_data() requires(not is_void_v<T>) {
|
||||
auto& typelist = _typelist();
|
||||
@@ -118,11 +123,6 @@ public:
|
||||
return typelist[uuid].get();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
static type_data* get_data() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template<typename...Ts>
|
||||
static dynarray<type_data*> get_data(typelist<Ts...>) {
|
||||
return {
|
||||
|
||||
@@ -42,15 +42,15 @@ namespace fennec::detail
|
||||
template<typename FuncT, typename...ArgsT> using thread_proxy_t = tuple<FuncT, ArgsT...>;
|
||||
|
||||
template<typename FuncT, typename...ArgsT, size_t...I>
|
||||
void _execute_thread(thread_proxy_t<function<FuncT>, ArgsT...>& data, index_metasequence<I...>) {
|
||||
auto func = data.template get<0>();
|
||||
func(fennec::move(data.template get<I + 1>()) ...);
|
||||
void _execute_thread(thread_proxy_t<FuncT, ArgsT...>& data, index_metasequence<I...>) {
|
||||
auto func = fennec::move(fennec::get<0>(data));
|
||||
func(fennec::move(fennec::get<I + 1>(data)) ...);
|
||||
}
|
||||
|
||||
template<typename DataT>
|
||||
void* _run_thread(void* data) {
|
||||
unique_ptr<DataT> ptr(data);
|
||||
detail::_execute_thread(*ptr, make_index_metasequence_t<DataT::size - 1>{});
|
||||
unique_ptr<DataT> ptr(static_cast<DataT*>(data));
|
||||
detail::_execute_thread(*ptr.get(), make_index_metasequence_t<DataT::size - 1>{});
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
58
include/fennec/threading/lock_guard.h
Normal file
58
include/fennec/threading/lock_guard.h
Normal file
@@ -0,0 +1,58 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file lock_guard.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_THREADING_LOCK_GUARD_H
|
||||
#define FENNEC_THREADING_LOCK_GUARD_H
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
template<typename MutexT>
|
||||
class lock_guard {
|
||||
public:
|
||||
lock_guard() = delete;
|
||||
|
||||
explicit lock_guard(MutexT& mutex)
|
||||
: mutex(mutex) {
|
||||
mutex.lock();
|
||||
}
|
||||
|
||||
~lock_guard() {
|
||||
mutex.unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
MutexT& mutex;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_THREADING_LOCK_GUARD_H
|
||||
79
include/fennec/threading/mutex.h
Normal file
79
include/fennec/threading/mutex.h
Normal file
@@ -0,0 +1,79 @@
|
||||
// =====================================================================================================================
|
||||
// fennec, a free and open source game engine
|
||||
// Copyright © 2025 Medusa Slockbower
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
// =====================================================================================================================
|
||||
|
||||
///
|
||||
/// \file mutex.h
|
||||
/// \brief
|
||||
///
|
||||
///
|
||||
/// \details
|
||||
/// \author Medusa Slockbower
|
||||
///
|
||||
/// \copyright Copyright © 2025 Medusa Slockbower ([GPLv3](https://www.gnu.org/licenses/gpl-3.0.en.html))
|
||||
///
|
||||
///
|
||||
|
||||
|
||||
#ifndef FENNEC_THREADING_MUTEX_H
|
||||
#define FENNEC_THREADING_MUTEX_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <fennec/lang/assert.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
class mutex {
|
||||
public:
|
||||
using handle_t = pthread_mutex_t;
|
||||
|
||||
mutex()
|
||||
: _handle() {
|
||||
assertf((pthread_mutex_init(&_handle, nullptr) == 0), "Failed to initialize mutex.")
|
||||
}
|
||||
~mutex() {
|
||||
assertf((pthread_mutex_destroy(&_handle) == 0), "Failed to destroy mutex.");
|
||||
}
|
||||
|
||||
mutex(const mutex&) = delete;
|
||||
mutex& operator=(const mutex&) = delete;
|
||||
|
||||
void lock() {
|
||||
assertf(pthread_mutex_lock(&_handle) == 0, "Error on lock()!");
|
||||
}
|
||||
|
||||
bool try_lock() {
|
||||
int res = pthread_mutex_trylock(&_handle);
|
||||
assertf(res == EBUSY or res == 0, "Error on trylock()!");
|
||||
return res == 0;
|
||||
}
|
||||
|
||||
void unlock() {
|
||||
assertf(pthread_mutex_unlock(&_handle) == 0, "Error on unlock()!");
|
||||
}
|
||||
|
||||
private:
|
||||
pthread_mutex_t _handle;
|
||||
pthread_mutexattr_t _attr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENNEC_THREADING_MUTEX_H
|
||||
@@ -33,12 +33,13 @@
|
||||
#define FENNEC_THREADING_THREAD_H
|
||||
|
||||
#include <pthread.h>
|
||||
#include <fennec/format/formatter.h>
|
||||
#include <fennec/lang/type_traits.h>
|
||||
#include <fennec/lang/type_transforms.h>
|
||||
|
||||
#include <fennec/memory/bytes.h>
|
||||
|
||||
#include <fennec/threading/detail/_thread.h>
|
||||
|
||||
#include <fennec/format/formatter.h>
|
||||
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
@@ -47,7 +48,7 @@ private:
|
||||
static constexpr pthread_t nullthread = 0;
|
||||
|
||||
public:
|
||||
using native_handle_type = pthread_t;
|
||||
using handle_t = pthread_t;
|
||||
|
||||
class id {
|
||||
public:
|
||||
@@ -89,10 +90,13 @@ public:
|
||||
requires(not is_same_v<thread, remove_cvref_t<FuncT>>)
|
||||
thread(FuncT&& func, ArgsT&&...args)
|
||||
: _handle(nullthread) {
|
||||
using proxy_t = detail::thread_proxy_t<function<FuncT>, ArgsT...>;
|
||||
unique_ptr<proxy_t> proxy = fennec::make_unique<proxy_t>(fennec::forward<FuncT>(func), fennec::forward<ArgsT>(args)...);
|
||||
using proxy_t = detail::thread_proxy_t<decay_t<FuncT>, decay_t<ArgsT>...>;
|
||||
unique_ptr<proxy_t> proxy = fennec::make_unique<proxy_t>(
|
||||
fennec::decay_copy(fennec::forward<FuncT>(func)),
|
||||
fennec::decay_copy(fennec::forward<ArgsT>(args))...
|
||||
);
|
||||
|
||||
int_t res = ::pthread_create(&_handle, nullptr, &detail::_run_thread<proxy_t>(), proxy.get());
|
||||
int_t res = ::pthread_create(&_handle, nullptr, &detail::_run_thread<proxy_t>, proxy.get());
|
||||
assertf(res == 0, "Thread creation failed!");
|
||||
|
||||
proxy.release();
|
||||
@@ -108,7 +112,8 @@ public:
|
||||
}
|
||||
|
||||
~thread() {
|
||||
assertf(_handle == 0, "Attempted to destruct a running thread!");
|
||||
assertf(_handle == 0 or joinable(), "Attempted to destruct a running thread!");
|
||||
join();
|
||||
}
|
||||
|
||||
bool joinable() const noexcept {
|
||||
@@ -131,7 +136,7 @@ public:
|
||||
}
|
||||
|
||||
int_t res = pthread_detach(_handle);
|
||||
assertf(res == 0, "Failed to detatch thread!");
|
||||
assertf(res == 0, "Failed to detach thread!");
|
||||
_handle = nullthread;
|
||||
}
|
||||
|
||||
@@ -155,14 +160,14 @@ private:
|
||||
template <>
|
||||
struct hash<thread::id> {
|
||||
size_t operator()(thread::id v) const noexcept {
|
||||
return hash<thread::native_handle_type>()(v._id);
|
||||
return hash<thread::handle_t>()(v._id);
|
||||
}
|
||||
};
|
||||
|
||||
template<>
|
||||
struct formatter<thread::id> {
|
||||
string operator()(const format_arg& fmt, thread::id v) const noexcept {
|
||||
return formatter<thread::native_handle_type>()(fmt, v._id);
|
||||
return formatter<thread::handle_t>()(fmt, v._id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user