Skip to content

Commit e6b292f

Browse files
committed
add eventloop implementations for call_* and call_when_*_completed functions
1 parent 68fa9dc commit e6b292f

File tree

6 files changed

+197
-1
lines changed

6 files changed

+197
-1
lines changed

build/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ lib boost_python
6868
import.cpp
6969
exec.cpp
7070
object/function_doc_signature.cpp
71+
eventloop.cpp
7172
: # requirements
7273
<link>static:<define>BOOST_PYTHON_STATIC_LIB
7374
<define>BOOST_PYTHON_SOURCE

include/boost/python.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
# include <boost/python/docstring_options.hpp>
2626
# include <boost/python/enum.hpp>
2727
# include <boost/python/errors.hpp>
28+
# include <boost/python/eventloop.hpp>
2829
# include <boost/python/exception_translator.hpp>
2930
# include <boost/python/exec.hpp>
3031
# include <boost/python/extract.hpp>

include/boost/python/eventloop.hpp

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
// Copyright Pan Yue 2021.
2+
// Distributed under the Boost Software License, Version 1.0. (See
3+
// accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
// TODO:
7+
// 1. posix::stream_descriptor need windows version
8+
// 2. call_* need return async.Handle
9+
# ifndef EVENT_LOOP_PY2021_H_
10+
# define EVENT_LOOP_PY2021_H_
11+
12+
#include <unordered_map>
13+
#include <boost/asio.hpp>
14+
#include <boost/python.hpp>
15+
16+
namespace a = boost::asio;
17+
namespace c = std::chrono;
18+
namespace py = boost::python;
19+
20+
namespace boost { namespace python { namespace eventloop {
21+
22+
class EventLoop
23+
{
24+
private:
25+
int64_t _timer_id = 0;
26+
a::io_context::strand _strand;
27+
std::unordered_map<int, std::unique_ptr<a::steady_timer>> _id_to_timer_map;
28+
// read: key = fd * 2 + 0, write: key = fd * 2 + 1
29+
std::unordered_map<int, std::unique_ptr<a::posix::stream_descriptor>> _descriptor_map;
30+
std::chrono::steady_clock::time_point _created_time;
31+
32+
void _add_reader_or_writer(int fd, py::object f, int key);
33+
void _remove_reader_or_writer(int key);
34+
35+
public:
36+
EventLoop(a::io_context& ctx):
37+
_strand{ctx}, _created_time{std::chrono::steady_clock::now()}
38+
{
39+
}
40+
41+
// TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
42+
inline void call_soon(py::object f)
43+
{
44+
_strand.post([f, loop=this] {
45+
f(boost::ref(*loop));
46+
});
47+
return;
48+
}
49+
50+
// TODO: implement this
51+
inline void call_soon_thread_safe(py::object f) {};
52+
53+
// Schedule callback to be called after the given delay number of seconds
54+
// TODO: An instance of asyncio.Handle is returned, which can be used later to cancel the callback.
55+
void call_later(double delay, py::object f);
56+
57+
void call_at(double when, py::object f);
58+
59+
inline double time()
60+
{
61+
return static_cast<std::chrono::duration<double>>(std::chrono::steady_clock::now() - _created_time).count();
62+
}
63+
64+
// week 2 ......start......
65+
66+
inline void add_reader(int fd, py::object f)
67+
{
68+
_add_reader_or_writer(fd, f, fd * 2);
69+
}
70+
71+
inline void remove_reader(int fd)
72+
{
73+
_remove_reader_or_writer(fd * 2);
74+
}
75+
76+
inline void add_writer(int fd, py::object f)
77+
{
78+
_add_reader_or_writer(fd, f, fd * 2 + 1);
79+
}
80+
81+
inline void remove_writer(int fd)
82+
{
83+
_remove_reader_or_writer(fd * 2 + 1);
84+
}
85+
86+
87+
void sock_recv(py::object sock, int bytes);
88+
89+
void sock_recv_into(py::object sock, py::object buffer);
90+
91+
void sock_sendall(py::object sock, py::object data);
92+
93+
void sock_connect(py::object sock, py::object address);
94+
95+
void sock_accept(py::object sock);
96+
97+
void sock_sendfile(py::object sock, py::object file, int offset = 0, int count = 0, bool fallback = true);
98+
99+
// week 2 ......end......
100+
101+
void run()
102+
{
103+
_strand.context().run();
104+
}
105+
};
106+
107+
108+
}}}
109+
110+
111+
# endif

src/eventloop.cpp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Copyright Pan Yue 2021.
2+
// Distributed under the Boost Software License, Version 1.0. (See
3+
// accompanying file LICENSE_1_0.txt or copy at
4+
// http://www.boost.org/LICENSE_1_0.txt)
5+
6+
// TODO:
7+
// 1. posix::stream_descriptor need windows version
8+
// 2. call_* need return async.Handle
9+
10+
#include <boost/asio.hpp>
11+
#include <boost/bind.hpp>
12+
#include <boost/python.hpp>
13+
14+
namespace a = boost::asio;
15+
namespace c = std::chrono;
16+
namespace py = boost::python;
17+
18+
namespace boost { namespace python { namespace eventloop {
19+
20+
void EventLoop::_add_reader_or_writer(int fd, py::object f, int key)
21+
{
22+
// add descriptor
23+
if (_descriptor_map.find(key) == _descriptor_map.end())
24+
{
25+
_descriptor_map.emplace(key,
26+
std::move(std::make_unique<a::posix::stream_descriptor>(_strand.context(), fd))
27+
);
28+
}
29+
30+
_descriptor_map.find(key)->second->async_wait(a::posix::descriptor::wait_type::wait_read,
31+
a::bind_executor(_strand, [key, f, loop=this] (const boost::system::error_code& ec)
32+
{
33+
// move descriptor
34+
auto iter = loop->_descriptor_map.find(key);
35+
if (iter != loop->_descriptor_map.end())
36+
{
37+
iter->second->release();
38+
loop->_descriptor_map.erase(iter);
39+
}
40+
loop->call_soon(f);
41+
}));
42+
return;
43+
}
44+
45+
void EventLoop::_remove_reader_or_writer(int key)
46+
{
47+
auto iter = _descriptor_map.find(key);
48+
if (iter != _descriptor_map.end())
49+
{
50+
iter->second->release();
51+
_descriptor_map.erase(iter);
52+
}
53+
}
54+
55+
void EventLoop::call_later(double delay, py::object f)
56+
{
57+
// add timer
58+
_id_to_timer_map.emplace(_timer_id,
59+
std::move(std::make_unique<a::steady_timer>(_strand.context(),
60+
std::chrono::steady_clock::now() + std::chrono::nanoseconds(int64_t(delay * 1e9))))
61+
);
62+
63+
_id_to_timer_map.find(_timer_id)->second->async_wait(
64+
// remove timer
65+
a::bind_executor(_strand, [id=_timer_id, f, loop=this] (const boost::system::error_code& ec)
66+
{
67+
loop->_id_to_timer_map.erase(id);
68+
loop->call_soon(f);
69+
}));
70+
_timer_id++;
71+
}
72+
73+
void EventLoop::call_at(double when, py::object f)
74+
{
75+
double diff = when - time();
76+
if (diff > 0)
77+
return call_later(diff, f);
78+
return call_soon(f);
79+
}
80+
81+
}}}

src/fabscript

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ bpl = library('boost_python' + root.py_suffix,
4040
'wrapper.cpp',
4141
'import.cpp',
4242
'exec.cpp',
43-
'object/function_doc_signature.cpp'],
43+
'object/function_doc_signature.cpp',
44+
'eventloop.cpp'],
4445
dependencies=root.config,
4546
features=features + define('BOOST_PYTHON_SOURCE'))
4647

test/Jamfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ bpl-test crossmod_exception
8484
: crossmod_exception.py crossmod_exception_a.cpp crossmod_exception_b.cpp
8585
]
8686

87+
[ bpl-test eventloop ]
8788
[ bpl-test injected ]
8889
[ bpl-test properties ]
8990
[ bpl-test return_arg ]

0 commit comments

Comments
 (0)