398 lines
12 KiB
C++
398 lines
12 KiB
C++
#ifndef _QTL_ASIO_H_
|
|
#define _QTL_ASIO_H_
|
|
|
|
#include "qtl_async.hpp"
|
|
#include <asio/version.hpp>
|
|
#define ASIO_STANDALONE
|
|
#if defined(_MSC_VER) && _WIN32_WINNT<0x0600
|
|
#define ASIO_ENABLE_CANCELIO 1
|
|
#endif
|
|
#if ASIO_VERSION < 101200
|
|
#include <asio/io_service.hpp>
|
|
#else
|
|
#include <asio/io_context.hpp>
|
|
#endif // ASIO_VERSION
|
|
#include <asio/strand.hpp>
|
|
#include <asio/ip/tcp.hpp>
|
|
#include <asio/async_result.hpp>
|
|
#include <asio/steady_timer.hpp>
|
|
|
|
#if ASIO_VERSION < 100000
|
|
#error The asio version required by QTL is at least 10.0
|
|
#endif
|
|
|
|
namespace qtl
|
|
{
|
|
|
|
namespace NS_ASIO = ::asio;
|
|
|
|
namespace asio
|
|
{
|
|
|
|
class service
|
|
{
|
|
public:
|
|
#if ASIO_VERSION < 101200
|
|
typedef NS_ASIO::io_service service_type;
|
|
#else
|
|
typedef NS_ASIO::io_context service_type;
|
|
#endif // ASIO_VERSION
|
|
|
|
service() NOEXCEPT { }
|
|
explicit service(int concurrency_hint) : _service(concurrency_hint) { }
|
|
|
|
void reset()
|
|
{
|
|
_service.reset();
|
|
}
|
|
|
|
void run()
|
|
{
|
|
_service.run();
|
|
}
|
|
|
|
void stop()
|
|
{
|
|
_service.stop();
|
|
}
|
|
|
|
service_type& context() NOEXCEPT { return _service; }
|
|
|
|
private:
|
|
|
|
class event_item : public qtl::event
|
|
{
|
|
public:
|
|
event_item(service& service, qtl::socket_type fd)
|
|
: _service(service), _strand(service.context()), _socket(service.context(), NS_ASIO::ip::tcp::v4(), fd), _timer(service.context()), _busying(false)
|
|
{
|
|
}
|
|
|
|
NS_ASIO::ip::tcp::socket& next_layer() { return _socket; }
|
|
|
|
public: // qtl::event
|
|
virtual void set_io_handler(int flags, long timeout, std::function<void(int)>&& handler) override
|
|
{
|
|
if (flags&qtl::event::ef_read)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
_socket.async_read_some(NS_ASIO::null_buffers(), _strand.wrap([this, handler](const NS_ASIO::error_code& ec, size_t bytes_transferred) {
|
|
#else
|
|
_socket.async_wait(NS_ASIO::socket_base::wait_read, _strand.wrap([this, handler](const NS_ASIO::error_code& ec) {
|
|
#endif // ASIO_VERSION
|
|
if (!ec)
|
|
handler(qtl::event::ef_read);
|
|
else if (ec == NS_ASIO::error::make_error_code(NS_ASIO::error::operation_aborted))
|
|
handler(qtl::event::ef_timeout);
|
|
else
|
|
handler(qtl::event::ef_exception);
|
|
_busying = false;
|
|
_timer.cancel();
|
|
}));
|
|
_busying = true;
|
|
}
|
|
if (flags&qtl::event::ef_write)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
_socket.async_write_some(NS_ASIO::null_buffers(), _strand.wrap([this, handler](const NS_ASIO::error_code& ec, size_t bytes_transferred) {
|
|
#else
|
|
_socket.async_wait(NS_ASIO::socket_base::wait_write, _strand.wrap([this, handler](const NS_ASIO::error_code& ec) {
|
|
#endif //ASIO_VERSION
|
|
if (!ec)
|
|
handler(qtl::event::ef_write);
|
|
else if (ec == NS_ASIO::error::make_error_code(NS_ASIO::error::operation_aborted))
|
|
handler(qtl::event::ef_timeout);
|
|
else
|
|
handler(qtl::event::ef_exception);
|
|
_timer.cancel();
|
|
_busying = false;
|
|
}));
|
|
_busying = true;
|
|
}
|
|
if (timeout > 0)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
_timer.expires_from_now(std::chrono::seconds(timeout));
|
|
#else
|
|
_timer.expires_after(NS_ASIO::chrono::seconds(timeout));
|
|
#endif // ASIO_VERSION
|
|
_timer.async_wait(_strand.wrap([this, handler](NS_ASIO::error_code ec) {
|
|
if (!ec)
|
|
{
|
|
_socket.cancel(ec);
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
virtual void remove() override
|
|
{
|
|
if (_busying) return;
|
|
#if ASIO_VERSION >= 101200 && (!defined(_WIN32) || _WIN32_WINNT >= 0x0603 )
|
|
_socket.release();
|
|
#endif //Windows 8.1
|
|
_service.remove(this);
|
|
}
|
|
virtual bool is_busying() override
|
|
{
|
|
return _busying;
|
|
}
|
|
|
|
private:
|
|
service& _service;
|
|
service_type::strand _strand;
|
|
NS_ASIO::ip::tcp::socket _socket;
|
|
NS_ASIO::steady_timer _timer;
|
|
bool _busying;
|
|
};
|
|
|
|
public:
|
|
|
|
template<typename Connection>
|
|
event_item* add(Connection* connection)
|
|
{
|
|
event_item* item = new event_item(*this, connection->socket());
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
_events.push_back(std::unique_ptr<event_item>(item));
|
|
return item;
|
|
}
|
|
|
|
private:
|
|
service_type _service;
|
|
std::mutex _mutex;
|
|
std::vector<std::unique_ptr<event_item>> _events;
|
|
|
|
void remove(event_item* item)
|
|
{
|
|
std::lock_guard<std::mutex> lock(_mutex);
|
|
auto it = std::find_if(_events.begin(), _events.end(), [item](std::unique_ptr<event_item>& v) {
|
|
return item==v.get();
|
|
});
|
|
if (it != _events.end()) _events.erase(it);
|
|
}
|
|
};
|
|
|
|
#if ASIO_VERSION < 101200
|
|
|
|
template <typename Handler, typename Signature>
|
|
using async_init_type = NS_ASIO::detail::async_result_init<Handler, Signature>;
|
|
|
|
template <typename Handler, typename Signature>
|
|
inline typename NS_ASIO::handler_type<Handler, Signature>::type
|
|
get_async_handler(async_init_type<Handler, Signature>& init)
|
|
{
|
|
return init.handler;
|
|
}
|
|
|
|
#else
|
|
|
|
template <typename Handler, typename Signature>
|
|
using async_init_type = NS_ASIO::async_completion<Handler, Signature>;
|
|
|
|
template <typename Handler, typename Signature>
|
|
inline typename async_init_type<Handler, Signature>::completion_handler_type
|
|
get_async_handler(async_init_type<Handler, Signature>& init)
|
|
{
|
|
return init.completion_handler;
|
|
}
|
|
|
|
#endif // ASIO_VERSION
|
|
|
|
template<typename Connection, typename OpenHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(OpenHandler, void(typename Connection::exception_type))
|
|
async_open(service& service, Connection& db, OpenHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<OpenHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<OpenHandler>(handler));
|
|
#else
|
|
async_init_type<OpenHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.open(service, get_async_handler(init), std::forward<Args>(args)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename CloseHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(CloseHandler, void())
|
|
async_close(Connection& db, CloseHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<CloseHandler,
|
|
void()> init(std::forward<CloseHandler>(std::forward<CloseHandler>(handler)));
|
|
#else
|
|
async_init_type<CloseHandler,
|
|
void()> init(std::forward<CloseHandler>(handler));
|
|
#endif
|
|
|
|
db.close(get_async_handler(init), std::forward<Args>(args)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename ExecuteHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t))
|
|
async_execute(Connection& db, ExecuteHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
|
|
#else
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(handler);
|
|
#endif
|
|
|
|
db.execute(get_async_handler(init), std::forward<Args>(args)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename ExecuteHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t))
|
|
async_execute_direct(Connection& db, ExecuteHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
|
|
#else
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(handler);
|
|
#endif
|
|
|
|
db.execute_direct(get_async_handler(init), std::forward<Args>(args)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename ExecuteHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t))
|
|
async_insert(Connection& db, ExecuteHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
|
|
#else
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(handler);
|
|
#endif
|
|
|
|
db.insert(get_async_handler(init), std::forward<Args>(args)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename ExecuteHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(ExecuteHandler, void(typename Connection::exception_type, uint64_t))
|
|
async_insert_direct(Connection& db, ExecuteHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(std::forward<ExecuteHandler>(handler));
|
|
#else
|
|
async_init_type<ExecuteHandler,
|
|
void(typename Connection::exception_type, uint64_t)> init(handler);
|
|
#endif
|
|
|
|
db.insert_direct(get_async_handler(init), std::forward<Args>(args)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename FinishHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
|
|
async_query(Connection& db, FinishHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
|
|
#else
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.query(std::forward<Args>(args)..., get_async_handler(init));
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename FinishHandler, typename... Args>
|
|
inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
|
|
async_query_explicit(Connection& db, FinishHandler&& handler, Args&&... args)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
|
|
#else
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.query_explicit(std::forward<Args>(args)..., get_async_handler(init));
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename A1, typename A2, typename FinishHandler, typename... RowHandlers>
|
|
inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
|
|
async_query_multi_with_params(Connection& db, A1&& a1, A2&& a2, FinishHandler&& handler, RowHandlers&&... row_handlers)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
|
|
#else
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.query_multi_with_params(std::forward<A1>(a1), std::forward<A2>(a2), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename A1, typename FinishHandler, typename... RowHandlers>
|
|
inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
|
|
async_query_multi_with_params(Connection& db, A1&& a1, FinishHandler&& handler, RowHandlers&&... row_handlers)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
|
|
#else
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.query_multi_with_params(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename A1, typename A2, typename FinishHandler, typename... RowHandlers>
|
|
inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
|
|
async_query_multi(Connection& db, A1&& a1, A2&& a2, FinishHandler&& handler, RowHandlers&&... row_handlers)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
|
|
#else
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.query_multi(std::forward<A1>(a1), std::forward<A2>(a2), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
template<typename Connection, typename A1, typename FinishHandler, typename... RowHandlers>
|
|
inline ASIO_INITFN_RESULT_TYPE(FinishHandler, void(typename Connection::exception_type))
|
|
async_query_multi(Connection& db, A1&& a1, FinishHandler&& handler, RowHandlers&&... row_handlers)
|
|
{
|
|
#if ASIO_VERSION < 101200
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(std::forward<FinishHandler>(handler));
|
|
#else
|
|
async_init_type<FinishHandler,
|
|
void(typename Connection::exception_type)> init(handler);
|
|
#endif
|
|
|
|
db.query_multi(std::forward<A1>(a1), get_async_handler(init), std::forward<RowHandlers>(row_handlers)...);
|
|
return init.result.get();
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif //_QTL_ASIO_H_
|