Add support for C++ std::thread.

Overwrites bits/gthr-default.h to implement gthreads.
This commit is contained in:
James Benton 2018-05-25 17:21:59 +01:00
parent a5a1bac28c
commit a9829a3226
7 changed files with 599 additions and 0 deletions

View File

@ -5,3 +5,4 @@ add_subdirectory(libdefaultheap)
add_subdirectory(libgfd)
add_subdirectory(libwhb)
add_subdirectory(wutnewlib)
add_subdirectory(wutstdc++)

View File

@ -0,0 +1,17 @@
cmake_minimum_required(VERSION 3.2)
project(wutstdc++ CXX)
add_library(wutstdc++
thread.cc)
target_compile_definitions(wutstdc++
PRIVATE _GLIBCXX_HAS_GTHREADS)
target_include_directories(wutstdc++
PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include" "${WUT_ROOT}/include")
install(TARGETS wutstdc++
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib")
install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/include/"
DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
FILES_MATCHING PATTERN "*.h*")

View File

@ -0,0 +1,288 @@
/* Threads compatibility routines for libgcc2 and libobjc. */
/* Compile this one with gcc. */
/* Copyright (C) 1997-2016 Free Software Foundation, Inc.
This file is part of GCC.
GCC 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, or (at your option) any later
version.
GCC 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.
Under Section 7 of GPL version 3, you are granted additional
permissions described in the GCC Runtime Library Exception, version
3.1, as published by the Free Software Foundation.
You should have received a copy of the GNU General Public License and
a copy of the GCC Runtime Library Exception along with this program;
see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
<http://www.gnu.org/licenses/>. */
#ifndef _GLIBCXX_GCC_GTHR_SINGLE_H
#define _GLIBCXX_GCC_GTHR_SINGLE_H
#define __GTHREADS 1
#define __GTHREADS_CXX0X 1
#include <coreinit/condition.h>
#include <coreinit/thread.h>
#include <coreinit/mutex.h>
#include <coreinit/baseheap.h>
#include <coreinit/expandedheap.h>
#include <string.h>
#include <sys/errno.h>
#define _GTHREAD_USE_MUTEX_TIMEDLOCK 0
typedef OSThread *__gthread_t;
typedef uint32_t __gthread_once_t;
typedef OSMutex __gthread_mutex_t;
typedef OSMutex __gthread_recursive_mutex_t;
typedef OSCondition __gthread_cond_t;
typedef struct timespec __gthread_time_t;
// Unimplemented
typedef void *__gthread_key_t;
#define __GTHREAD_HAS_COND 1
#define __GTHREAD_ONCE_INIT { }
#define __GTHREAD_MUTEX_INIT_FUNCTION __gthread_mutex_init_function
#define __GTHREAD_RECURSIVE_MUTEX_INIT_FUNCTION __gthread_recursive_mutex_init_function
#define __GTHREAD_COND_INIT_FUNCTION __gthread_cond_init_function
#define __GTHREAD_TIME_INIT { 0, 0 }
#define __GTHREAD_STACK_SIZE (4096*1024)
static inline int
__gthread_active_p (void)
{
return 1;
}
static inline void
__gthread_thread_deallocator(OSThread *thread, void *stack)
{
MEMExpandedHeap *heap = (MEMExpandedHeap *)MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM2);
MEMFreeToExpHeap(heap, thread);
MEMFreeToExpHeap(heap, stack);
}
static inline int
__gthread_create (__gthread_t *__threadid, void *(*__func) (void*),
void *__args)
{
MEMExpandedHeap *heap = (MEMExpandedHeap *)MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM2);
OSThread *thread = (OSThread *)MEMAllocFromExpHeapEx(heap, sizeof(OSThread), 8);
char *stack = (char *)MEMAllocFromExpHeapEx(heap, __GTHREAD_STACK_SIZE, 8);
memset(thread, 0, sizeof(OSThread));
if (!OSCreateThread(thread,
(OSThreadEntryPointFn)__func,
(int)__args,
NULL,
stack + __GTHREAD_STACK_SIZE,
__GTHREAD_STACK_SIZE,
16,
OS_THREAD_ATTRIB_AFFINITY_ANY)) {
MEMFreeToExpHeap((MEMExpandedHeap*)MEMGetBaseHeapHandle(MEM_BASE_HEAP_MEM2), thread);
return EINVAL;
}
*__threadid = thread;
OSSetThreadDeallocator(thread, &__gthread_thread_deallocator);
OSResumeThread(thread);
return 0;
}
static inline int
__gthread_join (__gthread_t __threadid, void **__value_ptr)
{
if (!OSJoinThread(__threadid, (int *)__value_ptr)) {
return EINVAL;
}
return 0;
}
static inline int
__gthread_detach (__gthread_t __threadid)
{
OSDetachThread(__threadid);
return 0;
}
static inline int
__gthread_equal (__gthread_t __t1, __gthread_t __t2)
{
return __t1 == __t2;
}
static inline __gthread_t
__gthread_self (void)
{
return OSGetCurrentThread();
}
static inline int
__gthread_yield (void)
{
OSYieldThread();
return 0;
}
static inline int
__gthread_once (__gthread_once_t *__once, void (*__func) (void))
{
// TODO: Implement __gthread_once
return -1;
}
static inline int
__gthread_key_create (__gthread_key_t *__key, void (*__dtor) (void *))
{
// TODO: Implement __gthread_key_create
return -1;
}
static inline int
__gthread_key_delete (__gthread_key_t __key)
{
// TODO: Implement __gthread_key_delete
return -1;
}
static inline void *
__gthread_getspecific (__gthread_key_t __key)
{
// TODO: Implement __gthread_getspecific
return NULL;
}
static inline int
__gthread_setspecific (__gthread_key_t __key, const void *__ptr)
{
// TODO: Implement __gthread_setspecific
return -1;
}
static inline void
__gthread_mutex_init_function (__gthread_mutex_t *__mutex)
{
OSInitMutex(__mutex);
}
static inline int
__gthread_mutex_destroy (__gthread_mutex_t *__mutex)
{
return 0;
}
static inline int
__gthread_mutex_lock (__gthread_mutex_t *__mutex)
{
OSLockMutex(__mutex);
return 0;
}
static inline int
__gthread_mutex_trylock (__gthread_mutex_t *__mutex)
{
if (!OSTryLockMutex(__mutex)) {
return -1;
}
return 0;
}
static inline int
__gthread_mutex_unlock (__gthread_mutex_t *__mutex)
{
OSUnlockMutex(__mutex);
return 0;
}
static inline int
__gthread_recursive_mutex_init_function (__gthread_recursive_mutex_t *__mutex)
{
OSInitMutex(__mutex);
return 0;
}
static inline int
__gthread_recursive_mutex_lock (__gthread_recursive_mutex_t *__mutex)
{
OSLockMutex(__mutex);
return 0;
}
static inline int
__gthread_recursive_mutex_trylock (__gthread_recursive_mutex_t *__mutex)
{
if (!OSTryLockMutex(__mutex)) {
return -1;
}
return 0;
}
static inline int
__gthread_recursive_mutex_unlock (__gthread_recursive_mutex_t *__mutex)
{
OSUnlockMutex(__mutex);
return 0;
}
static inline int
__gthread_recursive_mutex_destroy (__gthread_recursive_mutex_t *__mutex)
{
return 0;
}
static inline void
__gthread_cond_init_function (__gthread_cond_t *__cond)
{
OSInitCond(__cond);
}
static inline int
__gthread_cond_broadcast (__gthread_cond_t *__cond)
{
OSSignalCond(__cond);
return 0;
}
static inline int
__gthread_cond_signal (__gthread_cond_t *__cond)
{
OSSignalCond(__cond);
return 0;
}
static inline int
__gthread_cond_wait (__gthread_cond_t *__cond, __gthread_mutex_t *__mutex)
{
OSWaitCond(__cond, __mutex);
return 0;
}
static inline int
__gthread_cond_wait_recursive (__gthread_cond_t *__cond,
__gthread_recursive_mutex_t *__mutex)
{
OSWaitCond(__cond, __mutex);
return 0;
}
static inline int
__gthread_cond_destroy (__gthread_cond_t* __cond)
{
return 0;
}
#endif /* ! _GLIBCXX_GCC_GTHR_SINGLE_H */

View File

@ -0,0 +1,218 @@
// thread -*- C++ -*-
// Copyright (C) 2008-2018 Free Software Foundation, Inc.
//
// This file is part of the GNU ISO C++ Library. This library 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, or (at your option)
// any later version.
// This library 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.
// Under Section 7 of GPL version 3, you are granted additional
// permissions described in the GCC Runtime Library Exception, version
// 3.1, as published by the Free Software Foundation.
// You should have received a copy of the GNU General Public License and
// a copy of the GCC Runtime Library Exception along with this program;
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
// <http://www.gnu.org/licenses/>.
#define _GLIBCXX_THREAD_ABI_COMPAT 1
#include <thread>
#include <system_error>
#include <cerrno>
#include <bits/cxxabi_forced.h>
#if defined(_GLIBCXX_HAS_GTHREADS) && defined(_GLIBCXX_USE_C99_STDINT_TR1)
#if defined(_GLIBCXX_USE_GET_NPROCS)
# include <sys/sysinfo.h>
# define _GLIBCXX_NPROCS get_nprocs()
#elif defined(_GLIBCXX_USE_PTHREADS_NUM_PROCESSORS_NP)
# define _GLIBCXX_NPROCS pthread_num_processors_np()
#elif defined(_GLIBCXX_USE_SYSCTL_HW_NCPU)
# include <stddef.h>
# include <sys/sysctl.h>
static inline int get_nprocs()
{
int count;
size_t size = sizeof(count);
int mib[] = { CTL_HW, HW_NCPU };
if (!sysctl(mib, 2, &count, &size, NULL, 0))
return count;
return 0;
}
# define _GLIBCXX_NPROCS get_nprocs()
#elif defined(_GLIBCXX_USE_SC_NPROCESSORS_ONLN)
# include <unistd.h>
# define _GLIBCXX_NPROCS sysconf(_SC_NPROCESSORS_ONLN)
#elif defined(_GLIBCXX_USE_SC_NPROC_ONLN)
# include <unistd.h>
# define _GLIBCXX_NPROCS sysconf(_SC_NPROC_ONLN)
#else
# define _GLIBCXX_NPROCS 0
#endif
#ifndef _GLIBCXX_USE_NANOSLEEP
# ifdef _GLIBCXX_HAVE_SLEEP
# include <unistd.h>
# elif defined(_GLIBCXX_HAVE_WIN32_SLEEP)
# include <windows.h>
# else
# error "No sleep function known for this target"
# endif
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
extern "C"
{
static void*
execute_native_thread_routine(void* __p)
{
thread::_State_ptr __t{ static_cast<thread::_State*>(__p) };
__t->_M_run();
return nullptr;
}
#if _GLIBCXX_THREAD_ABI_COMPAT
static void*
execute_native_thread_routine_compat(void* __p)
{
thread::_Impl_base* __t = static_cast<thread::_Impl_base*>(__p);
thread::__shared_base_type __local;
// Now that a new thread has been created we can transfer ownership of
// the thread state to a local object, breaking the reference cycle
// created in thread::_M_start_thread.
__local.swap(__t->_M_this_ptr);
__t->_M_run();
return nullptr;
}
#endif
} // extern "C"
_GLIBCXX_BEGIN_NAMESPACE_VERSION
thread::_State::~_State() = default;
void
thread::join()
{
int __e = EINVAL;
if (_M_id != id())
__e = __gthread_join(_M_id._M_thread, 0);
if (__e)
__throw_system_error(__e);
_M_id = id();
}
void
thread::detach()
{
int __e = EINVAL;
if (_M_id != id())
__e = __gthread_detach(_M_id._M_thread);
if (__e)
__throw_system_error(__e);
_M_id = id();
}
void
thread::_M_start_thread(_State_ptr state, void (*)())
{
const int err = __gthread_create(&_M_id._M_thread,
&execute_native_thread_routine,
state.get());
if (err)
__throw_system_error(err);
state.release();
}
#if _GLIBCXX_THREAD_ABI_COMPAT
void
thread::_M_start_thread(__shared_base_type __b)
{
if (!__gthread_active_p())
#if __cpp_exceptions
throw system_error(make_error_code(errc::operation_not_permitted),
"Enable multithreading to use std::thread");
#else
__throw_system_error(int(errc::operation_not_permitted));
#endif
_M_start_thread(std::move(__b), nullptr);
}
void
thread::_M_start_thread(__shared_base_type __b, void (*)())
{
auto ptr = __b.get();
// Create a reference cycle that will be broken in the new thread.
ptr->_M_this_ptr = std::move(__b);
int __e = __gthread_create(&_M_id._M_thread,
&execute_native_thread_routine_compat, ptr);
if (__e)
{
ptr->_M_this_ptr.reset(); // break reference cycle, destroying *ptr.
__throw_system_error(__e);
}
}
#endif
unsigned int
thread::hardware_concurrency() noexcept
{
return 3;
}
namespace this_thread
{
void
__sleep_for(chrono::seconds __s, chrono::nanoseconds __ns)
{
#ifdef _GLIBCXX_USE_NANOSLEEP
__gthread_time_t __ts =
{
static_cast<std::time_t>(__s.count()),
static_cast<long>(__ns.count())
};
while (::nanosleep(&__ts, &__ts) == -1 && errno == EINTR)
{ }
#elif defined(_GLIBCXX_HAVE_SLEEP)
# ifdef _GLIBCXX_HAVE_USLEEP
::sleep(__s.count());
if (__ns.count() > 0)
{
long __us = __ns.count() / 1000;
if (__us == 0)
__us = 1;
::usleep(__us);
}
# else
::sleep(__s.count() + (__ns.count() >= 1000000));
# endif
#elif defined(_GLIBCXX_HAVE_WIN32_SLEEP)
unsigned long ms = __ns.count() / 1000000;
if (__ns.count() > 0 && ms == 0)
ms = 1;
::Sleep(chrono::milliseconds(__s).count() + ms);
#endif
}
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std
#endif // _GLIBCXX_HAS_GTHREADS && _GLIBCXX_USE_C99_STDINT_TR1

View File

@ -0,0 +1,19 @@
cmake_minimum_required(VERSION 3.2)
project(helloworld_std_thread CXX)
include("${WUT_ROOT}/share/wut.cmake" REQUIRED)
add_executable(helloworld_std_thread
main.cpp)
target_link_libraries(helloworld_std_thread
whb
defaultheap
coreinit
proc_ui
sysapp)
wut_enable_stdcpp(helloworld_std_thread)
wut_create_rpx(helloworld_std_thread.rpx helloworld_std_thread)
install(FILES "${CMAKE_CURRENT_BINARY_DIR}/helloworld_std_thread.rpx"
DESTINATION "${CMAKE_INSTALL_PREFIX}")

View File

@ -0,0 +1,45 @@
#include <coreinit/thread.h>
#include <coreinit/time.h>
#include <coreinit/systeminfo.h>
#include <whb/proc.h>
#include <whb/log.h>
#include <whb/log_console.h>
#include <thread>
int
hello_thread()
{
OSCalendarTime tm;
WHBProcInit();
WHBLogConsoleInit();
WHBLogPrintf("Hello World from a std::thread!");
while(WHBProcIsRunning()) {
OSTicksToCalendarTime(OSGetTime(), &tm);
WHBLogPrintf("%02d/%02d/%04d %02d:%02d:%02d I'm still here.",
tm.tm_mday, tm.tm_mon, tm.tm_year,
tm.tm_hour, tm.tm_min, tm.tm_sec);
WHBLogConsoleDraw();
OSSleepTicks(OSMilliseconds(1000));
}
WHBLogPrintf("Exiting... good bye.");
WHBLogConsoleDraw();
OSSleepTicks(OSMilliseconds(1000));
WHBLogConsoleFree();
WHBProcShutdown();
return 0;
}
int
main(int argc, char **argv)
{
std::thread t(hello_thread);
t.join();
return 0;
}

View File

@ -1,5 +1,16 @@
cmake_minimum_required(VERSION 3.2)
macro(wut_enable_stdcpp target)
target_link_libraries(${target}
wutstdc++)
target_compile_definitions(${target}
PRIVATE _GLIBCXX_HAS_GTHREADS)
set_target_properties(${target} PROPERTIES
COMPILE_FLAGS "-std=c++17")
endmacro(wut_enable_stdcpp)
macro(wut_create_rpx target source)
target_link_libraries(${source}
wutnewlib