Skip to content

Commit 10f25e9

Browse files
client: add connection bookkeeping to Connector for waitAny robustness
Currently, all connection bookkeeping is done opaquely through the network providers which, in turn, also do this bookkeeping opaquely using system interfaces (e.g., libev, epoll). Because of this, we cannot handle cases when waitAny is called and there are no connections (gh-51) or when a connection has ready responses (gh-132). In order to improve `waitAny` robustness, we need to add connection bookkeeping to Connector. We should move the timer start to the beginning of the waiting loop, since the preceding checking overhead should not be accounted for the waiting time. Closes #51 Needed for #132
1 parent eb0fda9 commit 10f25e9

File tree

3 files changed

+34
-4
lines changed

3 files changed

+34
-4
lines changed

src/Client/Connection.hpp

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ struct ConnectionImpl
8383
const typename NetProvider::Stream_t &get_strm() const { return strm; }
8484

8585
void setError(const std::string &msg, int errno_ = 0);
86+
bool hasError() const;
8687

8788
BUFFER &getInBuf();
8889
BUFFER &getOutBuf();
@@ -153,6 +154,13 @@ ConnectionImpl<BUFFER, NetProvider>::setError(const std::string &msg, int errno_
153154
error.emplace(msg, errno_);
154155
}
155156

157+
template <class BUFFER, class NetProvider>
158+
bool
159+
ConnectionImpl<BUFFER, NetProvider>::hasError() const
160+
{
161+
return error.has_value();
162+
}
163+
156164
template <class BUFFER, class NetProvider>
157165
BUFFER &
158166
ConnectionImpl<BUFFER, NetProvider>::getInBuf()
@@ -465,7 +473,7 @@ template<class BUFFER, class NetProvider>
465473
bool
466474
Connection<BUFFER, NetProvider>::hasError() const
467475
{
468-
return impl->error.has_value();
476+
return impl->hasError();
469477
}
470478

471479
template<class BUFFER, class NetProvider>

src/Client/Connector.hpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,10 @@ class Connector
129129
* and `connectionDecodeResponses`.
130130
*/
131131
std::set<ConnectionImpl<BUFFER, NetProvider> *> m_ReadyToDecode;
132+
/**
133+
* Set of active connections owned by connector.
134+
*/
135+
std::set<ConnectionImpl<BUFFER, NetProvider> *> m_Connections;
132136
};
133137

134138
template<class BUFFER, class NetProvider>
@@ -161,6 +165,7 @@ Connector<BUFFER, NetProvider>::connect(Connection<BUFFER, NetProvider> &conn,
161165
}
162166
LOG_DEBUG("Connection to ", opts.address, ':', opts.service,
163167
" has been established");
168+
m_Connections.insert(conn.getImpl());
164169
return 0;
165170
}
166171

@@ -192,6 +197,7 @@ Connector<BUFFER, NetProvider>::close(ConnectionImpl<BUFFER, NetProvider> *conn)
192197
m_NetProvider.close(conn->get_strm());
193198
m_ReadyToSend.erase(conn);
194199
m_ReadyToDecode.erase(conn);
200+
m_Connections.erase(conn);
195201
}
196202
}
197203

@@ -356,9 +362,24 @@ template<class BUFFER, class NetProvider>
356362
std::optional<Connection<BUFFER, NetProvider>>
357363
Connector<BUFFER, NetProvider>::waitAny(int timeout)
358364
{
365+
if (m_Connections.empty()) {
366+
LOG_DEBUG("waitAny() called on connector without connections");
367+
return std::nullopt;
368+
}
359369
Timer timer{timeout};
360370
timer.start();
361371
while (m_ReadyToDecode.empty()) {
372+
bool has_alive_conn = false;
373+
for (auto *conn : m_Connections) {
374+
if (!conn->hasError()) {
375+
has_alive_conn = true;
376+
break;
377+
}
378+
}
379+
if (!has_alive_conn) {
380+
LOG_ERROR("All connections have an error");
381+
return std::nullopt;
382+
}
362383
if (m_NetProvider.wait(timer.timeLeft()) != 0) {
363384
LOG_ERROR("Failed to poll connections: ", strerror(errno));
364385
return std::nullopt;

test/ClientTest.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1118,11 +1118,9 @@ test_dead_connection_wait(void)
11181118
fail_if(client.waitCount(conn, 1) == 0);
11191119
fail_if(conn.futureIsReady(f));
11201120

1121-
/* FIXME(gh-51) */
1122-
#if 0
1121+
TEST_CASE("waitAny() correctly handles case when all connections have an error (gh-51");
11231122
fail_if(client.waitAny() != std::nullopt);
11241123
fail_if(conn.futureIsReady(f));
1125-
#endif
11261124
}
11271125

11281126
/**
@@ -1451,6 +1449,9 @@ test_wait(Connector<BUFFER, NetProvider> &client)
14511449
#endif /* __linux__ */
14521450

14531451
client.close(conn);
1452+
1453+
TEST_CASE("waitAny() correctly handles case when there are no connections (gh-51");
1454+
fail_if(client.waitAny() != std::nullopt);
14541455
}
14551456

14561457
int main()

0 commit comments

Comments
 (0)