From e6b292feecfcbb5bde0f6ba1167fb865de116697 Mon Sep 17 00:00:00 2001
From: philoinovsky <1129410550@qq.com>
Date: Tue, 8 Jun 2021 14:56:48 +0000
Subject: [PATCH 01/15] add eventloop implementations for call_* and
call_when_*_completed functions
---
build/Jamfile | 1 +
include/boost/python.hpp | 1 +
include/boost/python/eventloop.hpp | 111 +++++++++++++++++++++++++++++
src/eventloop.cpp | 81 +++++++++++++++++++++
src/fabscript | 3 +-
test/Jamfile | 1 +
6 files changed, 197 insertions(+), 1 deletion(-)
create mode 100644 include/boost/python/eventloop.hpp
create mode 100644 src/eventloop.cpp
diff --git a/build/Jamfile b/build/Jamfile
index dbc9fb2036..bfb6c5539b 100644
--- a/build/Jamfile
+++ b/build/Jamfile
@@ -68,6 +68,7 @@ lib boost_python
import.cpp
exec.cpp
object/function_doc_signature.cpp
+ eventloop.cpp
: # requirements
static:BOOST_PYTHON_STATIC_LIB
BOOST_PYTHON_SOURCE
diff --git a/include/boost/python.hpp b/include/boost/python.hpp
index e484034103..a181afaeae 100644
--- a/include/boost/python.hpp
+++ b/include/boost/python.hpp
@@ -25,6 +25,7 @@
# include
# include
# include
+# include
# include
# include
# include
diff --git a/include/boost/python/eventloop.hpp b/include/boost/python/eventloop.hpp
new file mode 100644
index 0000000000..78fb48c8a9
--- /dev/null
+++ b/include/boost/python/eventloop.hpp
@@ -0,0 +1,111 @@
+// Copyright Pan Yue 2021.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// TODO:
+// 1. posix::stream_descriptor need windows version
+// 2. call_* need return async.Handle
+# ifndef EVENT_LOOP_PY2021_H_
+# define EVENT_LOOP_PY2021_H_
+
+#include
+#include
+#include
+
+namespace a = boost::asio;
+namespace c = std::chrono;
+namespace py = boost::python;
+
+namespace boost { namespace python { namespace eventloop {
+
+class EventLoop
+{
+private:
+ int64_t _timer_id = 0;
+ a::io_context::strand _strand;
+ std::unordered_map> _id_to_timer_map;
+ // read: key = fd * 2 + 0, write: key = fd * 2 + 1
+ std::unordered_map> _descriptor_map;
+ std::chrono::steady_clock::time_point _created_time;
+
+ void _add_reader_or_writer(int fd, py::object f, int key);
+ void _remove_reader_or_writer(int key);
+
+public:
+ EventLoop(a::io_context& ctx):
+ _strand{ctx}, _created_time{std::chrono::steady_clock::now()}
+ {
+ }
+
+ // TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
+ inline void call_soon(py::object f)
+ {
+ _strand.post([f, loop=this] {
+ f(boost::ref(*loop));
+ });
+ return;
+ }
+
+ // TODO: implement this
+ inline void call_soon_thread_safe(py::object f) {};
+
+ // Schedule callback to be called after the given delay number of seconds
+ // TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
+ void call_later(double delay, py::object f);
+
+ void call_at(double when, py::object f);
+
+ inline double time()
+ {
+ return static_cast>(std::chrono::steady_clock::now() - _created_time).count();
+ }
+
+ // week 2 ......start......
+
+ inline void add_reader(int fd, py::object f)
+ {
+ _add_reader_or_writer(fd, f, fd * 2);
+ }
+
+ inline void remove_reader(int fd)
+ {
+ _remove_reader_or_writer(fd * 2);
+ }
+
+ inline void add_writer(int fd, py::object f)
+ {
+ _add_reader_or_writer(fd, f, fd * 2 + 1);
+ }
+
+ inline void remove_writer(int fd)
+ {
+ _remove_reader_or_writer(fd * 2 + 1);
+ }
+
+
+ void sock_recv(py::object sock, int bytes);
+
+ void sock_recv_into(py::object sock, py::object buffer);
+
+ void sock_sendall(py::object sock, py::object data);
+
+ void sock_connect(py::object sock, py::object address);
+
+ void sock_accept(py::object sock);
+
+ void sock_sendfile(py::object sock, py::object file, int offset = 0, int count = 0, bool fallback = true);
+
+ // week 2 ......end......
+
+ void run()
+ {
+ _strand.context().run();
+ }
+};
+
+
+}}}
+
+
+# endif
diff --git a/src/eventloop.cpp b/src/eventloop.cpp
new file mode 100644
index 0000000000..8977a33958
--- /dev/null
+++ b/src/eventloop.cpp
@@ -0,0 +1,81 @@
+// Copyright Pan Yue 2021.
+// Distributed under the Boost Software License, Version 1.0. (See
+// accompanying file LICENSE_1_0.txt or copy at
+// http://www.boost.org/LICENSE_1_0.txt)
+
+// TODO:
+// 1. posix::stream_descriptor need windows version
+// 2. call_* need return async.Handle
+
+#include
+#include
+#include
+
+namespace a = boost::asio;
+namespace c = std::chrono;
+namespace py = boost::python;
+
+namespace boost { namespace python { namespace eventloop {
+
+void EventLoop::_add_reader_or_writer(int fd, py::object f, int key)
+{
+ // add descriptor
+ if (_descriptor_map.find(key) == _descriptor_map.end())
+ {
+ _descriptor_map.emplace(key,
+ std::move(std::make_unique(_strand.context(), fd))
+ );
+ }
+
+ _descriptor_map.find(key)->second->async_wait(a::posix::descriptor::wait_type::wait_read,
+ a::bind_executor(_strand, [key, f, loop=this] (const boost::system::error_code& ec)
+ {
+ // move descriptor
+ auto iter = loop->_descriptor_map.find(key);
+ if (iter != loop->_descriptor_map.end())
+ {
+ iter->second->release();
+ loop->_descriptor_map.erase(iter);
+ }
+ loop->call_soon(f);
+ }));
+ return;
+}
+
+void EventLoop::_remove_reader_or_writer(int key)
+{
+ auto iter = _descriptor_map.find(key);
+ if (iter != _descriptor_map.end())
+ {
+ iter->second->release();
+ _descriptor_map.erase(iter);
+ }
+}
+
+void EventLoop::call_later(double delay, py::object f)
+{
+ // add timer
+ _id_to_timer_map.emplace(_timer_id,
+ std::move(std::make_unique(_strand.context(),
+ std::chrono::steady_clock::now() + std::chrono::nanoseconds(int64_t(delay * 1e9))))
+ );
+
+ _id_to_timer_map.find(_timer_id)->second->async_wait(
+ // remove timer
+ a::bind_executor(_strand, [id=_timer_id, f, loop=this] (const boost::system::error_code& ec)
+ {
+ loop->_id_to_timer_map.erase(id);
+ loop->call_soon(f);
+ }));
+ _timer_id++;
+}
+
+void EventLoop::call_at(double when, py::object f)
+{
+ double diff = when - time();
+ if (diff > 0)
+ return call_later(diff, f);
+ return call_soon(f);
+}
+
+}}}
\ No newline at end of file
diff --git a/src/fabscript b/src/fabscript
index 0ebeac6098..6c5b858351 100644
--- a/src/fabscript
+++ b/src/fabscript
@@ -40,7 +40,8 @@ bpl = library('boost_python' + root.py_suffix,
'wrapper.cpp',
'import.cpp',
'exec.cpp',
- 'object/function_doc_signature.cpp'],
+ 'object/function_doc_signature.cpp',
+ 'eventloop.cpp'],
dependencies=root.config,
features=features + define('BOOST_PYTHON_SOURCE'))
diff --git a/test/Jamfile b/test/Jamfile
index 9a5c756956..1ca4d38f84 100644
--- a/test/Jamfile
+++ b/test/Jamfile
@@ -84,6 +84,7 @@ bpl-test crossmod_exception
: crossmod_exception.py crossmod_exception_a.cpp crossmod_exception_b.cpp
]
+[ bpl-test eventloop ]
[ bpl-test injected ]
[ bpl-test properties ]
[ bpl-test return_arg ]
From 7d8fae83481cb85d279cdf9f1b955a17b8ff7ed6 Mon Sep 17 00:00:00 2001
From: philoinovsky <1129410550@qq.com>
Date: Sat, 26 Jun 2021 17:06:01 +0000
Subject: [PATCH 02/15] add implementations of sock_* functions
---
src/eventloop.cpp | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
diff --git a/src/eventloop.cpp b/src/eventloop.cpp
index 8977a33958..cf2c8ab8b6 100644
--- a/src/eventloop.cpp
+++ b/src/eventloop.cpp
@@ -78,4 +78,34 @@ void EventLoop::call_at(double when, py::object f)
return call_soon(f);
}
+void EventLoop::sock_recv(py::object sock, int bytes)
+{
+
+}
+
+void EventLoop::sock_recv_into(py::object sock, py::object buffer)
+{
+
+}
+
+void EventLoop::sock_sendall(py::object sock, py::object data)
+{
+
+}
+
+void EventLoop::sock_connect(py::object sock, py::object address)
+{
+
+}
+
+void EventLoop::sock_accept(py::object sock)
+{
+
+}
+
+void EventLoop::sock_sendfile(py::object sock, py::object file, int offset, int count, bool fallback)
+{
+
+}
+
}}}
\ No newline at end of file
From 5747a3af2eb1f43f565bf4111c254fdc8f1f968f Mon Sep 17 00:00:00 2001
From: philoinovsky <1129410550@qq.com>
Date: Sun, 27 Jun 2021 18:08:31 +0000
Subject: [PATCH 03/15] Fixed namespace related issues
- fixed namespace pollution
- rename boost::python::eventloop to boost::python::asio
- remove eventloop.hpp from include list in python.hpp
- rename define guards in eventloop.cpp
- reorder class members in order: public, protected, private
- rename class EventLoop to event_loop
- remove `run()` from eventloop
---
include/boost/python.hpp | 1 -
include/boost/python/eventloop.hpp | 66 ++++++++++++------------------
src/eventloop.cpp | 36 ++++++++--------
3 files changed, 43 insertions(+), 60 deletions(-)
diff --git a/include/boost/python.hpp b/include/boost/python.hpp
index a181afaeae..e484034103 100644
--- a/include/boost/python.hpp
+++ b/include/boost/python.hpp
@@ -25,7 +25,6 @@
# include
# include
# include
-# include
# include
# include
# include
diff --git a/include/boost/python/eventloop.hpp b/include/boost/python/eventloop.hpp
index 78fb48c8a9..4a7c3b30a7 100644
--- a/include/boost/python/eventloop.hpp
+++ b/include/boost/python/eventloop.hpp
@@ -6,40 +6,25 @@
// TODO:
// 1. posix::stream_descriptor need windows version
// 2. call_* need return async.Handle
-# ifndef EVENT_LOOP_PY2021_H_
-# define EVENT_LOOP_PY2021_H_
+# ifndef EVENT_LOOP_PY2021_HPP
+# define EVENT_LOOP_PY2021_HPP
#include
#include
#include
-namespace a = boost::asio;
-namespace c = std::chrono;
-namespace py = boost::python;
+namespace boost { namespace python { namespace asio {
-namespace boost { namespace python { namespace eventloop {
-
-class EventLoop
+class event_loop
{
-private:
- int64_t _timer_id = 0;
- a::io_context::strand _strand;
- std::unordered_map> _id_to_timer_map;
- // read: key = fd * 2 + 0, write: key = fd * 2 + 1
- std::unordered_map> _descriptor_map;
- std::chrono::steady_clock::time_point _created_time;
-
- void _add_reader_or_writer(int fd, py::object f, int key);
- void _remove_reader_or_writer(int key);
-
public:
- EventLoop(a::io_context& ctx):
+ event_loop(boost::asio::io_context& ctx):
_strand{ctx}, _created_time{std::chrono::steady_clock::now()}
{
}
// TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
- inline void call_soon(py::object f)
+ inline void call_soon(object f)
{
_strand.post([f, loop=this] {
f(boost::ref(*loop));
@@ -48,22 +33,20 @@ class EventLoop
}
// TODO: implement this
- inline void call_soon_thread_safe(py::object f) {};
+ inline void call_soon_thread_safe(object f) {};
// Schedule callback to be called after the given delay number of seconds
// TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
- void call_later(double delay, py::object f);
+ void call_later(double delay, object f);
- void call_at(double when, py::object f);
+ void call_at(double when, object f);
inline double time()
{
return static_cast>(std::chrono::steady_clock::now() - _created_time).count();
}
- // week 2 ......start......
-
- inline void add_reader(int fd, py::object f)
+ inline void add_reader(int fd, object f)
{
_add_reader_or_writer(fd, f, fd * 2);
}
@@ -73,7 +56,7 @@ class EventLoop
_remove_reader_or_writer(fd * 2);
}
- inline void add_writer(int fd, py::object f)
+ inline void add_writer(int fd, object f)
{
_add_reader_or_writer(fd, f, fd * 2 + 1);
}
@@ -84,27 +67,30 @@ class EventLoop
}
- void sock_recv(py::object sock, int bytes);
+ void sock_recv(object sock, int bytes);
- void sock_recv_into(py::object sock, py::object buffer);
+ void sock_recv_into(object sock, object buffer);
- void sock_sendall(py::object sock, py::object data);
+ void sock_sendall(object sock, object data);
- void sock_connect(py::object sock, py::object address);
+ void sock_connect(object sock, object address);
- void sock_accept(py::object sock);
+ void sock_accept(object sock);
- void sock_sendfile(py::object sock, py::object file, int offset = 0, int count = 0, bool fallback = true);
+ void sock_sendfile(object sock, object file, int offset = 0, int count = 0, bool fallback = true);
- // week 2 ......end......
+private:
+ int64_t _timer_id = 0;
+ boost::asio::io_context::strand _strand;
+ std::unordered_map> _id_to_timer_map;
+ // read: key = fd * 2 + 0, write: key = fd * 2 + 1
+ std::unordered_map> _descriptor_map;
+ std::chrono::steady_clock::time_point _created_time;
- void run()
- {
- _strand.context().run();
- }
+ void _add_reader_or_writer(int fd, object f, int key);
+ void _remove_reader_or_writer(int key);
};
-
}}}
diff --git a/src/eventloop.cpp b/src/eventloop.cpp
index cf2c8ab8b6..c4ed980761 100644
--- a/src/eventloop.cpp
+++ b/src/eventloop.cpp
@@ -10,25 +10,23 @@
#include
#include
#include
+#include
-namespace a = boost::asio;
-namespace c = std::chrono;
-namespace py = boost::python;
-namespace boost { namespace python { namespace eventloop {
+namespace boost { namespace python { namespace asio {
-void EventLoop::_add_reader_or_writer(int fd, py::object f, int key)
+void event_loop::_add_reader_or_writer(int fd, object f, int key)
{
// add descriptor
if (_descriptor_map.find(key) == _descriptor_map.end())
{
_descriptor_map.emplace(key,
- std::move(std::make_unique(_strand.context(), fd))
+ std::move(std::make_unique(_strand.context(), fd))
);
}
- _descriptor_map.find(key)->second->async_wait(a::posix::descriptor::wait_type::wait_read,
- a::bind_executor(_strand, [key, f, loop=this] (const boost::system::error_code& ec)
+ _descriptor_map.find(key)->second->async_wait(boost::asio::posix::descriptor::wait_type::wait_read,
+ boost::asio::bind_executor(_strand, [key, f, loop=this] (const boost::system::error_code& ec)
{
// move descriptor
auto iter = loop->_descriptor_map.find(key);
@@ -42,7 +40,7 @@ void EventLoop::_add_reader_or_writer(int fd, py::object f, int key)
return;
}
-void EventLoop::_remove_reader_or_writer(int key)
+void event_loop::_remove_reader_or_writer(int key)
{
auto iter = _descriptor_map.find(key);
if (iter != _descriptor_map.end())
@@ -52,17 +50,17 @@ void EventLoop::_remove_reader_or_writer(int key)
}
}
-void EventLoop::call_later(double delay, py::object f)
+void event_loop::call_later(double delay, object f)
{
// add timer
_id_to_timer_map.emplace(_timer_id,
- std::move(std::make_unique(_strand.context(),
+ std::move(std::make_unique(_strand.context(),
std::chrono::steady_clock::now() + std::chrono::nanoseconds(int64_t(delay * 1e9))))
);
_id_to_timer_map.find(_timer_id)->second->async_wait(
// remove timer
- a::bind_executor(_strand, [id=_timer_id, f, loop=this] (const boost::system::error_code& ec)
+ boost::asio::bind_executor(_strand, [id=_timer_id, f, loop=this] (const boost::system::error_code& ec)
{
loop->_id_to_timer_map.erase(id);
loop->call_soon(f);
@@ -70,7 +68,7 @@ void EventLoop::call_later(double delay, py::object f)
_timer_id++;
}
-void EventLoop::call_at(double when, py::object f)
+void event_loop::call_at(double when, object f)
{
double diff = when - time();
if (diff > 0)
@@ -78,32 +76,32 @@ void EventLoop::call_at(double when, py::object f)
return call_soon(f);
}
-void EventLoop::sock_recv(py::object sock, int bytes)
+void event_loop::sock_recv(object sock, int bytes)
{
}
-void EventLoop::sock_recv_into(py::object sock, py::object buffer)
+void event_loop::sock_recv_into(object sock, object buffer)
{
}
-void EventLoop::sock_sendall(py::object sock, py::object data)
+void event_loop::sock_sendall(object sock, object data)
{
}
-void EventLoop::sock_connect(py::object sock, py::object address)
+void event_loop::sock_connect(object sock, object address)
{
}
-void EventLoop::sock_accept(py::object sock)
+void event_loop::sock_accept(object sock)
{
}
-void EventLoop::sock_sendfile(py::object sock, py::object file, int offset, int count, bool fallback)
+void event_loop::sock_sendfile(object sock, object file, int offset, int count, bool fallback)
{
}
From a2a2bbeaa59d88a5102fb834d0980df96940aab0 Mon Sep 17 00:00:00 2001
From: philoinovsky <1129410550@qq.com>
Date: Sun, 27 Jun 2021 18:20:00 +0000
Subject: [PATCH 04/15] add implementations for eventloop functions sock_*
---
include/boost/python/eventloop.hpp | 9 +-
src/eventloop.cpp | 195 +++++++++++++++++++++++++++--
2 files changed, 190 insertions(+), 14 deletions(-)
diff --git a/include/boost/python/eventloop.hpp b/include/boost/python/eventloop.hpp
index 4a7c3b30a7..c8338d73d4 100644
--- a/include/boost/python/eventloop.hpp
+++ b/include/boost/python/eventloop.hpp
@@ -67,20 +67,21 @@ class event_loop
}
- void sock_recv(object sock, int bytes);
+ object sock_recv(object sock, size_t nbytes);
- void sock_recv_into(object sock, object buffer);
+ size_t sock_recv_into(object sock, object buffer);
- void sock_sendall(object sock, object data);
+ object sock_sendall(object sock, object data);
void sock_connect(object sock, object address);
- void sock_accept(object sock);
+ object sock_accept(object sock);
void sock_sendfile(object sock, object file, int offset = 0, int count = 0, bool fallback = true);
private:
int64_t _timer_id = 0;
+ object _pymod_socket = import("socket");
boost::asio::io_context::strand _strand;
std::unordered_map> _id_to_timer_map;
// read: key = fd * 2 + 0, write: key = fd * 2 + 1
diff --git a/src/eventloop.cpp b/src/eventloop.cpp
index c4ed980761..676a66126f 100644
--- a/src/eventloop.cpp
+++ b/src/eventloop.cpp
@@ -6,14 +6,121 @@
// TODO:
// 1. posix::stream_descriptor need windows version
// 2. call_* need return async.Handle
+// 3. _ensure_fd_no_transport
+// 4. _ensure_resolve
#include
#include
#include
#include
+#include
+#include
namespace boost { namespace python { namespace asio {
+namespace
+{
+
+bool _hasattr(object o, const char* name)
+{
+ return PyObject_HasAttrString(o.ptr(), name);
+}
+
+void _sock_recv_handler(
+ std::promise>& prom_data,
+ std::promise& prom_nbytes_read,
+ size_t nbytes,
+ int fd)
+{
+ std::vector buffer(nbytes);
+ prom_nbytes_read.set_value(read(fd, buffer.data(), nbytes));
+ prom_data.set_value(std::move(buffer));
+}
+
+void _sock_send_handler(std::promise& prom, int fd, const char *py_str, ssize_t len)
+{
+ size_t nwrite = write(fd, py_str, len);
+ prom.set_value(nwrite);
+}
+
+void _sock_connect_cb(object pymod_socket, std::promise& prom, std::future& fut, object sock, object addr)
+{
+ try
+ {
+ object err = sock.attr("getsockopt")(
+ pymod_socket.attr("SOL_SOCKET"), pymod_socket.attr("SO_ERROR"));
+ if (err != object(0)) {
+ // TODO: print the address
+ PyErr_SetString(PyExc_OSError, "Connect call failed {address}");
+ }
+ }
+ catch (const error_already_set& e)
+ {
+ if (PyErr_ExceptionMatches(PyExc_BlockingIOError)
+ || PyErr_ExceptionMatches(PyExc_InterruptedError))
+ {
+ PyErr_Clear();
+ // pass
+ }
+ else if (PyErr_ExceptionMatches(PyExc_SystemExit)
+ || PyErr_ExceptionMatches(PyExc_KeyboardInterrupt))
+ {
+ // raise
+ }
+ else if (PyErr_ExceptionMatches(PyExc_BaseException))
+ {
+ PyErr_Clear();
+ prom.set_exception(std::current_exception());
+ }
+ else
+ {
+ PyErr_Clear();
+ prom.set_value();
+ }
+ }
+}
+
+void _sock_accept(event_loop& loop, std::promise