@@ -26,8 +26,6 @@ namespace tcp {
2626// socket specified at construction. Upon completion or error, the
2727// callback is called. Its lifetime is coupled with completion of the
2828// operation, so the called doesn't need to hold on to the instance.
29- // It does so by storing a shared_ptr to itself (effectively a leak)
30- // until the event loop calls back.
3129template <typename T>
3230class ReadValueOperation final
3331 : public Handler,
@@ -36,29 +34,15 @@ class ReadValueOperation final
3634 using callback_t =
3735 std::function<void (std::shared_ptr<Socket>, const Error& error, T&& t)>;
3836
39- ReadValueOperation (
40- std::shared_ptr<Loop> loop,
41- std::shared_ptr<Socket> socket,
42- callback_t fn)
43- : loop_(std::move(loop)),
44- socket_ (std::move(socket)),
45- fn_(std::move(fn)) {}
37+ ReadValueOperation (std::shared_ptr<Socket> socket, callback_t fn)
38+ : socket_(std::move(socket)), fn_(std::move(fn)) {}
4639
47- void run () {
48- // Cannot initialize leak until after the object has been
49- // constructed, because the std::make_shared initialization
50- // doesn't run after construction of the underlying object.
51- leak_ = this ->shared_from_this ();
52- // Register with loop only after we've leaked the shared_ptr,
53- // because we unleak it when the event loop thread calls.
54- loop_->registerDescriptor (socket_->fd (), EPOLLIN | EPOLLONESHOT, this );
40+ void run (Loop& loop) {
41+ loop.registerDescriptor (
42+ socket_->fd (), EPOLLIN | EPOLLONESHOT, this ->shared_from_this ());
5543 }
5644
57- void handleEvents (int events) override {
58- // Move leaked shared_ptr to the stack so that this object
59- // destroys itself once this function returns.
60- auto self = std::move (this ->leak_ );
61-
45+ void handleEvents (Loop&, int /* events*/ ) override {
6246 // Read T.
6347 auto rv = socket_->read (&t_, sizeof (t_));
6448 if (rv == -1 ) {
@@ -80,30 +64,26 @@ class ReadValueOperation final
8064 }
8165
8266 private:
83- std::shared_ptr<Loop> loop_;
8467 std::shared_ptr<Socket> socket_;
8568 callback_t fn_;
86- std::shared_ptr<ReadValueOperation<T>> leak_;
8769
8870 T t_;
8971};
9072
9173template <typename T>
9274void read (
93- std::shared_ptr< Loop> loop,
75+ Loop& loop,
9476 std::shared_ptr<Socket> socket,
9577 typename ReadValueOperation<T>::callback_t fn) {
96- auto x = std::make_shared<ReadValueOperation<T>>(
97- std::move (loop), std::move (socket), std::move (fn));
98- x->run ();
78+ auto x =
79+ std::make_shared<ReadValueOperation<T>>( std::move (socket), std::move (fn));
80+ x->run (loop );
9981}
10082
10183// WriteValueOperation asynchronously writes a value of type T to the
10284// socket specified at construction. Upon completion or error, the
10385// callback is called. Its lifetime is coupled with completion of the
10486// operation, so the called doesn't need to hold on to the instance.
105- // It does so by storing a shared_ptr to itself (effectively a leak)
106- // until the event loop calls back.
10787template <typename T>
10888class WriteValueOperation final
10989 : public Handler,
@@ -112,31 +92,15 @@ class WriteValueOperation final
11292 using callback_t =
11393 std::function<void (std::shared_ptr<Socket>, const Error& error)>;
11494
115- WriteValueOperation (
116- std::shared_ptr<Loop> loop,
117- std::shared_ptr<Socket> socket,
118- T t,
119- callback_t fn)
120- : loop_(std::move(loop)),
121- socket_ (std::move(socket)),
122- fn_(std::move(fn)),
123- t_(std::move(t)) {}
124-
125- void run () {
126- // Cannot initialize leak until after the object has been
127- // constructed, because the std::make_shared initialization
128- // doesn't run after construction of the underlying object.
129- leak_ = this ->shared_from_this ();
130- // Register with loop only after we've leaked the shared_ptr,
131- // because we unleak it when the event loop thread calls.
132- loop_->registerDescriptor (socket_->fd (), EPOLLOUT | EPOLLONESHOT, this );
133- }
95+ WriteValueOperation (std::shared_ptr<Socket> socket, T t, callback_t fn)
96+ : socket_(std::move(socket)), fn_(std::move(fn)), t_(std::move(t)) {}
13497
135- void handleEvents ( int events) override {
136- // Move leaked shared_ptr to the stack so that this object
137- // destroys itself once this function returns.
138- auto leak = std::move ( this -> leak_ );
98+ void run (Loop& loop) {
99+ loop. registerDescriptor (
100+ socket_-> fd (), EPOLLOUT | EPOLLONESHOT, this -> shared_from_this ());
101+ }
139102
103+ void handleEvents (Loop&, int /* events*/ ) override {
140104 // Write T.
141105 auto rv = socket_->write (&t_, sizeof (t_));
142106 if (rv == -1 ) {
@@ -154,33 +118,30 @@ class WriteValueOperation final
154118 }
155119
156120 private:
157- std::shared_ptr<Loop> loop_;
158121 std::shared_ptr<Socket> socket_;
159122 callback_t fn_;
160- std::shared_ptr<WriteValueOperation<T>> leak_;
161123
162124 T t_;
163125};
164126
165127template <typename T>
166128void write (
167- std::shared_ptr< Loop> loop,
129+ Loop& loop,
168130 std::shared_ptr<Socket> socket,
169131 T t,
170132 typename WriteValueOperation<T>::callback_t fn) {
171133 auto x = std::make_shared<WriteValueOperation<T>>(
172- std::move (loop), std::move ( socket), std::move (t), std::move (fn));
173- x->run ();
134+ std::move (socket), std::move (t), std::move (fn));
135+ x->run (loop );
174136}
175137
176138class ConnectOperation final
177139 : public Handler,
178140 public std::enable_shared_from_this<ConnectOperation> {
179141 public:
180- using callback_t =
181- std::function< void (std::shared_ptr<Socket>, const Error& error)>;
142+ using callback_t = std::function<
143+ void (Loop& loop, std::shared_ptr<Socket>, const Error& error)>;
182144 ConnectOperation (
183- std::shared_ptr<Loop> loop,
184145 const Address& remote,
185146 const int rank,
186147 const int size,
@@ -190,15 +151,9 @@ class ConnectOperation final
190151 rank_ (rank),
191152 size_(size),
192153 deadline_(std::chrono::steady_clock::now() + timeout),
193- loop_(std::move(loop)),
194154 fn_(std::move(fn)) {}
195155
196- void run () {
197- // Cannot initialize leak until after the object has been
198- // constructed, because the std::make_shared initialization
199- // doesn't run after construction of the underlying object.
200- leak_ = this ->shared_from_this ();
201-
156+ void run (Loop& loop) {
202157 const auto & sockaddr = remote_.getSockaddr ();
203158
204159 // Create new socket to connect to peer.
@@ -207,29 +162,26 @@ class ConnectOperation final
207162 socket_->noDelay (true );
208163 socket_->connect (sockaddr);
209164
210- // Register with loop only after we've leaked the shared_ptr,
211- // because we unleak it when the event loop thread calls.
212165 // Register for EPOLLOUT, because we want to be notified when
213166 // the connect completes. EPOLLERR is also necessary because
214167 // connect() can fail.
215- if (auto loop = loop_.lock ()) {
216- loop->registerDescriptor (
217- socket_->fd (), EPOLLOUT | EPOLLERR | EPOLLONESHOT, this );
218- } else {
219- fn_ (socket_, LoopError (" loop is gone" ));
220- }
168+ loop.registerDescriptor (
169+ socket_->fd (),
170+ EPOLLOUT | EPOLLERR | EPOLLONESHOT,
171+ this ->shared_from_this ());
221172 }
222173
223- void handleEvents (int events) override {
224- // Move leaked shared_ptr to the stack so that this object
225- // destroys itself once this function returns.
226- auto leak = std::move (this ->leak_ );
174+ void handleEvents (Loop& loop, int /* events*/ ) override {
175+ // Hold a reference to this object to keep it alive until the
176+ // callback is called.
177+ auto leak = shared_from_this ();
178+ loop.unregisterDescriptor (socket_->fd (), this );
227179
228180 int result;
229181 socklen_t result_len = sizeof (result);
230182 if (getsockopt (socket_->fd (), SOL_SOCKET, SO_ERROR, &result, &result_len) <
231183 0 ) {
232- fn_ (socket_, SystemError (" getsockopt" , errno, remote_));
184+ fn_ (loop, socket_, SystemError (" getsockopt" , errno, remote_));
233185 return ;
234186 }
235187 if (result != 0 ) {
@@ -248,16 +200,18 @@ class ConnectOperation final
248200 socket_->sockName ().str (),
249201 };
250202 DebugLogger::log (debugData);
203+
251204 // check deadline
252205 if (willRetry) {
253- run ();
206+ run (loop );
254207 } else {
255- fn_ (socket_, TimeoutError (" timed out connecting: " + e.what ()));
208+ fn_ (loop, socket_, TimeoutError (" timed out connecting: " + e.what ()));
256209 }
210+
257211 return ;
258212 }
259213
260- fn_ (socket_, Error::kSuccess );
214+ fn_ (loop, socket_, Error::kSuccess );
261215 }
262216
263217 private:
@@ -269,16 +223,12 @@ class ConnectOperation final
269223
270224 int retry_{0 };
271225
272- // We use a weak_ptr to the loop to avoid a reference cycle when an error
273- // occurs.
274- std::weak_ptr<Loop> loop_;
275226 std::shared_ptr<Socket> socket_;
276227 callback_t fn_;
277- std::shared_ptr<ConnectOperation> leak_;
278228};
279229
280230void connectLoop (
281- std::shared_ptr< Loop> loop,
231+ Loop& loop,
282232 const Address& remote,
283233 const int rank,
284234 const int size,
0 commit comments