WL#9359: InnoDB: Move from homebrew thread management to std::thread
Affects: Server-8.0
—
Status: Complete
From v8.0 we support the C++11 standard. The C++11 standard supports threading. We should leverage the std::thread library and get rid of our own homebrew. This will reduce code support and allow more idiomatic use. e.g., lambdas and task based parallelism.
This is an internal change within InnoDB. It will not affect users or result in changed behaviour that is observable by end users.
Split the os0thread.h file into two: 1. os0thread.h 2. os0thread-create.h The split is required for external utilities only. We already have os/thread.h which breaks our file naming rules. Write wrappers to handle old interfaces and new functionality. Do all the PFS and Server register/deregister handling on creation. The callable should not have to worry about these things. Get rid of OS specific #defines ---------------------os0thread.h------------------- #ifdef HAVE_PSI_INTERFACE /** Define for performance schema registration key */ using mysql_pfs_key_t = unsigned int; #endif /* HAVE_PSI_INTERFACE */ /** Operating system thread native handle */ using os_thread_id_t = std::thread::native_handle_type; /** Returns the thread identifier of current thread. Currently the thread identifier in Unix is the thread handle itself. @return current thread native handle */ os_thread_id_t os_thread_get_curr_id(); /** Compares two thread ids for equality. @param[in] lhs OS thread or thread id @param[in] rhs OS thread or thread id return true if equal */ #define os_thread_eq(lhs, rhs) ((lhs) == (rhs)) /** Advises the OS to give up remainder of the thread's time slice. */ #define os_thread_yield() \ do { \ std::this_thread::yield(); \ } while(false) /** The thread sleeps at least the time given in microseconds. @param[in] tm time in microseconds */ #define os_thread_sleep(usecs) \ do { \ std::this_thread::sleep_for(std::chrono::microseconds(usecs)); \ } while(false) ----------------------os0thread-create.h---------------------- /** Maximum number of threads inside InnoDB */ extern ulint srv_max_n_threads; /** Number of threads active. */ extern std::atomic_int os_thread_count; /** Initializes OS thread management data structures. */ inline void os_thread_open() { /* No op */ } /** Frees OS thread management data structures. */ inline void os_thread_close() { if (os_thread_count.load(std::memory_order_relaxed) != 0) { ib::warn() << "Some (" << os_thread_count.load(std::memory_order_relaxed) << ") threads are still active"; } } /** Check if there are threads active. @return true if the thread count > 0. */ inline bool os_thread_any_active() { return(os_thread_count.load(std::memory_order_relaxed) > 0); } /** Wrapper for a callable, it will count the number of registered Runnable instances and will register the thread executing the callable with the PFS and the Server threading infrastructure. */ class Runnable { public: #ifdef UNIV_PFS_THREAD /** Constructor for the Runnable object. @param[in] pfs_key Performance schema key */ explicit Runnable(mysql_pfs_key_t pfs_key) : m_pfs_key(pfs_key) { } #else explicit Runnable(mysql_pfs_key_t) { } #endif /* UNIV_PFS_THREAD */ public: /** Method to execute the callable @param[in] F Callable object @param[in] args Variable number of args to F */ templatevoid operator()(F&& f, Args&& ... args) { preamble(); auto task = std::bind( std::forward (f), std::forward (args) ...); task(); epilogue(); } private: /** Register the thread with the server */ void preamble() { my_thread_init(); #ifdef UNIV_PFS_THREAD PSI_thread* psi; psi = PSI_THREAD_CALL(new_thread)(m_pfs_key, nullptr, 0); PSI_THREAD_CALL(set_thread_os_id)(psi); PSI_THREAD_CALL(set_thread)(psi); #endif /* UNIV_PFS_THREAD */ std::atomic_thread_fence(std::memory_order_release);; int old; old = os_thread_count.fetch_add(1, std::memory_order_relaxed); ut_a(old <= static_cast (srv_max_n_threads) - 1); } /** Deregister the thread */ void epilogue() { std::atomic_thread_fence(std::memory_order_release);; int old; old = os_thread_count.fetch_sub(1, std::memory_order_relaxed); ut_a(old > 0); my_thread_end(); #ifdef UNIV_PFS_THREAD PSI_THREAD_CALL(delete_current_thread)(); #endif /* UNIV_PFS_THREAD */ } private: #ifdef UNIV_PFS_THREAD /** Performance schema key */ const mysql_pfs_key_t m_pfs_key; #endif /* UNIV_PFS_THREAD */ }; /** Create a detached thread @param[in] pfs_key Performance schema thread key @param[in] f Callable instance @param[in] args zero or more args */ template void create_thread(mysql_pfs_key_t pfs_key, F&& f, Args&& ... args) { Runnable runnable(pfs_key); std::thread t(runnable, f, args ...); t.detach(); } Example usage: create_thread(some_pfs_key, some_function) create_thread(some_pfs_key, some_function, 1, "abc", ptr);
Copyright (c) 2000, 2024, Oracle Corporation and/or its affiliates. All rights reserved.