#ifndef _QTL_ASIO_H_ #define _QTL_ASIO_H_ #include "qtl_async.hpp" #include #define ASIO_STANDALONE #if defined(_MSC_VER) && _WIN32_WINNT<0x0600 #define ASIO_ENABLE_CANCELIO 1 #endif #if ASIO_VERSION < 101200 #include #else #include #endif // ASIO_VERSION #include #include #include #include #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&& 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 event_item* add(Connection* connection) { event_item* item = new event_item(*this, connection->socket()); std::lock_guard lock(_mutex); _events.push_back(std::unique_ptr(item)); return item; } private: service_type _service; std::mutex _mutex; std::vector> _events; void remove(event_item* item) { std::lock_guard lock(_mutex); auto it = std::find_if(_events.begin(), _events.end(), [item](std::unique_ptr& v) { return item==v.get(); }); if (it != _events.end()) _events.erase(it); } }; #if ASIO_VERSION < 101200 template using async_init_type = NS_ASIO::detail::async_result_init; template inline typename NS_ASIO::handler_type::type get_async_handler(async_init_type& init) { return init.handler; } #else template using async_init_type = NS_ASIO::async_completion; template inline typename async_init_type::completion_handler_type get_async_handler(async_init_type& init) { return init.completion_handler; } #endif // ASIO_VERSION template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.open(service, get_async_handler(init), std::forward(args)...); return init.result.get(); } template inline ASIO_INITFN_RESULT_TYPE(CloseHandler, void()) async_close(Connection& db, CloseHandler&& handler, Args&&... args) { #if ASIO_VERSION < 101200 async_init_type init(std::forward(std::forward(handler))); #else async_init_type init(std::forward(handler)); #endif db.close(get_async_handler(init), std::forward(args)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.execute(get_async_handler(init), std::forward(args)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.execute_direct(get_async_handler(init), std::forward(args)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.insert(get_async_handler(init), std::forward(args)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.insert_direct(get_async_handler(init), std::forward(args)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.query(std::forward(args)..., get_async_handler(init)); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.query_explicit(std::forward(args)..., get_async_handler(init)); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.query_multi_with_params(std::forward(a1), std::forward(a2), get_async_handler(init), std::forward(row_handlers)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.query_multi_with_params(std::forward(a1), get_async_handler(init), std::forward(row_handlers)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.query_multi(std::forward(a1), std::forward(a2), get_async_handler(init), std::forward(row_handlers)...); return init.result.get(); } template 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 init(std::forward(handler)); #else async_init_type init(handler); #endif db.query_multi(std::forward(a1), get_async_handler(init), std::forward(row_handlers)...); return init.result.get(); } } } #endif //_QTL_ASIO_H_