Jonathan Wakely
6cf0040fff
libstdc++: Improve std::lock algorithm
The current std::lock algorithm is the one called "persistent" in Howard Hinnant's https://howardhinnant.github.io/dining_philosophers.html post. While it tends to perform acceptably fast, it wastes a lot of CPU cycles by continuously locking and unlocking the uncontended mutexes. Effectively, it's a spin lock with no back-off. This replaces it with the one Howard calls "smart and polite". It's smart, because when a Mi.try_lock() call fails because mutex Mi is contended, the algorithm reorders the mutexes until Mi is first, then calls Mi.lock(), to block until Mi is no longer contended. It's polite because it uses std::this_thread::yield() between the failed Mi.try_lock() call and the Mi.lock() call. (In reality it uses __gthread_yield() directly, because using this_thread::yield() would require shuffling code around to avoid a circular dependency.) This version of the algorithm is inspired by some hints from Howard, so that it has strictly bounded stack usage. As the comment in the code says: // This function can recurse up to N levels deep, for N = 1+sizeof...(L1). // On each recursion the lockables are rotated left one position, // e.g. depth 0: l0, l1, l2; depth 1: l1, l2, l0; depth 2: l2, l0, l1. // When a call to l_i.try_lock() fails it recurses/returns to depth=i // so that l_i is the first argument, and then blocks until l_i is locked. The 'i' parameter is the desired permuation of the lockables, and the 'depth' parameter is the depth in the call stack of the current instantiation of the function template. If i == depth then the function calls l0.lock() and then l1.try_lock()... for each lockable in the parameter pack l1. If i > depth then the function rotates the lockables to the left one place, and calls itself again to go one level deeper. Finally, if i < depth then the function returns to a shallower depth, equivalent to a right rotate of the lockables. When a call to try_lock() fails, i is set to the index of the contended lockable, so that the next call to l0.lock() will use the contended lockable as l0. This commit also replaces the std::try_lock implementation details. The new code is identical in behaviour, but uses a pair of constrained function templates. This avoids instantiating a class template, and is a litle simpler to call where used in std::__detail::__lock_impl and std::try_lock. Signed-off-by: Jonathan Wakely <jwakely@redhat.com> libstdc++-v3/ChangeLog: * include/std/mutex (__try_to_lock): Move to __detail namespace. (struct __try_lock_impl): Replace with ... (__detail::__try_lock_impl<Idx>(tuple<Lockables...>&)): New function templates to implement std::try_lock. (try_lock): Use new __try_lock_impl. (__detail::__lock_impl(int, int&, L0&, L1&...)): New function template to implement std::lock. (lock): Use __lock_impl.
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
…
This directory contains the GNU Compiler Collection (GCC). The GNU Compiler Collection is free software. See the files whose names start with COPYING for copying permission. The manuals, and some of the runtime libraries, are under different terms; see the individual source files for details. The directory INSTALL contains copies of the installation information as HTML and plain text. The source of this information is gcc/doc/install.texi. The installation information includes details of what is included in the GCC sources and what files GCC installs. See the file gcc/doc/gcc.texi (together with other files that it includes) for usage and porting information. An online readable version of the manual is in the files gcc/doc/gcc.info*. See http://gcc.gnu.org/bugs/ for how to report bugs usefully. Copyright years on GCC source files may be listed using range notation, e.g., 1987-2012, indicating that every year in the range, inclusive, is a copyrightable year that could otherwise be listed individually.
Description
Languages
C
48%
Ada
18.3%
C++
14.1%
Go
7%
GCC Machine Description
4.6%
Other
7.7%