Merge commit '6eb4f090f98f6b17a23c57768c16b7716b6c9cbd' as 'third_party/libosmium'

This commit is contained in:
Patrick Niklaus
2017-08-30 09:30:27 +00:00
434 changed files with 81367 additions and 0 deletions
@@ -0,0 +1,124 @@
#ifndef OSMIUM_THREAD_FUNCTION_WRAPPER_HPP
#define OSMIUM_THREAD_FUNCTION_WRAPPER_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <memory>
#include <utility>
namespace osmium {
namespace thread {
/**
* This function wrapper can collect move-only functions unlike
* std::function which needs copyable functions.
* Taken from the book "C++ Concurrency in Action".
*/
class function_wrapper {
struct impl_base {
virtual ~impl_base() = default;
virtual bool call() {
return true;
}
}; // struct impl_base
std::unique_ptr<impl_base> impl;
template <typename F>
struct impl_type : impl_base {
F m_functor;
explicit impl_type(F&& functor) :
m_functor(std::forward<F>(functor)) {
}
bool call() override {
m_functor();
return false;
}
}; // struct impl_type
public:
// Constructor must not be "explicit" for wrapper
// to work seemlessly.
template <typename TFunction>
// cppcheck-suppress noExplicitConstructor
function_wrapper(TFunction&& f) :
impl(new impl_type<TFunction>(std::forward<TFunction>(f))) {
}
// The integer parameter is only used to signal that we want
// the special function wrapper that makes the worker thread
// shut down.
explicit function_wrapper(int) :
impl(new impl_base()) {
}
bool operator()() {
return impl->call();
}
function_wrapper() = default;
function_wrapper(function_wrapper&& other) :
impl(std::move(other.impl)) {
}
function_wrapper& operator=(function_wrapper&& other) {
impl = std::move(other.impl);
return *this;
}
function_wrapper(const function_wrapper&) = delete;
function_wrapper& operator=(const function_wrapper&) = delete;
~function_wrapper() = default;
explicit operator bool() const {
return static_cast<bool>(impl);
}
}; // class function_wrapper
} // namespace thread
} // namespace osmium
#endif // OSMIUM_THREAD_FUNCTION_WRAPPER_HPP
+214
View File
@@ -0,0 +1,214 @@
#ifndef OSMIUM_THREAD_POOL_HPP
#define OSMIUM_THREAD_POOL_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <cstddef>
#include <future>
#include <thread>
#include <type_traits>
#include <utility>
#include <vector>
#include <osmium/thread/function_wrapper.hpp>
#include <osmium/thread/queue.hpp>
#include <osmium/thread/util.hpp>
#include <osmium/util/config.hpp>
namespace osmium {
/**
* @brief Threading-related low-level code
*/
namespace thread {
namespace detail {
// Maximum number of allowed pool threads (just to keep the user
// from setting something silly).
constexpr const int max_pool_threads = 32;
inline int get_pool_size(int num_threads, int user_setting, unsigned hardware_concurrency) {
if (num_threads == 0) {
num_threads = user_setting ? user_setting : -2;
}
if (num_threads < 0) {
num_threads += int(hardware_concurrency);
}
if (num_threads < 1) {
num_threads = 1;
} else if (num_threads > max_pool_threads) {
num_threads = max_pool_threads;
}
return num_threads;
}
inline std::size_t get_work_queue_size() noexcept {
const std::size_t n = osmium::config::get_max_queue_size("WORK", 10);
return n > 2 ? n : 2;
}
} // namespace detail
/**
* Thread pool.
*/
class Pool {
/**
* This class makes sure all pool threads will be joined when
* the pool is destructed.
*/
class thread_joiner {
std::vector<std::thread>& m_threads;
public:
explicit thread_joiner(std::vector<std::thread>& threads) :
m_threads(threads) {
}
~thread_joiner() {
for (auto& thread : m_threads) {
if (thread.joinable()) {
thread.join();
}
}
}
}; // class thread_joiner
osmium::thread::Queue<function_wrapper> m_work_queue;
std::vector<std::thread> m_threads;
thread_joiner m_joiner;
int m_num_threads;
void worker_thread() {
osmium::thread::set_thread_name("_osmium_worker");
while (true) {
function_wrapper task;
m_work_queue.wait_and_pop(task);
if (task && task()) {
// The called tasks returns true only when the
// worker thread should shut down.
return;
}
}
}
public:
static constexpr int default_num_threads = 0;
static constexpr int default_queue_size = 0;
/**
* Create thread pool with the given number of threads. If
* num_threads is 0, the number of threads is read from
* the environment variable OSMIUM_POOL_THREADS. The default
* value in that case is -2.
*
* If the number of threads is a negative number, it will be
* set to the actual number of cores on the system plus the
* given number, ie it will leave a number of cores unused.
*
* In all cases the minimum number of threads in the pool is 1.
*
* If max_queue_size is 0, the queue size is read from
* the environment variable OSMIUM_MAX_WORK_QUEUE_SIZE.
*/
explicit Pool(int num_threads = default_num_threads, std::size_t max_queue_size = default_queue_size) :
m_work_queue(max_queue_size > 0 ? max_queue_size : detail::get_work_queue_size(), "work"),
m_threads(),
m_joiner(m_threads),
m_num_threads(detail::get_pool_size(num_threads, osmium::config::get_pool_threads(), std::thread::hardware_concurrency())) {
try {
for (int i = 0; i < m_num_threads; ++i) {
m_threads.push_back(std::thread(&Pool::worker_thread, this));
}
} catch (...) {
shutdown_all_workers();
throw;
}
}
static Pool& default_instance() {
static Pool pool{};
return pool;
}
void shutdown_all_workers() {
for (int i = 0; i < m_num_threads; ++i) {
// The special function wrapper makes a worker shut down.
m_work_queue.push(function_wrapper{0});
}
}
~Pool() {
shutdown_all_workers();
}
int num_threads() const noexcept {
return m_num_threads;
}
std::size_t queue_size() const {
return m_work_queue.size();
}
bool queue_empty() const {
return m_work_queue.empty();
}
template <typename TFunction>
std::future<typename std::result_of<TFunction()>::type> submit(TFunction&& func) {
using result_type = typename std::result_of<TFunction()>::type;
std::packaged_task<result_type()> task{std::forward<TFunction>(func)};
std::future<result_type> future_result{task.get_future()};
m_work_queue.push(std::move(task));
return future_result;
}
}; // class Pool
} // namespace thread
} // namespace osmium
#endif // OSMIUM_THREAD_POOL_HPP
+229
View File
@@ -0,0 +1,229 @@
#ifndef OSMIUM_THREAD_QUEUE_HPP
#define OSMIUM_THREAD_QUEUE_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <chrono>
#include <condition_variable>
#include <cstddef>
#include <mutex>
#include <queue>
#include <string>
#include <utility> // IWYU pragma: keep
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
# include <atomic>
# include <iostream>
#endif
namespace osmium {
namespace thread {
static const std::chrono::milliseconds max_wait{10};
/**
* A thread-safe queue.
*/
template <typename T>
class Queue {
/// Maximum size of this queue. If the queue is full pushing to
/// the queue will block.
const std::size_t m_max_size;
/// Name of this queue (for debugging only).
const std::string m_name;
mutable std::mutex m_mutex;
std::queue<T> m_queue;
/// Used to signal consumers when data is available in the queue.
std::condition_variable m_data_available;
/// Used to signal producers when queue is not full.
std::condition_variable m_space_available;
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
/// The largest size the queue has been so far.
std::size_t m_largest_size;
/// The number of times push() was called on the queue.
std::atomic<int> m_push_counter;
/// The number of times the queue was full and a thread pushing
/// to the queue was blocked.
std::atomic<int> m_full_counter;
/**
* The number of times wait_and_pop(with_timeout)() was called
* on the queue.
*/
std::atomic<int> m_pop_counter;
/// The number of times the queue was full and a thread pushing
/// to the queue was blocked.
std::atomic<int> m_empty_counter;
#endif
public:
/**
* Construct a multithreaded queue.
*
* @param max_size Maximum number of elements in the queue. Set to
* 0 for an unlimited size.
* @param name Optional name for this queue. (Used for debugging.)
*/
explicit Queue(std::size_t max_size = 0, const std::string& name = "") :
m_max_size(max_size),
m_name(name),
m_mutex(),
m_queue(),
m_data_available(),
m_space_available()
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
,
m_largest_size(0),
m_push_counter(0),
m_full_counter(0),
m_pop_counter(0),
m_empty_counter(0)
#endif
{
}
~Queue() {
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
std::cerr << "queue '" << m_name
<< "' with max_size=" << m_max_size
<< " had largest size " << m_largest_size
<< " and was full " << m_full_counter
<< " times in " << m_push_counter
<< " push() calls and was empty " << m_empty_counter
<< " times in " << m_pop_counter
<< " pop() calls\n";
#endif
}
/**
* Push an element onto the queue. If the queue has a max size,
* this call will block if the queue is full.
*/
void push(T value) {
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
++m_push_counter;
#endif
if (m_max_size) {
while (size() >= m_max_size) {
std::unique_lock<std::mutex> lock{m_mutex};
m_space_available.wait_for(lock, max_wait, [this] {
return m_queue.size() < m_max_size;
});
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
++m_full_counter;
#endif
}
}
std::lock_guard<std::mutex> lock{m_mutex};
m_queue.push(std::move(value));
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
if (m_largest_size < m_queue.size()) {
m_largest_size = m_queue.size();
}
#endif
m_data_available.notify_one();
}
void wait_and_pop(T& value) {
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
++m_pop_counter;
#endif
std::unique_lock<std::mutex> lock{m_mutex};
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
if (m_queue.empty()) {
++m_empty_counter;
}
#endif
m_data_available.wait(lock, [this] {
return !m_queue.empty();
});
if (!m_queue.empty()) {
value = std::move(m_queue.front());
m_queue.pop();
lock.unlock();
if (m_max_size) {
m_space_available.notify_one();
}
}
}
bool try_pop(T& value) {
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
++m_pop_counter;
#endif
{
std::lock_guard<std::mutex> lock{m_mutex};
if (m_queue.empty()) {
#ifdef OSMIUM_DEBUG_QUEUE_SIZE
++m_empty_counter;
#endif
return false;
}
value = std::move(m_queue.front());
m_queue.pop();
}
if (m_max_size) {
m_space_available.notify_one();
}
return true;
}
bool empty() const {
std::lock_guard<std::mutex> lock{m_mutex};
return m_queue.empty();
}
std::size_t size() const {
std::lock_guard<std::mutex> lock{m_mutex};
return m_queue.size();
}
}; // class Queue
} // namespace thread
} // namespace osmium
#endif // OSMIUM_THREAD_QUEUE_HPP
+118
View File
@@ -0,0 +1,118 @@
#ifndef OSMIUM_THREAD_UTIL_HPP
#define OSMIUM_THREAD_UTIL_HPP
/*
This file is part of Osmium (http://osmcode.org/libosmium).
Copyright 2013-2017 Jochen Topf <jochen@topf.org> and others (see README).
Boost Software License - Version 1.0 - August 17th, 2003
Permission is hereby granted, free of charge, to any person or organization
obtaining a copy of the software and accompanying documentation covered by
this license (the "Software") to use, reproduce, display, distribute,
execute, and transmit the Software, and to prepare derivative works of the
Software, and to permit third-parties to whom the Software is furnished to
do so, all subject to the following:
The copyright notices in the Software and this entire statement, including
the above license grant, this restriction and the following disclaimer,
must be included in all copies of the Software, in whole or in part, and
all derivative works of the Software, unless such copies or derivative
works are solely in the form of machine-executable object code generated by
a source language processor.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <chrono>
#include <future>
#include <thread>
#include <utility>
#ifdef __linux__
# include <sys/prctl.h>
#endif
namespace osmium {
namespace thread {
/**
* Check if the future resulted in an exception. This will re-throw
* the exception stored in the future if there was one. Otherwise it
* will just return.
*/
template <typename T>
inline void check_for_exception(std::future<T>& future) {
if (future.valid() && future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
future.get();
}
}
/**
* Wait until the given future becomes ready. Will block if the future
* is not ready. Can be called more than once unlike future.get().
*/
template <typename T>
inline void wait_until_done(std::future<T>& future) {
if (future.valid()) {
future.get();
}
}
/**
* Set name of current thread for debugging. This only works on Linux.
*/
#ifdef __linux__
inline void set_thread_name(const char* name) noexcept {
prctl(PR_SET_NAME, name, 0, 0, 0);
}
#else
inline void set_thread_name(const char*) noexcept {
// intentionally left blank
}
#endif
class thread_handler {
std::thread m_thread;
public:
thread_handler() :
m_thread() {
}
template <typename TFunction, typename... TArgs>
explicit thread_handler(TFunction&& f, TArgs&&... args) :
m_thread(std::forward<TFunction>(f), std::forward<TArgs>(args)...) {
}
thread_handler(const thread_handler&) = delete;
thread_handler& operator=(const thread_handler&) = delete;
thread_handler(thread_handler&&) = default;
thread_handler& operator=(thread_handler&&) = default;
~thread_handler() {
if (m_thread.joinable()) {
m_thread.join();
}
}
}; // class thread_handler
} // namespace thread
} // namespace osmium
#endif // OSMIUM_THREAD_UTIL_HPP