Skip to content

Commit 217484b

Browse files
authored
Client does not shut down properly when cluster connection attempts all fail (#285)
* Fix for shutting down the client properly if the cluster connection attempts all fail. The Thread join now will detect if it is being called from running thread and avoid deadlock. * Corrected the shutdown order of services, changed to be the same as Java client. * Corrected what is being tested at IssueTest.issue221. * Prevented client destruction before shutdown. * Fixes when IOSelector sets isAlive to true and prevents wakeup if the wakeup socket is not initialized yet. * Added connection closing when ConnectionManager is shutting down. * Corrected the error message printing for protocol messages.
1 parent d26ade3 commit 217484b

File tree

18 files changed

+188
-38
lines changed

18 files changed

+188
-38
lines changed

hazelcast/include/hazelcast/client/connection/Connection.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ namespace hazelcast {
6868

6969
void connect(int timeoutInMillis);
7070

71-
void close();
71+
void close(const char *closeReason = NULL);
7272

7373
void write(protocol::ClientMessage *message);
7474

hazelcast/include/hazelcast/client/exception/ProtocolExceptions.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@ namespace hazelcast {
3333
namespace exception {
3434
class HAZELCAST_API ProtocolException : public IException {
3535
public:
36-
ProtocolException(const std::string& source, const std::string& message, int32_t errorNo, int32_t causeCode)
37-
: IException(source, message), errorCode(errorNo), causeErrorCode(causeCode) {
36+
ProtocolException(const std::string& message, const std::string& details, int32_t errorNo,
37+
int32_t causeCode)
38+
: IException("Cluster", details), errorCode(errorNo), causeErrorCode(causeCode) {
3839
}
3940

4041
ProtocolException(const std::string& source, const std::string& message)
@@ -56,8 +57,8 @@ namespace hazelcast {
5657
#define DEFINE_PROTOCOL_EXCEPTION(ClassName) \
5758
class HAZELCAST_API ClassName : public ProtocolException {\
5859
public:\
59-
ClassName(const std::string& source, const std::string& message, int32_t errorCode, int32_t causeCode) \
60-
: ProtocolException(source, message, errorCode, causeCode) {\
60+
ClassName(const std::string& message, const std::string& details, int32_t errorNo, int32_t causeCode) \
61+
: ProtocolException(message, details, errorNo, causeCode) {\
6162
}\
6263
ClassName(const std::string& source, const std::string& message) \
6364
: ProtocolException(source, message) {\

hazelcast/include/hazelcast/client/spi/LifecycleService.h

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@
1616
//
1717
// Created by sancar koyunlu on 6/17/13.
1818

19-
20-
21-
2219
#ifndef HAZELCAST_LIFECYCLE_SERVICE
2320
#define HAZELCAST_LIFECYCLE_SERVICE
2421

@@ -67,7 +64,7 @@ namespace hazelcast {
6764
std::set<LifecycleListener *> listeners;
6865
util::Mutex listenerLock;
6966
util::AtomicBoolean active;
70-
67+
util::Mutex shutdownLock;
7168
};
7269

7370
}

hazelcast/include/hazelcast/util/Closeable.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace hazelcast {
2727
public:
2828
virtual ~Closeable();
2929

30-
virtual void close() = 0;
30+
virtual void close(const char *closeReason) = 0;
3131
};
3232
}
3333
}

hazelcast/include/hazelcast/util/IOUtil.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ namespace hazelcast {
4646
return value;
4747
}
4848

49-
static void closeResource(Closeable *closable);
49+
static void closeResource(Closeable *closable, const char *closeReason = NULL);
5050

5151
};
5252
}

hazelcast/src/hazelcast/client/connection/ClusterListenerThread.cpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@ namespace hazelcast {
8484
util::ILogger::getLogger().severe(
8585
std::string("Error while connecting to cluster! =>") + e.what());
8686
isStartedSuccessfully = false;
87+
clientContext.getLifecycleService().shutdown();
8788
startLatch.countDown();
8889
return;
8990
}
@@ -105,7 +106,7 @@ namespace hazelcast {
105106

106107
clientContext.getConnectionManager().onCloseOwnerConnection();
107108
if (deletingConnection.compareAndSet(false, true)) {
108-
util::IOUtil::closeResource(conn.get());
109+
util::IOUtil::closeResource(conn.get(), "Error while listening cluster events");
109110
conn.reset();
110111
deletingConnection = false;
111112
clientContext.getLifecycleService().fireLifecycleEvent(LifecycleEvent::CLIENT_DISCONNECTED);
@@ -117,7 +118,7 @@ namespace hazelcast {
117118

118119
void ClusterListenerThread::stop() {
119120
if (deletingConnection.compareAndSet(false, true)) {
120-
util::IOUtil::closeResource(conn.get());
121+
util::IOUtil::closeResource(conn.get(), "Cluster listener thread is stopping");
121122
conn.reset();
122123
deletingConnection = false;
123124
}

hazelcast/src/hazelcast/client/connection/Connection.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ namespace hazelcast {
8989
outputSocketStream.write(PROTOCOL);
9090
}
9191

92-
void Connection::close() {
92+
void Connection::close(const char *closeReason) {
9393
if (!live.compareAndSet(true, false)) {
9494
return;
9595
}
@@ -98,8 +98,10 @@ namespace hazelcast {
9898
int socketId = socket->getSocketId();
9999

100100
std::stringstream message;
101-
message << "Closing connection (id:" << connectionId << ") to " << serverAddr << " with socket id " << socketId <<
102-
(_isOwnerConnection ? " as the owner connection." : ".");
101+
message << "Closing connection (id:" << connectionId << ") to " << serverAddr <<
102+
" with socket id " << socketId <<
103+
(_isOwnerConnection ? " as the owner connection." : ". ") <<
104+
(NULL != closeReason ? closeReason : "");
103105
util::ILogger::getLogger().warning(message.str());
104106
if (!_isOwnerConnection) {
105107
readHandler.deRegisterSocket();

hazelcast/src/hazelcast/client/connection/ConnectionManager.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
//
1717
// Created by sancar koyunlu on 8/21/13.
1818

19+
#include <boost/foreach.hpp>
1920
#include "hazelcast/util/Util.h"
2021
#include "hazelcast/client/protocol/AuthenticationStatus.h"
2122
#include "hazelcast/client/exception/AuthenticationException.h"
@@ -69,6 +70,10 @@ namespace hazelcast {
6970

7071
void ConnectionManager::shutdown() {
7172
live = false;
73+
// close connections
74+
BOOST_FOREACH(boost::shared_ptr<Connection> connection , connections.values()) {
75+
connection->close("Hazelcast client is shutting down");
76+
}
7277
heartBeater.shutdown();
7378
if (heartBeatThread.get() != NULL) {
7479
heartBeatThread->cancel();
@@ -350,7 +355,7 @@ namespace hazelcast {
350355

351356
void ConnectionManager::checkLive() {
352357
if (!live) {
353-
throw exception::HazelcastException("ConnectionManager is not active!");
358+
throw exception::HazelcastException("ConnectionManager", "ConnectionManager is not active!");
354359
}
355360
}
356361

@@ -384,7 +389,7 @@ namespace hazelcast {
384389
} else {
385390
ownerConnectionFuture.close();
386391
}
387-
util::IOUtil::closeResource(&connection);
392+
util::IOUtil::closeResource(&connection, "Heartbeat failed");
388393
}
389394

390395
void ConnectionManager::removeEndpoint(const Address &address) {

hazelcast/src/hazelcast/client/connection/IOSelector.cpp

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@ namespace hazelcast {
4242
:connectionManager(connectionManager) {
4343
t.tv_sec = 5;
4444
t.tv_usec = 0;
45-
isAlive = true;
4645
}
4746

4847
void IOSelector::staticListen(util::ThreadArgs &args) {
@@ -55,6 +54,10 @@ namespace hazelcast {
5554
}
5655

5756
void IOSelector::wakeUp() {
57+
if (!wakeUpSocket.get()) {
58+
return;
59+
}
60+
5861
int wakeUpSignal = 9;
5962
try {
6063
wakeUpSocket->send(&wakeUpSignal, sizeof(int));
@@ -91,6 +94,7 @@ namespace hazelcast {
9194
sleepingSocket->setBlocking(false);
9295
wakeUpSocketSet.insertSocket(sleepingSocket.get());
9396
wakeUpListenerSocketId = sleepingSocket->getSocketId();
97+
isAlive = true;
9498
return true;
9599
} else {
96100
util::ILogger::getLogger().severe("IOSelector::initListenSocket " + std::string(strerror(errno)));
@@ -99,7 +103,14 @@ namespace hazelcast {
99103
}
100104

101105
void IOSelector::shutdown() {
102-
isAlive = false;
106+
if (!isAlive.compareAndSet(true, false)) {
107+
return;
108+
}
109+
try {
110+
wakeUp();
111+
} catch (exception::IOException &) {
112+
// suppress io exception
113+
}
103114
}
104115

105116
void IOSelector::addTask(ListenerTask *listenerTask) {

hazelcast/src/hazelcast/client/connection/OwnerConnectionFuture.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ namespace hazelcast {
9191
std::stringstream message;
9292
message << "Closing owner connection to " << currentOwnerConnection->getRemoteEndpoint();
9393
util::ILogger::getLogger().finest(message.str());
94-
util::IOUtil::closeResource(currentOwnerConnection.get());
94+
util::IOUtil::closeResource(currentOwnerConnection.get(), message.str().c_str());
9595
markAsClosed();
9696
}
9797
}

0 commit comments

Comments
 (0)