| // thread -*- C++ -*- |
| |
| // Copyright (C) 2008-2021 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 <memory> // include this first so <thread> can use shared_ptr |
| #include <thread> |
| #include <system_error> |
| #include <cerrno> |
| #include <cxxabi_forced.h> |
| |
| #ifndef _GLIBCXX_USE_NANOSLEEP |
| # ifdef _GLIBCXX_HAVE_SLEEP |
| # include <unistd.h> |
| # elif defined(_GLIBCXX_HAVE_WIN32_SLEEP) |
| # include <windows.h> |
| # elif defined _GLIBCXX_NO_SLEEP && defined _GLIBCXX_HAS_GTHREADS |
| // We expect to be able to sleep for targets that support multiple threads: |
| # error "No sleep function known for this target" |
| # endif |
| #endif |
| |
| #ifdef _GLIBCXX_HAS_GTHREADS |
| |
| #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 |
| |
| 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 (*)()) |
| { |
| if (!__gthread_active_p()) |
| { |
| #if __cpp_exceptions |
| throw system_error(make_error_code(errc::operation_not_permitted), |
| "Enable multithreading to use std::thread"); |
| #else |
| __builtin_abort(); |
| #endif |
| } |
| |
| 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 |
| { |
| int __n = _GLIBCXX_NPROCS; |
| if (__n < 0) |
| __n = 0; |
| return __n; |
| } |
| |
| _GLIBCXX_END_NAMESPACE_VERSION |
| } // namespace std |
| |
| #endif // _GLIBCXX_HAS_GTHREADS |
| |
| #ifndef _GLIBCXX_NO_SLEEP |
| namespace std _GLIBCXX_VISIBILITY(default) |
| { |
| _GLIBCXX_BEGIN_NAMESPACE_VERSION |
| namespace this_thread |
| { |
| void |
| __sleep_for(chrono::seconds __s, chrono::nanoseconds __ns) |
| { |
| #ifdef _GLIBCXX_USE_NANOSLEEP |
| struct ::timespec __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) |
| const auto target = chrono::steady_clock::now() + __s + __ns; |
| while (true) |
| { |
| unsigned secs = __s.count(); |
| if (__ns.count() > 0) |
| { |
| # ifdef _GLIBCXX_HAVE_USLEEP |
| long us = __ns.count() / 1000; |
| if (us == 0) |
| us = 1; |
| ::usleep(us); |
| # else |
| if (__ns.count() > 1000000 || secs == 0) |
| ++secs; // No sub-second sleep function, so round up. |
| # endif |
| } |
| |
| if (secs > 0) |
| { |
| // Sleep in a loop to handle interruption by signals: |
| while ((secs = ::sleep(secs))) |
| { } |
| } |
| const auto now = chrono::steady_clock::now(); |
| if (now >= target) |
| break; |
| __s = chrono::duration_cast<chrono::seconds>(target - now); |
| __ns = chrono::duration_cast<chrono::nanoseconds>(target - (now + __s)); |
| } |
| #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 // ! NO_SLEEP |