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 */
	template
	void 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);