forked from aldebaran/libqi-python
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpysignal.cpp
More file actions
228 lines (195 loc) · 6.69 KB
/
pysignal.cpp
File metadata and controls
228 lines (195 loc) · 6.69 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
/*
** Copyright (C) 2020 SoftBank Robotics Europe
** See COPYING for the license
*/
#include <qipython/pysignal.hpp>
#include <qipython/common.hpp>
#include <qipython/pyguard.hpp>
#include <qipython/pystrand.hpp>
#include <qipython/pyfuture.hpp>
#include <qipython/pyobject.hpp>
#include <qi/signal.hpp>
#include <qi/anyobject.hpp>
qiLogCategory("qi.python.signal");
namespace py = pybind11;
namespace qi
{
namespace py
{
namespace
{
constexpr static const auto asyncArgName = "_async";
AnyReference dynamicCallFunction(const SharedObject<::py::function>& func,
const AnyReferenceVector& args)
{
GILAcquire lock;
::py::list pyArgs(args.size());
::py::size_t i = 0;
for (const auto& arg : args)
pyArgs[i++] = castToPyObject(arg);
invokeCatchPythonError(func.inner(), *pyArgs);
return AnyValue::makeVoid().release();
}
// Procedure<Future<SignalLink>(SignalSubscriber)> F
template<typename F>
::py::object connect(F&& connect,
const ::py::function& pyCallback,
bool async)
{
GILAcquire lock;
const auto strand = strandOfFunction(pyCallback);
SignalSubscriber subscriber(AnyFunction::fromDynamicFunction(
boost::bind(dynamicCallFunction,
SharedObject(pyCallback),
_1)),
strand.get());
subscriber.setCallType(MetaCallType_Auto);
const auto fut = std::forward<F>(connect)(std::move(subscriber))
.andThen(FutureCallbackType_Sync, [](SignalLink link) {
return AnyValue::from(link);
});
return resultObject(fut, async);
}
::py::object proxySignalConnect(detail::ProxySignal& sig,
const ::py::function& callback,
bool async)
{
GILAcquire lock;
return detail::proxySignalConnect(sig.object, sig.signalId, callback, async);
}
} // namespace
namespace detail
{
detail::ProxySignal::~ProxySignal()
{
// The destructor can lock waiting for callbacks to end.
GILRelease unlock;
object.reset();
}
::py::object signalConnect(SignalBase& sig,
const ::py::function& callback,
bool async)
{
GILAcquire lock;
return connect(
[&](const SignalSubscriber& sub) {
GILRelease unlock;
return sig.connectAsync(sub).andThen(FutureCallbackType_Sync,
[](const SignalSubscriber& sub) {
return sub.link();
});
},
callback, async);
}
::py::object signalDisconnect(SignalBase& sig, SignalLink id, bool async)
{
const auto fut =
sig.disconnectAsync(id).andThen(FutureCallbackType_Sync, [](bool success) {
return AnyValue::from(success);
});
GILAcquire lock;
return resultObject(fut, async);
}
::py::object signalDisconnectAll(SignalBase& sig, bool async)
{
const auto fut =
sig.disconnectAllAsync().andThen(FutureCallbackType_Sync, [](bool success) {
return AnyValue::from(success);
});
GILAcquire lock;
return resultObject(fut, async);
}
::py::object proxySignalConnect(const AnyObject& obj,
unsigned int signalId,
const ::py::function& callback,
bool async)
{
GILAcquire lock;
return connect(
[&](const SignalSubscriber& sub) {
GILRelease unlock;
return obj.connect(signalId, sub).async();
},
callback, async);
}
::py::object proxySignalDisconnect(const AnyObject& obj,
SignalLink id,
bool async)
{
const auto fut = [&] {
GILRelease unlock;
return toFuture(obj.disconnect(id));
}();
GILAcquire lock;
return resultObject(fut, async);
}
} // namespace detail
bool isSignal(const ::py::object& obj)
{
GILAcquire lock;
return ::py::isinstance<Signal>(obj) ||
::py::isinstance<detail::ProxySignal>(obj);
}
void exportSignal(::py::module& m)
{
using namespace ::py;
using namespace ::py::literals;
GILAcquire lock;
using SignalPtr = std::unique_ptr<Signal, DeleteOutsideGIL>;
class_<Signal, SignalPtr>(m, "Signal")
.def(init([](std::string signature, std::function<void(bool)> onConnect) {
qi::SignalBase::OnSubscribers onSub;
if (onConnect)
onSub = qi::futurizeOutput(onConnect);
return new Signal(signature, onSub);
}),
call_guard<GILRelease>(),
"signature"_a = "m", "onConnect"_a = std::function<void(bool)>())
.def(
"connect", &detail::signalConnect,
call_guard<GILRelease>(),
"callback"_a, arg(asyncArgName) = false,
doc(
"Connect the signal to a callback, the callback will be called each "
"time the signal is triggered. Use the id returned to unregister the "
"callback."
":param callback: the callback that will be called when the signal is "
"triggered.\n"
":returns: the connection id of the registered callback."))
.def("disconnect", &detail::signalDisconnect,
call_guard<GILRelease>(), "id"_a, arg(asyncArgName) = false,
doc("Disconnect the callback associated to id.\n"
":param id: the connection id returned by connect.\n"
":returns: true on success."))
.def("disconnectAll", &detail::signalDisconnectAll,
call_guard<GILRelease>(), arg(asyncArgName) = false,
doc("Disconnect all subscribers associated to the property.\n\n"
"This function should be used with caution, as it may also remove "
"subscribers that were added by other callers.\n\n"
":returns: true on success\n"))
.def("__call__",
[](Signal& sig, args pyArgs) {
const auto args =
AnyReference::from(pyArgs).content().asTupleValuePtr();
GILRelease unlock;
sig.trigger(args);
},
doc("Trigger the signal"));
class_<detail::ProxySignal>(m, "_ProxySignal")
.def("connect", &proxySignalConnect, "callback"_a,
arg(asyncArgName) = false)
.def("disconnect",
[](detail::ProxySignal& sig, SignalLink id, bool async) {
return detail::proxySignalDisconnect(sig.object, id, async);
},
"id"_a, arg(asyncArgName) = false)
.def("__call__",
[](detail::ProxySignal& sig, args pyArgs) {
const auto args =
AnyReference::from(pyArgs).content().asTupleValuePtr();
GILRelease unlock;
sig.object.metaPost(sig.signalId, args);
});
}
} // namespace py
} // namespace qi