diff options
155 files changed, 4028 insertions, 889 deletions
diff --git a/cpp/include/Ice/ConnectionAsync.h b/cpp/include/Ice/ConnectionAsync.h index bc3f6064525..58f54cc3492 100644 --- a/cpp/include/Ice/ConnectionAsync.h +++ b/cpp/include/Ice/ConnectionAsync.h @@ -112,6 +112,99 @@ newCallback_Connection_flushBatchRequests(T* instance, void (T::*excb)(const ::I return new Callback_Connection_flushBatchRequests<T, CT>(instance, excb, sentcb); } +template<class T> +class CallbackNC_Connection_heartbeat : public Callback_Connection_heartbeat_Base, + public ::IceInternal::OnewayCallbackNC<T> +{ +public: + + typedef IceUtil::Handle<T> TPtr; + + typedef void (T::*Exception)(const ::Ice::Exception&); + typedef void (T::*Sent)(bool); + + CallbackNC_Connection_heartbeat(const TPtr& obj, Exception excb, Sent sentcb) + : ::IceInternal::OnewayCallbackNC<T>(obj, 0, excb, sentcb) + { + } + + virtual void completed(const ::Ice::AsyncResultPtr& __result) const + { + ::Ice::ConnectionPtr __con = __result->getConnection(); + assert(__con); + try + { + __con->end_heartbeat(__result); + assert(false); + } + catch(const ::Ice::Exception& ex) + { + ::IceInternal::CallbackNC<T>::exception(__result, ex); + } + } +}; + +template<class T> Callback_Connection_heartbeatPtr +newCallback_Connection_heartbeat(const IceUtil::Handle<T>& instance, + void (T::*excb)(const ::Ice::Exception&), + void (T::*sentcb)(bool) = 0) +{ + return new CallbackNC_Connection_heartbeat<T>(instance, excb, sentcb); +} + +template<class T> Callback_Connection_heartbeatPtr +newCallback_Connection_heartbeat(T* instance, void (T::*excb)(const ::Ice::Exception&), void (T::*sentcb)(bool) = 0) +{ + return new CallbackNC_Connection_heartbeat<T>(instance, excb, sentcb); +} + +template<class T, typename CT> +class Callback_Connection_heartbeat : public Callback_Connection_heartbeat_Base, + public ::IceInternal::OnewayCallback<T, CT> +{ +public: + + typedef IceUtil::Handle<T> TPtr; + + typedef void (T::*Exception)(const ::Ice::Exception& , const CT&); + typedef void (T::*Sent)(bool , const CT&); + + Callback_Connection_heartbeat(const TPtr& obj, Exception excb, Sent sentcb) + : ::IceInternal::OnewayCallback<T, CT>(obj, 0, excb, sentcb) + { + } + + virtual void completed(const ::Ice::AsyncResultPtr& __result) const + { + ::Ice::ConnectionPtr __con = __result->getConnection(); + assert(__con); + try + { + __con->end_heartbeat(__result); + assert(false); + } + catch(const ::Ice::Exception& ex) + { + ::IceInternal::Callback<T, CT>::exception(__result, ex); + } + } +}; + +template<class T, typename CT> Callback_Connection_heartbeatPtr +newCallback_Connection_heartbeat(const IceUtil::Handle<T>& instance, + void (T::*excb)(const ::Ice::Exception&, const CT&), + void (T::*sentcb)(bool, const CT&) = 0) +{ + return new Callback_Connection_heartbeat<T, CT>(instance, excb, sentcb); +} + +template<class T, typename CT> Callback_Connection_heartbeatPtr +newCallback_Connection_heartbeat(T* instance, void (T::*excb)(const ::Ice::Exception&, const CT&), + void (T::*sentcb)(bool, const CT&) = 0) +{ + return new Callback_Connection_heartbeat<T, CT>(instance, excb, sentcb); +} + } #endif diff --git a/cpp/src/Glacier2/RoutingTable.cpp b/cpp/src/Glacier2/RoutingTable.cpp index fde938fba00..e6b090de4cb 100644 --- a/cpp/src/Glacier2/RoutingTable.cpp +++ b/cpp/src/Glacier2/RoutingTable.cpp @@ -65,7 +65,7 @@ Glacier2::RoutingTable::add(const ObjectProxySeq& unfiltered, const Current& cur if(!_verifier->verify(*prx)) { - current.con->close(true); + current.con->close(CloseForcefully); throw ObjectNotExistException(__FILE__, __LINE__); } ObjectPrx proxy = (*prx)->ice_twoway()->ice_secure(false)->ice_facet(""); // We add proxies in default form. diff --git a/cpp/src/Glacier2/SessionRouterI.cpp b/cpp/src/Glacier2/SessionRouterI.cpp index 9f774b79a8e..512d371611f 100644 --- a/cpp/src/Glacier2/SessionRouterI.cpp +++ b/cpp/src/Glacier2/SessionRouterI.cpp @@ -80,7 +80,7 @@ public: // Close the connection otherwise the peer has no way to know that // the session has gone. // - _connection->close(true); + _connection->close(CloseForcefully); _router->destroySession(_connection); } } @@ -922,7 +922,7 @@ SessionRouterI::refreshSession(const Ice::ConnectionPtr& con) // Close the connection otherwise the peer has no way to know that the // session has gone. // - con->close(true); + con->close(CloseForcefully); throw SessionNotExistException(); } } @@ -1149,10 +1149,10 @@ SessionRouterI::getRouterImpl(const ConnectionPtr& connection, const Ice::Identi if(_rejectTraceLevel >= 1) { Trace out(_instance->logger(), "Glacier2"); - out << "rejecting request. no session is associated with the connection.\n"; - out << "identity: " << _instance->communicator()->identityToString(id); + out << "rejecting request, no session is associated with the connection.\n"; + out << "identity: " << identityToString(id); } - connection->close(true); + connection->close(CloseForcefully); throw ObjectNotExistException(__FILE__, __LINE__); } return 0; diff --git a/cpp/src/Ice/ConnectionI.cpp b/cpp/src/Ice/ConnectionI.cpp index 7845259855c..23388d399c6 100644 --- a/cpp/src/Ice/ConnectionI.cpp +++ b/cpp/src/Ice/ConnectionI.cpp @@ -409,29 +409,31 @@ Ice::ConnectionI::destroy(DestructionReason reason) } void -Ice::ConnectionI::close(bool force) +Ice::ConnectionI::close(ConnectionClose mode) { IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this); - if(force) + if(mode == CloseForcefully) { - setState(StateClosed, ForcedCloseConnectionException(__FILE__, __LINE__)); + setState(StateClosed, ConnectionManuallyClosedException(__FILE__, __LINE__, false)); + } + else if(mode == CloseGracefully) + { + setState(StateClosing, ConnectionManuallyClosedException(__FILE__, __LINE__, true)); } else { + assert(mode == CloseGracefullyAndWait); + // - // If we do a graceful shutdown, then we wait until all - // outstanding requests have been completed. Otherwise, the - // CloseConnectionException will cause all outstanding - // requests to be retried, regardless of whether the server - // has processed them or not. + // Wait until all outstanding requests have been completed. // while(!_asyncRequests.empty()) { wait(); } - setState(StateClosing, CloseConnectionException(__FILE__, __LINE__)); + setState(StateClosing, ConnectionManuallyClosedException(__FILE__, __LINE__, true)); } } @@ -550,13 +552,13 @@ Ice::ConnectionI::monitor(const IceUtil::Time& now, const ACMConfig& acm) // // We send a heartbeat if there was no activity in the last // (timeout / 4) period. Sending a heartbeat sooner than really - // needed is safer to ensure that the receiver will receive in - // time the heartbeat. Sending the heartbeat if there was no + // needed is safer to ensure that the receiver will receive the + // heartbeat in time. Sending the heartbeat if there was no // activity in the last (timeout / 2) period isn't enough since // monitor() is called only every (timeout / 2) period. // // Note that this doesn't imply that we are sending 4 heartbeats - // per timeout period because the monitor() method is sill only + // per timeout period because the monitor() method is still only // called every (timeout / 2) period. // if(acm.heartbeat == HeartbeatAlways || @@ -564,7 +566,7 @@ Ice::ConnectionI::monitor(const IceUtil::Time& now, const ACMConfig& acm) { if(acm.heartbeat != HeartbeatOnInvocation || _dispatchCount > 0) { - heartbeat(); + sendHeartbeatNow(); } } @@ -802,6 +804,174 @@ Ice::ConnectionI::end_flushBatchRequests(const AsyncResultPtr& r) } #endif +namespace +{ + +const ::std::string __heartbeat_name = "heartbeat"; + +class HeartbeatAsync : public OutgoingAsyncBase +{ +public: + + HeartbeatAsync(const ConnectionIPtr& connection, + const CommunicatorPtr& communicator, + const InstancePtr& instance) : + OutgoingAsyncBase(instance), + _communicator(communicator), + _connection(connection) + { + } + + virtual CommunicatorPtr getCommunicator() const + { + return _communicator; + } + + virtual ConnectionPtr getConnection() const + { + return _connection; + } + + virtual const string& getOperation() const + { + return __heartbeat_name; + } + + void invoke() + { + _observer.attach(_instance.get(), __heartbeat_name); + try + { + _os.write(magic[0]); + _os.write(magic[1]); + _os.write(magic[2]); + _os.write(magic[3]); + _os.write(currentProtocol); + _os.write(currentProtocolEncoding); + _os.write(validateConnectionMsg); + _os.write(static_cast<Byte>(0)); // Compression status (always zero for validate connection). + _os.write(headerSize); // Message size. + _os.i = _os.b.begin(); + + AsyncStatus status = _connection->sendAsyncRequest(ICE_SHARED_FROM_THIS, false, false, 0); + if(status & AsyncStatusSent) + { + _sentSynchronously = true; + if(status & AsyncStatusInvokeSentCallback) + { + invokeSent(); + } + } + } + catch(const RetryException& ex) + { + if(exception(*ex.get())) + { + invokeExceptionAsync(); + } + } + catch(const Exception& ex) + { + if(exception(ex)) + { + invokeExceptionAsync(); + } + } + } + +private: + + CommunicatorPtr _communicator; + ConnectionIPtr _connection; +}; +typedef IceUtil::Handle<HeartbeatAsync> HeartbeatAsyncPtr; + +} + +#ifdef ICE_CPP11_MAPPING +void +Ice::ConnectionI::heartbeat() +{ + Connection::heartbeatAsync().get(); +} + +std::function<void()> +Ice::ConnectionI::heartbeatAsync(::std::function<void(::std::exception_ptr)> ex, ::std::function<void(bool)> sent) +{ + class HeartbeatLambda : public HeartbeatAsync, public LambdaInvoke + { + public: + + HeartbeatLambda(std::shared_ptr<Ice::ConnectionI>&& connection, + std::shared_ptr<Ice::Communicator>& communicator, + const InstancePtr& instance, + std::function<void(std::exception_ptr)> ex, + std::function<void(bool)> sent) : + HeartbeatAsync(connection, communicator, instance), LambdaInvoke(std::move(ex), std::move(sent)) + { + } + }; + auto outAsync = make_shared<HeartbeatLambda>(ICE_SHARED_FROM_THIS, _communicator, _instance, ex, sent); + outAsync->invoke(); + return [outAsync]() { outAsync->cancel(); }; +} +#else +void +Ice::ConnectionI::heartbeat() +{ + end_heartbeat(begin_heartbeat()); +} + +AsyncResultPtr +Ice::ConnectionI::begin_heartbeat() +{ + return _iceI_begin_heartbeat(dummyCallback, 0); +} + +AsyncResultPtr +Ice::ConnectionI::begin_heartbeat(const CallbackPtr& cb, const LocalObjectPtr& cookie) +{ + return _iceI_begin_heartbeat(cb, cookie); +} + +AsyncResultPtr +Ice::ConnectionI::begin_heartbeat(const Callback_Connection_heartbeatPtr& cb, const LocalObjectPtr& cookie) +{ + return _iceI_begin_heartbeat(cb, cookie); +} + +AsyncResultPtr +Ice::ConnectionI::_iceI_begin_heartbeat(const CallbackBasePtr& cb, const LocalObjectPtr& cookie) +{ + class HeartbeatCallback : public HeartbeatAsync, public CallbackCompletion + { + public: + + HeartbeatCallback(const ConnectionIPtr& connection, + const CommunicatorPtr& communicator, + const InstancePtr& instance, + const CallbackBasePtr& callback, + const LocalObjectPtr& cookie) : + HeartbeatAsync(connection, communicator, instance), + CallbackCompletion(callback, cookie) + { + _cookie = cookie; + } + }; + + HeartbeatAsyncPtr result = new HeartbeatCallback(this, _communicator, _instance, cb, cookie); + result->invoke(); + return result; +} + +void +Ice::ConnectionI::end_heartbeat(const AsyncResultPtr& r) +{ + AsyncResult::check(r, this, __heartbeat_name); + r->waitForResponse(); +} +#endif + void Ice::ConnectionI::setHeartbeatCallback(ICE_IN(ICE_HEARTBEAT_CALLBACK) callback) { @@ -1755,7 +1925,7 @@ Ice::ConnectionI::finish(bool close) out << "closed " << _endpoint->protocol() << " connection\n" << toString(); if(!(dynamic_cast<const CloseConnectionException*>(_exception.get()) || - dynamic_cast<const ForcedCloseConnectionException*>(_exception.get()) || + dynamic_cast<const ConnectionManuallyClosedException*>(_exception.get()) || dynamic_cast<const ConnectionTimeoutException*>(_exception.get()) || dynamic_cast<const CommunicatorDestroyedException*>(_exception.get()) || dynamic_cast<const ObjectAdapterDeactivatedException*>(_exception.get()))) @@ -2076,7 +2246,7 @@ Ice::ConnectionI::setState(State state, const LocalException& ex) // Don't warn about certain expected exceptions. // if(!(dynamic_cast<const CloseConnectionException*>(&ex) || - dynamic_cast<const ForcedCloseConnectionException*>(&ex) || + dynamic_cast<const ConnectionManuallyClosedException*>(&ex) || dynamic_cast<const ConnectionTimeoutException*>(&ex) || dynamic_cast<const CommunicatorDestroyedException*>(&ex) || dynamic_cast<const ObjectAdapterDeactivatedException*>(&ex) || @@ -2255,7 +2425,7 @@ Ice::ConnectionI::setState(State state) if(_observer && state == StateClosed && _exception) { if(!(dynamic_cast<const CloseConnectionException*>(_exception.get()) || - dynamic_cast<const ForcedCloseConnectionException*>(_exception.get()) || + dynamic_cast<const ConnectionManuallyClosedException*>(_exception.get()) || dynamic_cast<const ConnectionTimeoutException*>(_exception.get()) || dynamic_cast<const CommunicatorDestroyedException*>(_exception.get()) || dynamic_cast<const ObjectAdapterDeactivatedException*>(_exception.get()) || @@ -2285,8 +2455,7 @@ Ice::ConnectionI::setState(State state) void Ice::ConnectionI::initiateShutdown() { - assert(_state == StateClosing); - assert(_dispatchCount == 0); + assert(_state == StateClosing && _dispatchCount == 0); if(_shutdownInitiated) { @@ -2316,7 +2485,7 @@ Ice::ConnectionI::initiateShutdown() setState(StateClosingPending); // - // Notify the the transceiver of the graceful connection closure. + // Notify the transceiver of the graceful connection closure. // SocketOperation op = _transceiver->closing(true, *_exception); if(op) @@ -2329,7 +2498,7 @@ Ice::ConnectionI::initiateShutdown() } void -Ice::ConnectionI::heartbeat() +Ice::ConnectionI::sendHeartbeatNow() { assert(_state == StateActive); @@ -3016,7 +3185,7 @@ Ice::ConnectionI::parseMessage(InputStream& stream, Int& invokeNum, Int& request setState(StateClosingPending, CloseConnectionException(__FILE__, __LINE__)); // - // Notify the the transceiver of the graceful connection closure. + // Notify the transceiver of the graceful connection closure. // SocketOperation op = _transceiver->closing(false, *_exception); if(op) diff --git a/cpp/src/Ice/ConnectionI.h b/cpp/src/Ice/ConnectionI.h index 72652518b94..7fb3781e880 100644 --- a/cpp/src/Ice/ConnectionI.h +++ b/cpp/src/Ice/ConnectionI.h @@ -157,12 +157,12 @@ public: void activate(); void hold(); void destroy(DestructionReason); - virtual void close(bool); // From Connection. + virtual void close(ConnectionClose); // From Connection. bool isActiveOrHolding() const; bool isFinished() const; - void throwException() const; // Throws the connection exception if destroyed. + virtual void throwException() const; // From Connection. Throws the connection exception if destroyed. void waitUntilHolding() const; void waitUntilFinished(); // Not const, as this might close the connection upon timeout. @@ -193,6 +193,19 @@ public: virtual void setCloseCallback(ICE_IN(ICE_CLOSE_CALLBACK)); virtual void setHeartbeatCallback(ICE_IN(ICE_HEARTBEAT_CALLBACK)); + virtual void heartbeat(); + +#ifdef ICE_CPP11_MAPPING + virtual std::function<void()> + heartbeatAsync(::std::function<void(::std::exception_ptr)>, ::std::function<void(bool)> = nullptr); +#else + virtual AsyncResultPtr begin_heartbeat(); + virtual AsyncResultPtr begin_heartbeat(const CallbackPtr&, const LocalObjectPtr& = 0); + virtual AsyncResultPtr begin_heartbeat(const Callback_Connection_heartbeatPtr&, const LocalObjectPtr& = 0); + + virtual void end_heartbeat(const AsyncResultPtr&); +#endif + virtual void setACM(const IceUtil::Optional<int>&, const IceUtil::Optional<ACMClose>&, const IceUtil::Optional<ACMHeartbeat>&); @@ -276,7 +289,7 @@ private: void setState(State); void initiateShutdown(); - void heartbeat(); + void sendHeartbeatNow(); bool initialize(IceInternal::SocketOperation = IceInternal::SocketOperationNone); bool validate(IceInternal::SocketOperation = IceInternal::SocketOperationNone); @@ -308,6 +321,7 @@ private: #ifndef ICE_CPP11_MAPPING AsyncResultPtr _iceI_begin_flushBatchRequests(const IceInternal::CallbackBasePtr&, const LocalObjectPtr&); + AsyncResultPtr _iceI_begin_heartbeat(const IceInternal::CallbackBasePtr&, const LocalObjectPtr&); #endif Ice::CommunicatorPtr _communicator; diff --git a/cpp/src/Ice/Exception.cpp b/cpp/src/Ice/Exception.cpp index 4ac7d66cdd3..a20c564101e 100644 --- a/cpp/src/Ice/Exception.cpp +++ b/cpp/src/Ice/Exception.cpp @@ -632,14 +632,10 @@ Ice::CloseConnectionException::ice_print(ostream& out) const } void -Ice::ForcedCloseConnectionException::ice_print(ostream& out) const +Ice::ConnectionManuallyClosedException::ice_print(ostream& out) const { Exception::ice_print(out); - out << ":\nprotocol error: connection forcefully closed"; - if(!reason.empty()) - { - out << ":\n" << reason; - } + out << ":\nprotocol error: connection manually closed (" << (graceful ? "gracefully" : "forcefully") << ")"; } void diff --git a/cpp/src/Ice/ProxyFactory.cpp b/cpp/src/Ice/ProxyFactory.cpp index eba949508b6..34503ec961b 100644 --- a/cpp/src/Ice/ProxyFactory.cpp +++ b/cpp/src/Ice/ProxyFactory.cpp @@ -201,11 +201,12 @@ IceInternal::ProxyFactory::checkRetryAfterException(const LocalException& ex, co } // - // Don't retry if the communicator is destroyed or object adapter - // deactivated. + // Don't retry if the communicator is destroyed, object adapter is deactivated, + // or connection is manually closed. // if(dynamic_cast<const CommunicatorDestroyedException*>(&ex) || - dynamic_cast<const ObjectAdapterDeactivatedException*>(&ex)) + dynamic_cast<const ObjectAdapterDeactivatedException*>(&ex) || + dynamic_cast<const ConnectionManuallyClosedException*>(&ex)) { ex.ice_throw(); } diff --git a/cpp/src/IceBridge/IceBridge.cpp b/cpp/src/IceBridge/IceBridge.cpp new file mode 100644 index 00000000000..31ec58aae6c --- /dev/null +++ b/cpp/src/IceBridge/IceBridge.cpp @@ -0,0 +1,837 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +#include <Ice/Connection.h> +#include <Ice/ObjectAdapter.h> +#include <Ice/Service.h> +#include <Ice/UUID.h> +#include <IceUtil/Options.h> + +using namespace std; +using namespace Ice; + +namespace +{ + +// +// Represents a pending twoway invocation. +// +class Invocation : public IceUtil::Shared +{ +public: + + Invocation(const AMD_Object_ice_invokePtr& cb) : + _cb(cb) + { + } + + void success(bool ok, const pair<const Byte*, const Byte*>& results) + { + _cb->ice_response(ok, results); + } + + void exception(const Exception& ex) + { + _cb->ice_exception(ex); + } + +private: + + AMD_Object_ice_invokePtr _cb; +}; +typedef IceUtil::Handle<Invocation> InvocationPtr; + +// +// Represents a pending oneway invocation. +// +class OnewayInvocation : public IceUtil::Shared +{ +public: + + OnewayInvocation(const AMD_Object_ice_invokePtr& cb) : + _cb(cb) + { + } + + void success(bool, const pair<const Byte*, const Byte*>&) + { + assert(false); + } + + void exception(const Exception& ex) + { + _cb->ice_exception(ex); + } + + void sent(bool sentSynchronously) + { + _cb->ice_response(true, vector<Byte>()); + } + +private: + + AMD_Object_ice_invokePtr _cb; +}; +typedef IceUtil::Handle<OnewayInvocation> OnewayInvocationPtr; + +// +// Holds information about an incoming invocation that's been queued until an outgoing connection has +// been established. +// +struct QueuedInvocation : public IceUtil::Shared +{ + QueuedInvocation(const AMD_Object_ice_invokePtr& c, const pair<const Byte*, const Byte*>& p, const Current& curr) : + cb(c), current(curr) + { + if(p.first) + { + // + // The pointers in paramData refer to the Ice marshaling buffer and won't remain valid after + // ice_invoke_async completes, so we have to make a copy of the parameter data. + // + vector<Byte> tmp(p.first, p.second); + paramData.swap(tmp); + } + } + + const AMD_Object_ice_invokePtr cb; + vector<Byte> paramData; + const Current current; +}; +typedef IceUtil::Handle<QueuedInvocation> QueuedInvocationPtr; +typedef vector<QueuedInvocationPtr> InvocationList; + +// +// Relays heartbeat messages. +// +class HeartbeatCallbackI : public HeartbeatCallback +{ +public: + + HeartbeatCallbackI(const ConnectionPtr& con) : + _connection(con) + { + } + + virtual void heartbeat(const ConnectionPtr&) + { + // + // When a connection receives a heartbeat message, we send one over its corresponding connection. + // + try + { + _connection->begin_heartbeat(); + } + catch(...) + { + } + } + +private: + + const ConnectionPtr _connection; +}; + +// +// Represents a pair of bridged connections. +// +class BridgeConnection : public IceUtil::Shared +{ +public: + + BridgeConnection(const ObjectAdapterPtr& adapter, const ObjectPrx& target, const ConnectionPtr& incoming) : + _adapter(adapter), _target(target), _incoming(incoming), _closed(false) + { + } + + void setOutgoing(const ConnectionPtr& outgoing) + { + if(outgoing) + { + InvocationList queuedInvocations; + + { + IceUtil::Mutex::Lock lock(_lock); + + assert(!_outgoing); + + if(_closed) + { + // + // The incoming connection is already closed. There's no point in leaving the outgoing + // connection open. + // + outgoing->close(CloseGracefully); + } + else + { + _outgoing = outgoing; + + // + // Save the queued invocations. + // + queuedInvocations.swap(_queue); + + // + // Register hearbeat callbacks on both connections. + // + _incoming->setHeartbeatCallback(new HeartbeatCallbackI(_outgoing)); + _outgoing->setHeartbeatCallback(new HeartbeatCallbackI(_incoming)); + + // + // Configure the outgoing connection for bidirectional requests. + // + _outgoing->setAdapter(_adapter); + } + } + + // + // Flush any queued invocations. + // + flush(outgoing, queuedInvocations); + } + else + { + IceUtil::Mutex::Lock lock(_lock); + + // + // The outgoing connection failed so we close the incoming connection. closed() will eventually + // be called for it. + // + if(_incoming) + { + _incoming->close(CloseGracefully); + } + } + } + + void closed(const ConnectionPtr& con) + { + InvocationList queuedInvocations; + ConnectionPtr toBeClosed; + + { + IceUtil::Mutex::Lock lock(_lock); + + if(con == _incoming) + { + // + // The incoming connection was closed. + // + toBeClosed = _outgoing; + queuedInvocations.swap(_queue); + _closed = true; + _incoming = 0; + } + else + { + assert(con == _outgoing); + + // + // An outgoing connection was closed. + // + toBeClosed = _incoming; + _outgoing = 0; + } + } + + // + // Close the corresponding connection. + // + if(toBeClosed) + { + // + // Examine the exception that caused the connection to be closed. A CloseConnectionException + // indicates the connection was closed gracefully and we do the same. We also look for + // ConnectionManuallyClosedException, which indicates the connection was closed locally. + // We close forcefully for any other exception. + // + try + { + con->throwException(); + } + catch(const Ice::CloseConnectionException&) + { + toBeClosed->close(CloseGracefully); + } + catch(const Ice::ConnectionManuallyClosedException& ex) + { + // + // Connection was manually closed by the bridge. + // + toBeClosed->close(ex.graceful ? CloseGracefully : CloseForcefully); + } + catch(...) + { + toBeClosed->close(CloseForcefully); + } + } + + // + // Even though the connection is already closed, we still need to "complete" the pending invocations so + // that the connection's dispatch count is updated correctly. + // + for(InvocationList::iterator p = queuedInvocations.begin(); p != queuedInvocations.end(); ++p) + { + (*p)->cb->ice_exception(ConnectionLostException(__FILE__, __LINE__)); + } + } + + void dispatch(const AMD_Object_ice_invokePtr& cb, const pair<const Byte*, const Byte*>& paramData, + const Current& current) + { + // + // We've received an invocations, either from the client via the incoming connection, or from + // the server via the outgoing (bidirectional) connection. The current.con member tells us + // the connection over which the request arrived. + // + + ConnectionPtr dest; + + { + IceUtil::Mutex::Lock lock(_lock); + + if(_closed) + { + // + // If the incoming connection has already closed, we're done. + // + cb->ice_exception(ConnectionLostException(__FILE__, __LINE__)); + } + else if(!_outgoing) + { + // + // Queue the invocation until the outgoing connection is established. + // + _queue.push_back(new QueuedInvocation(cb, paramData, current)); + } + else if(current.con == _incoming) + { + // + // Forward to the outgoing connection. + // + dest = _outgoing; + } + else + { + // + // Forward to the incoming connection. + // + assert(current.con == _outgoing); + dest = _incoming; + } + } + + if(dest) + { + send(dest, cb, paramData, current); + } + } + +private: + + void flush(const ConnectionPtr& outgoing, const InvocationList& invocations) + { + for(InvocationList::const_iterator p = invocations.begin(); p != invocations.end(); ++p) + { + QueuedInvocationPtr i = *p; + + pair<const Byte*, const Byte*> paramData; + if(i->paramData.empty()) + { + paramData.first = paramData.second = 0; + } + else + { + paramData.first = &i->paramData[0]; + paramData.second = paramData.first + i->paramData.size(); + } + + send(outgoing, i->cb, paramData, i->current); + } + } + + void send(const ConnectionPtr& dest, const AMD_Object_ice_invokePtr& cb, + const pair<const Byte*, const Byte*>& paramData, const Current& current) + { + try + { + // + // Create a proxy having the same identity as the request. + // + ObjectPrx prx = dest->createProxy(current.id); + + // + // Examine the proxy and the request to determine whether it should be forwarded as a oneway or a twoway. + // + if(!prx->ice_isTwoway() || !current.requestId) + { + if(prx->ice_isTwoway()) + { + prx = prx->ice_oneway(); + } + OnewayInvocationPtr i = new OnewayInvocation(cb); + Callback_Object_ice_invokePtr d = + newCallback_Object_ice_invoke(i, &OnewayInvocation::success, &OnewayInvocation::exception, + &OnewayInvocation::sent); + prx->begin_ice_invoke(current.operation, current.mode, paramData, current.ctx, d); + } + else + { + InvocationPtr i = new Invocation(cb); + Callback_Object_ice_invokePtr d = + newCallback_Object_ice_invoke(i, &Invocation::success, &Invocation::exception); + prx->begin_ice_invoke(current.operation, current.mode, paramData, current.ctx, d); + } + } + catch(const std::exception& ex) + { + cb->ice_exception(ex); + } + } + + const ObjectAdapterPtr _adapter; + const ObjectPrx _target; + ConnectionPtr _incoming; + + IceUtil::Mutex _lock; + + bool _closed; + ConnectionPtr _outgoing; + + // + // We maintain our own queue for invocations that arrive on the incoming connection before the outgoing + // connection has been established. We don't want to forward these to proxies and let the proxies handle + // the queuing because then the invocations could be sent out of order (e.g., when invocations are split + // among twoway/oneway/datagram proxies). + // + InvocationList _queue; +}; +typedef IceUtil::Handle<BridgeConnection> BridgeConnectionPtr; + +// +// Allows the bridge to be used as an Ice router. +// +class RouterI : public Router +{ +public: + + virtual ObjectPrx getClientProxy(const Current&) const + { + return 0; + } + + virtual ObjectPrx getServerProxy(const Current&) const + { + return 0; + } + + virtual ObjectProxySeq addProxies(const ObjectProxySeq&, const Current&) + { + return ObjectProxySeq(); + } +}; + +class FinderI : public RouterFinder +{ +public: + + FinderI(const RouterPrx& router) : + _router(router) + { + } + + virtual RouterPrx getRouter(const Current&) + { + return _router; + } + +private: + + const RouterPrx _router; +}; + +class BridgeI; +typedef IceUtil::Handle<BridgeI> BridgeIPtr; + +class CloseCallbackI : public CloseCallback +{ +public: + + CloseCallbackI(const BridgeIPtr& bridge) : + _bridge(bridge) + { + } + + virtual void closed(const ConnectionPtr&); + +private: + + const BridgeIPtr _bridge; +}; + +class GetConnectionCallback : public IceUtil::Shared +{ +public: + + GetConnectionCallback(const BridgeIPtr& bridge, const BridgeConnectionPtr& bc) : + _bridge(bridge), _bc(bc) + { + } + + void success(const ConnectionPtr&); + void exception(const Exception&); + +private: + + const BridgeIPtr _bridge; + const BridgeConnectionPtr _bc; +}; +typedef IceUtil::Handle<GetConnectionCallback> GetConnectionCallbackPtr; + +// +// The main bridge servant. +// +class BridgeI : public Ice::BlobjectArrayAsync +{ +public: + + BridgeI(const ObjectAdapterPtr& adapter, const ObjectPrx& target) : + _adapter(adapter), _target(target) + { + } + + virtual void ice_invoke_async(const AMD_Object_ice_invokePtr& cb, + const std::pair<const Byte*, const Byte*>& paramData, + const Current& current) + { + BridgeConnectionPtr bc; + + { + IceUtil::Mutex::Lock lock(_lock); + + ConnectionMap::iterator p = _connections.find(current.con); + if(p == _connections.end()) + { + // + // The connection is unknown to us, it must be a new incoming connection. + // + + EndpointInfoPtr info = current.con->getEndpoint()->getInfo(); + + // + // Create a target proxy that matches the configuration of the incoming connection. + // + ObjectPrx target; + if(info->datagram()) + { + target = _target->ice_datagram(); + } + else if(info->secure()) + { + target = _target->ice_secure(true); + } + else + { + target = _target; + } + + // + // Force the proxy to establish a new connection by using a unique connection ID. + // + target = target->ice_connectionId(Ice::generateUUID()); + + bc = new BridgeConnection(_adapter, target, current.con); + _connections.insert(make_pair(current.con, bc)); + current.con->setCloseCallback(new CloseCallbackI(this)); + + // + // Try to establish the outgoing connection. + // + try + { + // + // Begin the connection establishment process asynchronously. This can take a while to complete, + // especially when using Bluetooth. + // + GetConnectionCallbackPtr gc = new GetConnectionCallback(this, bc); + Callback_Object_ice_getConnectionPtr d = + newCallback_Object_ice_getConnection(gc, &GetConnectionCallback::success, + &GetConnectionCallback::exception); + target->begin_ice_getConnection(d); + } + catch(const Exception& ex) + { + cb->ice_exception(ex); + return; + } + } + else + { + bc = p->second; + } + } + + // + // Delegate the invocation to the BridgeConnection object. + // + bc->dispatch(cb, paramData, current); + } + + void closed(const ConnectionPtr& con) + { + // + // Notify the BridgeConnection that a connection has closed. We also need to remove it from our map. + // + + BridgeConnectionPtr bc; + + { + IceUtil::Mutex::Lock lock(_lock); + + ConnectionMap::iterator p = _connections.find(con); + assert(p != _connections.end()); + bc = p->second; + _connections.erase(p); + } + + bc->closed(con); + } + + void outgoingSuccess(const BridgeConnectionPtr& bc, const ConnectionPtr& outgoing) + { + // + // An outgoing connection was established. Notify the BridgeConnection object. + // + IceUtil::Mutex::Lock lock(_lock); + _connections.insert(make_pair(outgoing, bc)); + outgoing->setCloseCallback(new CloseCallbackI(this)); + bc->setOutgoing(outgoing); + } + + void outgoingException(const BridgeConnectionPtr& bc, const Exception& ex) + { + // + // An outgoing connection attempt failed. Notify the BridgeConnection object. + // + bc->setOutgoing(0); + } + +private: + + const ObjectAdapterPtr _adapter; + const ObjectPrx _target; + + IceUtil::Mutex _lock; + + typedef map<ConnectionPtr, BridgeConnectionPtr> ConnectionMap; + ConnectionMap _connections; +}; + +void +CloseCallbackI::closed(const ConnectionPtr& con) +{ + _bridge->closed(con); +} + +void +GetConnectionCallback::success(const ConnectionPtr& outgoing) +{ + _bridge->outgoingSuccess(_bc, outgoing); +} + +void +GetConnectionCallback::exception(const Exception& ex) +{ + _bridge->outgoingException(_bc, ex); +} + +class BridgeService : public Service +{ +public: + + BridgeService(); + +protected: + + virtual bool start(int, char*[], int&); + virtual bool stop(); + virtual CommunicatorPtr initializeCommunicator(int&, char*[], const InitializationData&); + +private: + + void usage(const std::string&); +}; + +} + +BridgeService::BridgeService() +{ +} + +bool +BridgeService::start(int argc, char* argv[], int& status) +{ + IceUtilInternal::Options opts; + opts.addOpt("h", "help"); + opts.addOpt("v", "version"); + + vector<string> args; + try + { + args = opts.parse(argc, const_cast<const char**>(argv)); + } + catch(const IceUtilInternal::BadOptException& e) + { + error(e.reason); + usage(argv[0]); + return false; + } + + if(opts.isSet("help")) + { + usage(argv[0]); + status = EXIT_SUCCESS; + return false; + } + if(opts.isSet("version")) + { + print(ICE_STRING_VERSION); + status = EXIT_SUCCESS; + return false; + } + + if(!args.empty()) + { + cerr << argv[0] << ": too many arguments" << endl; + usage(argv[0]); + return false; + } + + PropertiesPtr properties = communicator()->getProperties(); + + const string targetProperty = "IceBridge.Target.Endpoints"; + const string targetEndpoints = properties->getProperty(targetProperty); + if(targetEndpoints.empty()) + { + error("property '" + targetProperty + "' is not set"); + return false; + } + + Ice::ObjectPrx target; + + try + { + target = communicator()->stringToProxy("dummy:" + targetEndpoints); + } + catch(const std::exception& ex) + { + ServiceError err(this); + err << "setting for target endpoints '" << targetEndpoints << "' is invalid:\n" << ex; + return false; + } + + // + // Initialize the object adapter. + // + const string endpointsProperty = "IceBridge.Source.Endpoints"; + if(properties->getProperty(endpointsProperty).empty()) + { + error("property '" + endpointsProperty + "' is not set"); + return false; + } + + ObjectAdapterPtr adapter = communicator()->createObjectAdapter("IceBridge.Source"); + + adapter->addDefaultServant(new BridgeI(adapter, target), ""); + + if(properties->getPropertyAsIntWithDefault("IceBridge.Router", 0) > 0) + { + RouterPrx router = RouterPrx::uncheckedCast(adapter->add(new RouterI, stringToIdentity("IceBridge/router"))); + adapter->add(new FinderI(router), stringToIdentity("Ice/RouterFinder")); + } + + try + { + adapter->activate(); + } + catch(const std::exception& ex) + { + { + ServiceError err(this); + err << "caught exception activating object adapter\n" << ex; + } + + stop(); + return false; + } + + return true; +} + +bool +BridgeService::stop() +{ + return true; +} + +CommunicatorPtr +BridgeService::initializeCommunicator(int& argc, char* argv[], const InitializationData& initializationData) +{ + InitializationData initData = initializationData; + initData.properties = createProperties(argc, argv, initializationData.properties); + + StringSeq args = argsToStringSeq(argc, argv); + args = initData.properties->parseCommandLineOptions("IceBridge", args); + stringSeqToArgs(args, argc, argv); + + // + // Disable automatic retry by default. + // + if(initData.properties->getProperty("Ice.RetryIntervals").empty()) + { + initData.properties->setProperty("Ice.RetryIntervals", "-1"); + } + + return Service::initializeCommunicator(argc, argv, initData); +} + +void +BridgeService::usage(const string& appName) +{ + string options = + "Options:\n" + "-h, --help Show this message.\n" + "-v, --version Display the Ice version.\n"; +#ifndef _WIN32 + options.append( + "--daemon Run as a daemon.\n" + "--pidfile FILE Write process ID into FILE.\n" + "--noclose Do not close open file descriptors.\n" + "--nochdir Do not change the current working directory.\n" + ); +#endif + print("Usage: " + appName + " [options]\n" + options); +} + +#ifdef _WIN32 + +int +wmain(int argc, wchar_t* argv[]) + +#else + +int +main(int argc, char* argv[]) + +#endif +{ + BridgeService svc; + return svc.main(argc, argv); +} diff --git a/cpp/src/IceBridge/Makefile.mk b/cpp/src/IceBridge/Makefile.mk new file mode 100644 index 00000000000..b3c282b0c39 --- /dev/null +++ b/cpp/src/IceBridge/Makefile.mk @@ -0,0 +1,15 @@ +# ********************************************************************** +# +# Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +# +# This copy of Ice is licensed to you under the terms described in the +# ICE_LICENSE file included in this distribution. +# +# ********************************************************************** + +$(project)_programs := icebridge +$(project)_dependencies := Ice + +icebridge_targetdir := $(bindir) + +projects += $(project) diff --git a/cpp/src/IceGrid/SessionManager.cpp b/cpp/src/IceGrid/SessionManager.cpp index 34caf1f9bd2..57ded43015f 100644 --- a/cpp/src/IceGrid/SessionManager.cpp +++ b/cpp/src/IceGrid/SessionManager.cpp @@ -58,7 +58,7 @@ SessionManager::findAllQueryObjects(bool cached) { try { - connection->close(false); + connection->close(Ice::CloseGracefullyAndWait); } catch(const Ice::LocalException&) { diff --git a/cpp/test/Ice/acm/AllTests.cpp b/cpp/test/Ice/acm/AllTests.cpp index 52c4922d98b..8e6563ede85 100644 --- a/cpp/test/Ice/acm/AllTests.cpp +++ b/cpp/test/Ice/acm/AllTests.cpp @@ -535,6 +535,32 @@ public: } }; +class HeartbeatManualTest : public TestCase +{ +public: + + HeartbeatManualTest(const RemoteCommunicatorPrxPtr& com) : TestCase("manual heartbeats", com) + { + // + // Disable heartbeats. + // + setClientACM(10, -1, 0); + setServerACM(10, -1, 0); + } + + virtual void runTestCase(const RemoteObjectAdapterPrxPtr& adapter, const TestIntfPrxPtr& proxy) + { + proxy->startHeartbeatCount(); + Ice::ConnectionPtr con = proxy->ice_getConnection(); + con->heartbeat(); + con->heartbeat(); + con->heartbeat(); + con->heartbeat(); + con->heartbeat(); + proxy->waitForHeartbeatCount(5); + } +}; + class SetACMTest : public TestCase { public: @@ -564,8 +590,9 @@ public: test(acm.close == Ice::CloseOnInvocationAndIdle); test(acm.heartbeat == Ice::HeartbeatAlways); - // Make sure the client sends few heartbeats to the server - proxy->waitForHeartbeat(2); + // Make sure the client sends a few heartbeats to the server. + proxy->startHeartbeatCount(); + proxy->waitForHeartbeatCount(2); } }; @@ -591,6 +618,7 @@ allTests(const Ice::CommunicatorPtr& communicator) tests.push_back(ICE_MAKE_SHARED(HeartbeatOnIdleTest, com)); tests.push_back(ICE_MAKE_SHARED(HeartbeatAlwaysTest, com)); + tests.push_back(ICE_MAKE_SHARED(HeartbeatManualTest, com)); tests.push_back(ICE_MAKE_SHARED(SetACMTest, com)); for(vector<TestCasePtr>::const_iterator p = tests.begin(); p != tests.end(); ++p) diff --git a/cpp/test/Ice/acm/Test.ice b/cpp/test/Ice/acm/Test.ice index 5ab98180dd3..d78abd6eb0f 100644 --- a/cpp/test/Ice/acm/Test.ice +++ b/cpp/test/Ice/acm/Test.ice @@ -17,7 +17,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/cpp/test/Ice/acm/TestI.cpp b/cpp/test/Ice/acm/TestI.cpp index d7742680a70..f4987856421 100644 --- a/cpp/test/Ice/acm/TestI.cpp +++ b/cpp/test/Ice/acm/TestI.cpp @@ -26,41 +26,6 @@ toString(int value) return os.str(); } -class HeartbeatCallbackI : -#ifdef ICE_CPP11_MAPPING - public enable_shared_from_this<HeartbeatCallbackI>, -#else - public Ice::HeartbeatCallback, -#endif - private IceUtil::Monitor<IceUtil::Mutex> -{ -public: - - void - waitForCount(int count) - { - Lock sync(*this); - _count = count; - while(_count > 0) - { - wait(); - } - } - - virtual void - heartbeat(const Ice::ConnectionPtr&) - { - Lock sync(*this); - --_count; - notifyAll(); - } - -private: - - int _count; -}; -ICE_DEFINE_PTR(HeartbeatCallbackIPtr, HeartbeatCallbackI); - } RemoteObjectAdapterPrxPtr @@ -162,16 +127,23 @@ TestI::interruptSleep(const Ice::Current& current) } void -TestI::waitForHeartbeat(int count, const Ice::Current& current) +TestI::startHeartbeatCount(const Ice::Current& current) { - HeartbeatCallbackIPtr callback = ICE_MAKE_SHARED(HeartbeatCallbackI); + _callback = ICE_MAKE_SHARED(HeartbeatCallbackI); #ifdef ICE_CPP11_MAPPING + HeartbeatCallbackIPtr callback = _callback; current.con->setHeartbeatCallback([callback](Ice::ConnectionPtr connection) { callback->heartbeat(move(connection)); }); #else - current.con->setHeartbeatCallback(callback); + current.con->setHeartbeatCallback(_callback); #endif - callback->waitForCount(count); +} + +void +TestI::waitForHeartbeatCount(int count, const Ice::Current&) +{ + assert(_callback); + _callback->waitForCount(count); } diff --git a/cpp/test/Ice/acm/TestI.h b/cpp/test/Ice/acm/TestI.h index be031e3799d..c45415022a6 100644 --- a/cpp/test/Ice/acm/TestI.h +++ b/cpp/test/Ice/acm/TestI.h @@ -44,7 +44,51 @@ public: virtual void sleep(int, const Ice::Current&); virtual void sleepAndHold(int, const Ice::Current&); virtual void interruptSleep(const Ice::Current&); - virtual void waitForHeartbeat(int, const Ice::Current&); + virtual void startHeartbeatCount(const Ice::Current&); + virtual void waitForHeartbeatCount(int, const Ice::Current&); + +private: + + class HeartbeatCallbackI : +#ifdef ICE_CPP11_MAPPING + public std::enable_shared_from_this<HeartbeatCallbackI>, +#else + public Ice::HeartbeatCallback, +#endif + private IceUtil::Monitor<IceUtil::Mutex> + { + public: + + HeartbeatCallbackI() : + _count(0) + { + } + + void + waitForCount(int count) + { + Lock sync(*this); + while(_count < count) + { + wait(); + } + } + + virtual void + heartbeat(const Ice::ConnectionPtr&) + { + Lock sync(*this); + ++_count; + notifyAll(); + } + + private: + + int _count; + }; + ICE_DEFINE_PTR(HeartbeatCallbackIPtr, HeartbeatCallbackI); + + HeartbeatCallbackIPtr _callback; }; #endif diff --git a/cpp/test/Ice/ami/AllTests.cpp b/cpp/test/Ice/ami/AllTests.cpp index 439f8a7792f..3366c4fdc7e 100644 --- a/cpp/test/Ice/ami/AllTests.cpp +++ b/cpp/test/Ice/ami/AllTests.cpp @@ -773,6 +773,21 @@ private: }; typedef IceUtil::Handle<FlushExCallback> FlushExCallbackPtr; +class CloseCallback : virtual public CallbackBase, virtual public Ice::CloseCallback +{ +public: + + CloseCallback() + { + } + + virtual void closed(const Ice::ConnectionPtr& con) + { + called(); + } +}; +typedef IceUtil::Handle<CloseCallback> CloseCallbackPtr; + class Thrower : public CallbackBase { public: @@ -1850,7 +1865,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) test(p->opBatchCount() == 0); auto b1 = p->ice_batchOneway(); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); auto id = this_thread::get_id(); promise<void> promise; @@ -1924,7 +1939,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) auto b1 = Ice::uncheckedCast<Test::TestIntfPrx>( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); promise<void> promise; b1->ice_getConnection()->flushBatchRequestsAsync( @@ -2002,7 +2017,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) auto b1 = Ice::uncheckedCast<Test::TestIntfPrx>( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); promise<void> promise; auto id = this_thread::get_id(); @@ -2072,8 +2087,8 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) b2->ice_getConnection(); // Ensure connection is established. b1->opBatch(); b2->opBatch(); - b1->ice_getConnection()->close(false); - b2->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); + b2->ice_getConnection()->close(Ice::CloseGracefullyAndWait); promise<void> promise; auto id = this_thread::get_id(); @@ -2161,8 +2176,38 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) if(p->ice_getConnection() && protocol != "bt") { - cout << "testing close connection with sending queue... " << flush; + cout << "testing graceful close connection with wait... " << flush; + { + // + // Local case: begin several requests, close the connection gracefully, and make sure it waits + // for the requests to complete. + // + vector<future<void>> results; + for(int i = 0; i < 3; ++i) + { + auto s = make_shared<promise<void>>(); + p->sleepAsync(50, + [s]() { s->set_value(); }, + [s](exception_ptr ex) { s->set_exception(ex); }); + results.push_back(s->get_future()); + } + p->ice_getConnection()->close(Ice::CloseGracefullyAndWait); + for(vector<future<void>>::iterator p = results.begin(); p != results.end(); ++p) + { + try + { + p->get(); + } + catch(const Ice::LocalException&) + { + test(false); + } + } + } { + // + // Remote case. + // Ice::ByteSeq seq; seq.resize(1024 * 10); for(Ice::ByteSeq::iterator q = seq.begin(); q != seq.end(); ++q) @@ -2191,7 +2236,8 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) results.push_back(s->get_future()); } atomic_flag sent = ATOMIC_FLAG_INIT; - p->closeAsync(false, nullptr, nullptr, [&sent](bool) { sent.test_and_set(); }); + p->closeAsync(Test::CloseMode::CloseGracefullyAndWait, nullptr, nullptr, + [&sent](bool) { sent.test_and_set(); }); if(!sent.test_and_set()) { for(int i = 0; i < maxQueue; i++) @@ -2231,8 +2277,111 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) } } cout << "ok" << endl; - } + cout << "testing graceful close connection without wait... " << flush; + { + // + // Local case: start a lengthy operation and then close the connection gracefully on the client side + // without waiting for the pending invocation to complete. There will be no retry and we expect the + // invocation to fail with ConnectionManuallyClosedException. + // + // This test requires two threads in the server's thread pool: one will block in sleep() and the other + // will process the CloseConnection message. + // + p->ice_ping(); + auto con = p->ice_getConnection(); + auto s = make_shared<promise<void>>(); + p->sleepAsync(100, + [s]() { s->set_value(); }, + [s](exception_ptr ex) { s->set_exception(ex); }); + future<void> f = s->get_future(); + con->close(Ice::ConnectionClose::CloseGracefully); + try + { + f.get(); + test(false); + } + catch(const Ice::ConnectionManuallyClosedException& ex) + { + test(ex.graceful); + } + + // + // Remote case: the server closes the connection gracefully. Our call to TestIntf::close() + // completes successfully and then the connection should be closed immediately afterward, + // despite the fact that there's a pending call to sleep(). The call to sleep() should be + // automatically retried and complete successfully with a new connection. + // + p->ice_ping(); + con = p->ice_getConnection(); + auto sc = make_shared<promise<void>>(); + con->setCloseCallback( + [sc](Ice::ConnectionPtr connection) + { + sc->set_value(); + }); + future<void> fc = sc->get_future(); + s = make_shared<promise<void>>(); + p->sleepAsync(100, + [s]() { s->set_value(); }, + [s](exception_ptr ex) { s->set_exception(ex); }); + f = s->get_future(); + p->close(Test::CloseMode::CloseGracefully); + fc.get(); + try + { + f.get(); + } + catch(const Ice::LocalException&) + { + test(false); + } + p->ice_ping(); + test(p->ice_getConnection() != con); + } + cout << "ok" << endl; + + cout << "testing forceful close connection... " << flush; + { + // + // Local case: start a lengthy operation and then close the connection forcefully on the client side. + // There will be no retry and we expect the invocation to fail with ConnectionManuallyClosedException. + // + p->ice_ping(); + auto con = p->ice_getConnection(); + auto s = make_shared<promise<void>>(); + p->sleepAsync(100, + [s]() { s->set_value(); }, + [s](exception_ptr ex) { s->set_exception(ex); }); + future<void> f = s->get_future(); + con->close(Ice::ConnectionClose::CloseForcefully); + try + { + f.get(); + test(false); + } + catch(const Ice::ConnectionManuallyClosedException& ex) + { + test(!ex.graceful); + } + + // + // Remote case: the server closes the connection forcefully. This causes the request to fail + // with a ConnectionLostException. Since the close() operation is not idempotent, the client + // will not retry. + // + try + { + p->close(Test::CloseMode::CloseForcefully); + test(false); + } + catch(const Ice::ConnectionLostException&) + { + // Expected. + } + } + cout << "ok" << endl; + } } p->shutdown(); @@ -2944,7 +3093,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) test(p->opBatchCount() == 0); Test::TestIntfPrx b1 = p->ice_batchOneway(); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = b1->begin_ice_flushBatchRequests( Ice::newCallback(cb, &FlushCallback::completedAsync, &FlushCallback::sentAsync)); @@ -2962,7 +3111,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) test(p->opBatchCount() == 0); Test::TestIntfPrx b1 = p->ice_batchOneway(); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(cookie); b1->begin_ice_flushBatchRequests( Ice::newCallback(cb, &FlushCallback::completedAsync, &FlushCallback::sentAsync), cookie); @@ -3013,7 +3162,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) test(p->opBatchCount() == 0); Test::TestIntfPrx b1 = p->ice_batchOneway(); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = b1->begin_ice_flushBatchRequests( Ice::newCallback_Object_ice_flushBatchRequests(cb, &FlushCallback::exception, @@ -3031,7 +3180,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) test(p->opBatchCount() == 0); Test::TestIntfPrx b1 = p->ice_batchOneway(); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(cookie); b1->begin_ice_flushBatchRequests( Ice::newCallback_Object_ice_flushBatchRequests(cb, &FlushCallback::exceptionWC, @@ -3100,7 +3249,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushExCallbackPtr cb = new FlushExCallback(); Ice::AsyncResultPtr r = b1->ice_getConnection()->begin_flushBatchRequests( Ice::newCallback(cb, &FlushExCallback::completedAsync, &FlushExCallback::sentAsync)); @@ -3119,7 +3268,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushExCallbackPtr cb = new FlushExCallback(cookie); b1->ice_getConnection()->begin_flushBatchRequests( Ice::newCallback(cb, &FlushExCallback::completedAsync, &FlushExCallback::sentAsync), cookie); @@ -3171,7 +3320,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushExCallbackPtr cb = new FlushExCallback(); Ice::AsyncResultPtr r = b1->ice_getConnection()->begin_flushBatchRequests( Ice::newCallback_Connection_flushBatchRequests(cb, &FlushExCallback::exception, @@ -3191,7 +3340,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushExCallbackPtr cb = new FlushExCallback(cookie); b1->ice_getConnection()->begin_flushBatchRequests( Ice::newCallback_Connection_flushBatchRequests(cb, &FlushExCallback::exceptionWC, @@ -3249,7 +3398,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = communicator->begin_flushBatchRequests( Ice::newCallback(cb, &FlushCallback::completedAsync, &FlushCallback::sentAsync)); @@ -3268,7 +3417,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(cookie); communicator->begin_flushBatchRequests( Ice::newCallback(cb, &FlushCallback::completedAsync, &FlushCallback::sentAsync), cookie); @@ -3319,7 +3468,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) b2->ice_getConnection(); // Ensure connection is established. b1->opBatch(); b2->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = communicator->begin_flushBatchRequests( Ice::newCallback(cb, &FlushCallback::completedAsync, &FlushCallback::sentAsync)); @@ -3346,8 +3495,8 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) b2->ice_getConnection(); // Ensure connection is established. b1->opBatch(); b2->opBatch(); - b1->ice_getConnection()->close(false); - b2->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); + b2->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = communicator->begin_flushBatchRequests( Ice::newCallback(cb, &FlushCallback::completedAsync, &FlushCallback::sentAsync)); @@ -3402,7 +3551,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = communicator->begin_flushBatchRequests( Ice::newCallback_Communicator_flushBatchRequests(cb, &FlushCallback::exception, @@ -3422,7 +3571,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) Test::TestIntfPrx b1 = Test::TestIntfPrx::uncheckedCast( p->ice_getConnection()->createProxy(p->ice_getIdentity())->ice_batchOneway()); b1->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(cookie); communicator->begin_flushBatchRequests( Ice::newCallback_Communicator_flushBatchRequests(cb, &FlushCallback::exceptionWC, @@ -3476,7 +3625,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) b2->ice_getConnection(); // Ensure connection is established. b1->opBatch(); b2->opBatch(); - b1->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = communicator->begin_flushBatchRequests( Ice::newCallback_Communicator_flushBatchRequests(cb, &FlushCallback::exception, @@ -3504,8 +3653,8 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) b2->ice_getConnection(); // Ensure connection is established. b1->opBatch(); b2->opBatch(); - b1->ice_getConnection()->close(false); - b2->ice_getConnection()->close(false); + b1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); + b2->ice_getConnection()->close(Ice::CloseGracefullyAndWait); FlushCallbackPtr cb = new FlushCallback(); Ice::AsyncResultPtr r = communicator->begin_flushBatchRequests( Ice::newCallback_Communicator_flushBatchRequests(cb, &FlushCallback::exception, @@ -3715,8 +3864,35 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) if(p->ice_getConnection() && protocol != "bt") { - cout << "testing close connection with sending queue... " << flush; + cout << "testing graceful close connection with wait... " << flush; + { + // + // Local case: begin several requests, close the connection gracefully, and make sure it waits + // for the requests to complete. + // + vector<Ice::AsyncResultPtr> results; + for(int i = 0; i < 3; ++i) + { + results.push_back(p->begin_sleep(50)); + } + p->ice_getConnection()->close(Ice::CloseGracefullyAndWait); + for(vector<Ice::AsyncResultPtr>::const_iterator q = results.begin(); q != results.end(); ++q) + { + (*q)->waitForCompleted(); + try + { + (*q)->throwLocalException(); + } + catch(const Ice::LocalException&) + { + test(false); + } + } + } { + // + // Remote case. + // Ice::ByteSeq seq; seq.resize(1024 * 10); for(Ice::ByteSeq::iterator q = seq.begin(); q != seq.end(); ++q) @@ -3740,7 +3916,7 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) { results.push_back(p->begin_opWithPayload(seq)); } - if(!p->begin_close(false)->isSent()) + if(!p->begin_close(Test::CloseGracefullyAndWait)->isSent()) { for(int i = 0; i < maxQueue; i++) { @@ -3775,6 +3951,96 @@ allTests(const Ice::CommunicatorPtr& communicator, bool collocated) } } cout << "ok" << endl; + + cout << "testing graceful close connection without wait... " << flush; + { + // + // Local case: start a lengthy operation and then close the connection gracefully on the client side + // without waiting for the pending invocation to complete. There will be no retry and we expect the + // invocation to fail with ConnectionManuallyClosedException. + // + // This test requires two threads in the server's thread pool: one will block in sleep() and the other + // will process the CloseConnection message. + // + p->ice_ping(); + Ice::ConnectionPtr con = p->ice_getConnection(); + Ice::AsyncResultPtr r = p->begin_sleep(100); + con->close(Ice::CloseGracefully); + r->waitForCompleted(); + try + { + r->throwLocalException(); + test(false); + } + catch(const Ice::ConnectionManuallyClosedException& ex) + { + test(ex.graceful); + } + + // + // Remote case: the server closes the connection gracefully. Our call to TestIntf::close() + // completes successfully and then the connection should be closed immediately afterward, + // despite the fact that there's a pending call to sleep(). The call to sleep() should be + // automatically retried and complete successfully. + // + p->ice_ping(); + con = p->ice_getConnection(); + CloseCallbackPtr cb = new CloseCallback; + con->setCloseCallback(cb); + r = p->begin_sleep(100); + p->close(Test::CloseGracefully); + cb->check(); + r->waitForCompleted(); + try + { + r->throwLocalException(); + } + catch(const Ice::LocalException&) + { + test(false); + } + p->ice_ping(); + test(p->ice_getConnection() != con); + } + cout << "ok" << endl; + + cout << "testing forceful close connection... " << flush; + { + // + // Local case: start a lengthy operation and then close the connection forcefully on the client side. + // There will be no retry and we expect the invocation to fail with ConnectionManuallyClosedException. + // + p->ice_ping(); + Ice::ConnectionPtr con = p->ice_getConnection(); + Ice::AsyncResultPtr r = p->begin_sleep(100); + con->close(Ice::CloseForcefully); + r->waitForCompleted(); + try + { + r->throwLocalException(); + test(false); + } + catch(const Ice::ConnectionManuallyClosedException& ex) + { + test(!ex.graceful); + } + + // + // Remote case: the server closes the connection forcefully. This causes the request to fail + // with a ConnectionLostException. Since the close() operation is not idempotent, the client + // will not retry. + // + try + { + p->close(Test::CloseForcefully); + test(false); + } + catch(const Ice::ConnectionLostException&) + { + // Expected. + } + } + cout << "ok" << endl; } p->shutdown(); diff --git a/cpp/test/Ice/ami/Client.cpp b/cpp/test/Ice/ami/Client.cpp index 18fb26c44bb..c6915bc2557 100644 --- a/cpp/test/Ice/ami/Client.cpp +++ b/cpp/test/Ice/ami/Client.cpp @@ -37,6 +37,7 @@ main(int argc, char* argv[]) { Ice::InitializationData initData = getTestInitData(argc, argv); initData.properties->setProperty("Ice.Warn.AMICallback", "0"); + initData.properties->setProperty("Ice.Warn.Connections", "0"); // // Limit the send buffer size, this test relies on the socket diff --git a/cpp/test/Ice/ami/Test.ice b/cpp/test/Ice/ami/Test.ice index 289750a3eb2..c82910e4620 100644 --- a/cpp/test/Ice/ami/Test.ice +++ b/cpp/test/Ice/ami/Test.ice @@ -19,6 +19,13 @@ exception TestIntfException { }; +enum CloseMode +{ + CloseForcefully, + CloseGracefully, + CloseGracefullyAndWait +}; + interface TestIntf { void op(); @@ -34,7 +41,8 @@ interface TestIntf out int eight, out int nine, out int ten, out int eleven); int opBatchCount(); bool waitForBatch(int count); - void close(bool force); + void close(CloseMode mode); + void sleep(int ms); void shutdown(); bool supportsFunctionalTests(); diff --git a/cpp/test/Ice/ami/TestI.cpp b/cpp/test/Ice/ami/TestI.cpp index f2dab5afd70..d7aa1d44762 100644 --- a/cpp/test/Ice/ami/TestI.cpp +++ b/cpp/test/Ice/ami/TestI.cpp @@ -93,9 +93,16 @@ TestIntfI::waitForBatch(Ice::Int count, const Ice::Current&) } void -TestIntfI::close(bool force, const Ice::Current& current) +TestIntfI::close(Test::CloseMode mode, const Ice::Current& current) { - current.con->close(force); + current.con->close(static_cast<ConnectionClose>(mode)); +} + +void +TestIntfI::sleep(Ice::Int ms, const Ice::Current& current) +{ + IceUtil::Monitor<IceUtil::Mutex>::Lock sync(*this); + timedWait(IceUtil::Time::milliSeconds(ms)); } void diff --git a/cpp/test/Ice/ami/TestI.h b/cpp/test/Ice/ami/TestI.h index bee3571b74b..edde85f6008 100644 --- a/cpp/test/Ice/ami/TestI.h +++ b/cpp/test/Ice/ami/TestI.h @@ -32,7 +32,8 @@ public: virtual void opWithArgs(Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, Ice::Int&, const Ice::Current&); virtual bool waitForBatch(Ice::Int, const Ice::Current&); - virtual void close(bool, const Ice::Current&); + virtual void close(Test::CloseMode, const Ice::Current&); + virtual void sleep(Ice::Int, const Ice::Current&); virtual void shutdown(const Ice::Current&); virtual bool supportsFunctionalTests(const Ice::Current&); diff --git a/cpp/test/Ice/background/AllTests.cpp b/cpp/test/Ice/background/AllTests.cpp index f5fdc059870..4d6b82904d6 100644 --- a/cpp/test/Ice/background/AllTests.cpp +++ b/cpp/test/Ice/background/AllTests.cpp @@ -379,7 +379,7 @@ allTests(const Ice::CommunicatorPtr& communicator) #ifdef ICE_CPP11_MAPPING background->opAsync(); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); background->opAsync(); vector<future<void>> results; @@ -407,7 +407,7 @@ allTests(const Ice::CommunicatorPtr& communicator) } #else background->begin_op(); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); background->begin_op(); vector<Ice::AsyncResultPtr> results; @@ -452,7 +452,7 @@ connectTests(const ConfigurationPtr& configuration, const Test::BackgroundPrxPtr { test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); int i; for(i = 0; i < 4; ++i) @@ -560,7 +560,7 @@ connectTests(const ConfigurationPtr& configuration, const Test::BackgroundPrxPtr } configuration->connectException(new Ice::SocketException(__FILE__, __LINE__)); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(10)); configuration->connectException(0); try @@ -592,7 +592,7 @@ initializeTests(const ConfigurationPtr& configuration, { test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); int i; for(i = 0; i < 4; i++) @@ -682,7 +682,7 @@ initializeTests(const ConfigurationPtr& configuration, cerr << ex << endl; test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); try { @@ -695,7 +695,7 @@ initializeTests(const ConfigurationPtr& configuration, cerr << ex << endl; test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); #endif // @@ -728,7 +728,7 @@ initializeTests(const ConfigurationPtr& configuration, { test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); try { @@ -764,7 +764,7 @@ initializeTests(const ConfigurationPtr& configuration, } configuration->initializeException(new Ice::SocketException(__FILE__, __LINE__)); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(10)); configuration->initializeException(0); try @@ -784,12 +784,12 @@ initializeTests(const ConfigurationPtr& configuration, } configuration->initializeSocketOperation(IceInternal::SocketOperationWrite); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); background->ice_ping(); configuration->initializeSocketOperation(IceInternal::SocketOperationNone); ctl->initializeException(true); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(10)); ctl->initializeException(false); try @@ -812,11 +812,11 @@ initializeTests(const ConfigurationPtr& configuration, { #if !defined(ICE_USE_IOCP) && !defined(ICE_USE_CFSTREAM) ctl->initializeSocketOperation(IceInternal::SocketOperationWrite); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); background->op(); ctl->initializeSocketOperation(IceInternal::SocketOperationNone); #else - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); background->op(); #endif } @@ -847,7 +847,7 @@ validationTests(const ConfigurationPtr& configuration, { test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); try { @@ -921,7 +921,7 @@ validationTests(const ConfigurationPtr& configuration, cerr << ex << endl; test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); try { @@ -1081,7 +1081,7 @@ validationTests(const ConfigurationPtr& configuration, cerr << ex << endl; test(false); } - background->ice_getConnection()->close(false); + background->ice_getConnection()->close(Ice::CloseGracefullyAndWait); try { @@ -1163,7 +1163,7 @@ validationTests(const ConfigurationPtr& configuration, #else backgroundBatchOneway->begin_ice_flushBatchRequests(); #endif - backgroundBatchOneway->ice_getConnection()->close(false); + backgroundBatchOneway->ice_getConnection()->close(Ice::CloseGracefullyAndWait); ctl->holdAdapter(); backgroundBatchOneway->opWithPayload(seq); @@ -1183,10 +1183,10 @@ validationTests(const ConfigurationPtr& configuration, // in the flush to report a CloseConnectionException). Instead we // wait for the first flush to complete. // - //backgroundBatchOneway->ice_getConnection()->close(false); + //backgroundBatchOneway->ice_getConnection()->close(Ice::CloseGracefullyAndWait); backgroundBatchOneway->end_ice_flushBatchRequests(r); #endif - backgroundBatchOneway->ice_getConnection()->close(false); + backgroundBatchOneway->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } void @@ -1775,10 +1775,10 @@ readWriteTests(const ConfigurationPtr& configuration, IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(10)); background->ice_ping(); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); IceUtil::ThreadControl::sleep(IceUtil::Time::milliSeconds(10)); - background->ice_getCachedConnection()->close(true); + background->ice_getCachedConnection()->close(Ice::CloseForcefully); } thread1->destroy(); diff --git a/cpp/test/Ice/binding/AllTests.cpp b/cpp/test/Ice/binding/AllTests.cpp index a8d5f454a74..26e7fa48aa6 100644 --- a/cpp/test/Ice/binding/AllTests.cpp +++ b/cpp/test/Ice/binding/AllTests.cpp @@ -170,7 +170,7 @@ allTests(const Ice::CommunicatorPtr& communicator) test(test2->ice_getConnection() == test3->ice_getConnection()); names.erase(test1->getAdapterName()); - test1->ice_getConnection()->close(false); + test1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } // @@ -192,7 +192,7 @@ allTests(const Ice::CommunicatorPtr& communicator) for(vector<RemoteObjectAdapterPrxPtr>::const_iterator q = adapters.begin(); q != adapters.end(); ++q) { - (*q)->getTestIntf()->ice_getConnection()->close(false); + (*q)->getTestIntf()->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } } @@ -217,7 +217,7 @@ allTests(const Ice::CommunicatorPtr& communicator) test(test2->ice_getConnection() == test3->ice_getConnection()); names.erase(test1->getAdapterName()); - test1->ice_getConnection()->close(false); + test1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } // @@ -314,7 +314,7 @@ allTests(const Ice::CommunicatorPtr& communicator) { try { - (*q)->getTestIntf()->ice_getConnection()->close(false); + (*q)->getTestIntf()->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } catch(const Ice::LocalException&) { @@ -354,7 +354,7 @@ allTests(const Ice::CommunicatorPtr& communicator) test(test2->ice_getConnection() == test3->ice_getConnection()); names.erase(getAdapterNameWithAMI(test1)); - test1->ice_getConnection()->close(false); + test1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } // @@ -376,7 +376,7 @@ allTests(const Ice::CommunicatorPtr& communicator) for(vector<RemoteObjectAdapterPrxPtr>::const_iterator q = adapters.begin(); q != adapters.end(); ++q) { - (*q)->getTestIntf()->ice_getConnection()->close(false); + (*q)->getTestIntf()->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } } @@ -401,7 +401,7 @@ allTests(const Ice::CommunicatorPtr& communicator) test(test2->ice_getConnection() == test3->ice_getConnection()); names.erase(test1->getAdapterName()); - test1->ice_getConnection()->close(false); + test1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } // @@ -433,7 +433,7 @@ allTests(const Ice::CommunicatorPtr& communicator) while(!names.empty()) { names.erase(test->getAdapterName()); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } test = ICE_UNCHECKED_CAST(TestIntfPrx, test->ice_endpointSelection(Ice::Random)); @@ -445,7 +445,7 @@ allTests(const Ice::CommunicatorPtr& communicator) while(!names.empty()) { names.erase(test->getAdapterName()); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } deactivate(com, adapters); @@ -473,7 +473,7 @@ allTests(const Ice::CommunicatorPtr& communicator) #if TARGET_OS_IPHONE > 0 if(i != nRetry) { - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter31"; i++); } #endif @@ -483,7 +483,7 @@ allTests(const Ice::CommunicatorPtr& communicator) #if TARGET_OS_IPHONE > 0 if(i != nRetry) { - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter32"; i++); } #endif @@ -493,7 +493,7 @@ allTests(const Ice::CommunicatorPtr& communicator) #if TARGET_OS_IPHONE > 0 if(i != nRetry) { - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter33"; i++); } #endif @@ -525,29 +525,29 @@ allTests(const Ice::CommunicatorPtr& communicator) #if TARGET_OS_IPHONE > 0 if(i != nRetry) { - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter36"; i++); } #endif test(i == nRetry); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); adapters.push_back(com->createObjectAdapter("Adapter35", endpoints[1]->toString())); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter35"; i++); #if TARGET_OS_IPHONE > 0 if(i != nRetry) { - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter35"; i++); } #endif test(i == nRetry); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); adapters.push_back(com->createObjectAdapter("Adapter34", endpoints[0]->toString())); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter34"; i++); #if TARGET_OS_IPHONE > 0 if(i != nRetry) { - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); for(i = 0; i < nRetry && test->getAdapterName() == "Adapter34"; i++); } #endif @@ -865,7 +865,7 @@ allTests(const Ice::CommunicatorPtr& communicator) for(i = 0; i < 5; i++) { test(test->getAdapterName() == "Adapter82"); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } TestIntfPrxPtr testSecure = ICE_UNCHECKED_CAST(TestIntfPrx, test->ice_secure(true)); @@ -881,7 +881,7 @@ allTests(const Ice::CommunicatorPtr& communicator) for(i = 0; i < 5; i++) { test(test->getAdapterName() == "Adapter81"); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } com->createObjectAdapter("Adapter83", (test->ice_getEndpoints()[1])->toString()); // Reactive tcp OA. @@ -889,7 +889,7 @@ allTests(const Ice::CommunicatorPtr& communicator) for(i = 0; i < 5; i++) { test(test->getAdapterName() == "Adapter83"); - test->ice_getConnection()->close(false); + test->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } com->deactivateObjectAdapter(adapters[0]); @@ -1098,7 +1098,7 @@ allTests(const Ice::CommunicatorPtr& communicator) // Close the connection now to free a FD (it could be done after the sleep but // there could be race condiutation since the connection might not be closed // immediately due to threading). - test->ice_connectionId("0")->ice_getConnection()->close(false); + test->ice_connectionId("0")->ice_getConnection()->close(Ice::CloseGracefullyAndWait); // // The server closed the acceptor, wait one second and retry after freeing a FD. diff --git a/cpp/test/Ice/hold/AllTests.cpp b/cpp/test/Ice/hold/AllTests.cpp index d08764d57d8..7cb0e59a53c 100644 --- a/cpp/test/Ice/hold/AllTests.cpp +++ b/cpp/test/Ice/hold/AllTests.cpp @@ -290,7 +290,7 @@ allTests(const Ice::CommunicatorPtr& communicator) { completed->get_future().get(); holdSerialized->ice_ping(); // Ensure everything's dispatched - holdSerialized->ice_getConnection()->close(false); + holdSerialized->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } } completed->get_future().get(); @@ -305,7 +305,7 @@ allTests(const Ice::CommunicatorPtr& communicator) { result->waitForSent(); holdSerialized->ice_ping(); // Ensure everything's dispatched - holdSerialized->ice_getConnection()->close(false); + holdSerialized->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } } result->waitForCompleted(); diff --git a/cpp/test/Ice/location/AllTests.cpp b/cpp/test/Ice/location/AllTests.cpp index 148230c9fbf..4e96a75a0d5 100644 --- a/cpp/test/Ice/location/AllTests.cpp +++ b/cpp/test/Ice/location/AllTests.cpp @@ -637,7 +637,7 @@ allTests(const Ice::CommunicatorPtr& communicator, const string& ref) cout << "testing object migration... " << flush; hello = ICE_CHECKED_CAST(HelloPrx, communicator->stringToProxy("hello")); obj->migrateHello(); - hello->ice_getConnection()->close(false); + hello->ice_getConnection()->close(Ice::CloseGracefullyAndWait); hello->sayHello(); obj->migrateHello(); hello->sayHello(); diff --git a/cpp/test/Ice/metrics/AllTests.cpp b/cpp/test/Ice/metrics/AllTests.cpp index c880af199ce..8d30231b520 100644 --- a/cpp/test/Ice/metrics/AllTests.cpp +++ b/cpp/test/Ice/metrics/AllTests.cpp @@ -287,7 +287,7 @@ struct Connect { if(proxy->ice_getCachedConnection()) { - proxy->ice_getCachedConnection()->close(false); + proxy->ice_getCachedConnection()->close(Ice::CloseGracefullyAndWait); } try { @@ -298,7 +298,7 @@ struct Connect } if(proxy->ice_getCachedConnection()) { - proxy->ice_getCachedConnection()->close(false); + proxy->ice_getCachedConnection()->close(Ice::CloseGracefullyAndWait); } } @@ -534,8 +534,8 @@ allTests(const Ice::CommunicatorPtr& communicator, const CommunicatorObserverIPt if(!collocated) { - metrics->ice_getConnection()->close(false); - metrics->ice_connectionId("Con1")->ice_getConnection()->close(false); + metrics->ice_getConnection()->close(Ice::CloseGracefullyAndWait); + metrics->ice_connectionId("Con1")->ice_getConnection()->close(Ice::CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -645,7 +645,7 @@ allTests(const Ice::CommunicatorPtr& communicator, const CommunicatorObserverIPt map = toMap(serverMetrics->getMetricsView("View", timestamp)["Connection"]); test(map["holding"]->current == 1); - metrics->ice_getConnection()->close(false); + metrics->ice_getConnection()->close(Ice::CloseGracefullyAndWait); map = toMap(clientMetrics->getMetricsView("View", timestamp)["Connection"]); test(map["closing"]->current == 1); @@ -660,7 +660,7 @@ allTests(const Ice::CommunicatorPtr& communicator, const CommunicatorObserverIPt props["IceMX.Metrics.View.Map.Connection.GroupBy"] = "none"; updateProps(clientProps, serverProps, update.get(), props, "Connection"); - metrics->ice_getConnection()->close(false); + metrics->ice_getConnection()->close(Ice::CloseGracefullyAndWait); metrics->ice_timeout(500)->ice_ping(); controller->hold(); @@ -717,7 +717,7 @@ allTests(const Ice::CommunicatorPtr& communicator, const CommunicatorObserverIPt testAttribute(clientMetrics, clientProps, update.get(), "Connection", "mcastHost", ""); testAttribute(clientMetrics, clientProps, update.get(), "Connection", "mcastPort", ""); - m->ice_getConnection()->close(false); + m->ice_getConnection()->close(Ice::CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -736,7 +736,7 @@ allTests(const Ice::CommunicatorPtr& communicator, const CommunicatorObserverIPt IceMX::MetricsPtr m1 = clientMetrics->getMetricsView("View", timestamp)["ConnectionEstablishment"][0]; test(m1->current == 0 && m1->total == 1 && m1->id == hostAndPort); - metrics->ice_getConnection()->close(false); + metrics->ice_getConnection()->close(Ice::CloseGracefullyAndWait); controller->hold(); try { @@ -788,7 +788,7 @@ allTests(const Ice::CommunicatorPtr& communicator, const CommunicatorObserverIPt try { prx->ice_ping(); - prx->ice_getConnection()->close(false); + prx->ice_getConnection()->close(Ice::CloseGracefullyAndWait); } catch(const Ice::LocalException&) { diff --git a/cpp/test/Ice/metrics/TestAMDI.cpp b/cpp/test/Ice/metrics/TestAMDI.cpp index 621bc976c1d..d5173e4380b 100644 --- a/cpp/test/Ice/metrics/TestAMDI.cpp +++ b/cpp/test/Ice/metrics/TestAMDI.cpp @@ -22,7 +22,7 @@ MetricsI::opAsync(function<void()> response, function<void(exception_ptr)>, cons void MetricsI::failAsync(function<void()> response, function<void(exception_ptr)>, const Ice::Current& current) { - current.con->close(true); + current.con->close(Ice::CloseForcefully); response(); } @@ -87,7 +87,7 @@ MetricsI::op_async(const Test::AMD_Metrics_opPtr& cb, const Ice::Current&) void MetricsI::fail_async(const Test::AMD_Metrics_failPtr& cb, const Ice::Current& current) { - current.con->close(true); + current.con->close(Ice::CloseForcefully); cb->ice_response(); } diff --git a/cpp/test/Ice/metrics/TestI.cpp b/cpp/test/Ice/metrics/TestI.cpp index 24e7b9b8ec3..30f7a98bf35 100644 --- a/cpp/test/Ice/metrics/TestI.cpp +++ b/cpp/test/Ice/metrics/TestI.cpp @@ -18,7 +18,7 @@ MetricsI::op(const Ice::Current&) void MetricsI::fail(const Ice::Current& current) { - current.con->close(true); + current.con->close(Ice::CloseForcefully); } void diff --git a/cpp/test/Ice/operations/BatchOneways.cpp b/cpp/test/Ice/operations/BatchOneways.cpp index cc86c14a951..e3d261cf7e3 100644 --- a/cpp/test/Ice/operations/BatchOneways.cpp +++ b/cpp/test/Ice/operations/BatchOneways.cpp @@ -121,7 +121,7 @@ batchOneways(const Test::MyClassPrxPtr& p) batch1->ice_ping(); batch2->ice_ping(); batch1->ice_flushBatchRequests(); - batch1->ice_getConnection()->close(false); + batch1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); batch1->ice_ping(); batch2->ice_ping(); @@ -129,7 +129,7 @@ batchOneways(const Test::MyClassPrxPtr& p) batch2->ice_getConnection(); batch1->ice_ping(); - batch1->ice_getConnection()->close(false); + batch1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); batch1->ice_ping(); batch2->ice_ping(); } diff --git a/cpp/test/Ice/operations/BatchOnewaysAMI.cpp b/cpp/test/Ice/operations/BatchOnewaysAMI.cpp index 9adb2be5c7b..4545d8581cf 100644 --- a/cpp/test/Ice/operations/BatchOnewaysAMI.cpp +++ b/cpp/test/Ice/operations/BatchOnewaysAMI.cpp @@ -127,7 +127,7 @@ batchOnewaysAMI(const Test::MyClassPrxPtr& p) batch1->ice_pingAsync().get(); batch2->ice_pingAsync().get(); batch1->ice_flushBatchRequestsAsync().get(); - batch1->ice_getConnection()->close(false); + batch1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); batch1->ice_pingAsync().get(); batch2->ice_pingAsync().get(); @@ -135,7 +135,7 @@ batchOnewaysAMI(const Test::MyClassPrxPtr& p) batch2->ice_getConnection(); batch1->ice_pingAsync().get(); - batch1->ice_getConnection()->close(false); + batch1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); batch1->ice_pingAsync().get(); batch2->ice_pingAsync().get(); @@ -182,7 +182,7 @@ batchOnewaysAMI(const Test::MyClassPrxPtr& p) batch1->end_ice_ping(batch1->begin_ice_ping()); batch2->end_ice_ping(batch2->begin_ice_ping()); batch1->end_ice_flushBatchRequests(batch1->begin_ice_flushBatchRequests()); - batch1->ice_getConnection()->close(false); + batch1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); batch1->end_ice_ping(batch1->begin_ice_ping()); batch2->end_ice_ping(batch2->begin_ice_ping()); @@ -190,7 +190,7 @@ batchOnewaysAMI(const Test::MyClassPrxPtr& p) batch2->ice_getConnection(); batch1->end_ice_ping(batch1->begin_ice_ping()); - batch1->ice_getConnection()->close(false); + batch1->ice_getConnection()->close(Ice::CloseGracefullyAndWait); batch1->end_ice_ping(batch1->begin_ice_ping()); batch2->end_ice_ping(batch2->begin_ice_ping()); diff --git a/cpp/test/Ice/retry/TestI.cpp b/cpp/test/Ice/retry/TestI.cpp index f721a3b6ab7..7fd3c0d4dd4 100644 --- a/cpp/test/Ice/retry/TestI.cpp +++ b/cpp/test/Ice/retry/TestI.cpp @@ -22,7 +22,7 @@ RetryI::op(bool kill, const Ice::Current& current) { if(current.con) { - current.con->close(true); + current.con->close(Ice::CloseForcefully); } else { diff --git a/cpp/test/Ice/timeout/AllTests.cpp b/cpp/test/Ice/timeout/AllTests.cpp index 6681a8f56df..1947f4e9aa1 100644 --- a/cpp/test/Ice/timeout/AllTests.cpp +++ b/cpp/test/Ice/timeout/AllTests.cpp @@ -294,7 +294,7 @@ allTests(const Ice::CommunicatorPtr& communicator) TimeoutPrxPtr to = ICE_CHECKED_CAST(TimeoutPrx, obj->ice_timeout(250)); Ice::ConnectionPtr connection = to->ice_getConnection(); timeout->holdAdapter(600); - connection->close(false); + connection->close(Ice::CloseGracefullyAndWait); try { connection->getInfo(); // getInfo() doesn't throw in the closing state. @@ -309,9 +309,10 @@ allTests(const Ice::CommunicatorPtr& communicator) connection->getInfo(); test(false); } - catch(const Ice::CloseConnectionException&) + catch(const Ice::ConnectionManuallyClosedException& ex) { // Expected. + test(ex.graceful); } timeout->op(); // Ensure adapter is active. } diff --git a/cpp/test/Ice/udp/AllTests.cpp b/cpp/test/Ice/udp/AllTests.cpp index e2e00f19da2..f16b10baa70 100644 --- a/cpp/test/Ice/udp/AllTests.cpp +++ b/cpp/test/Ice/udp/AllTests.cpp @@ -116,7 +116,7 @@ allTests(const CommunicatorPtr& communicator) { test(seq.size() > 16384); } - obj->ice_getConnection()->close(false); + obj->ice_getConnection()->close(CloseGracefullyAndWait); communicator->getProperties()->setProperty("Ice.UDP.SndSize", "64000"); seq.resize(50000); try diff --git a/cpp/test/IceSSL/configuration/AllTests.cpp b/cpp/test/IceSSL/configuration/AllTests.cpp index 96d14174b36..96c1e527cb7 100644 --- a/cpp/test/IceSSL/configuration/AllTests.cpp +++ b/cpp/test/IceSSL/configuration/AllTests.cpp @@ -1585,7 +1585,7 @@ allTests(const CommunicatorPtr& communicator, const string& testDir, bool p12) // verifier->reset(); verifier->returnValue(false); - server->ice_getConnection()->close(false); + server->ice_getConnection()->close(Ice::CloseGracefullyAndWait); try { server->ice_ping(); diff --git a/cpp/test/IceStorm/stress/Subscriber.cpp b/cpp/test/IceStorm/stress/Subscriber.cpp index d43c2469b28..4722fe93caf 100644 --- a/cpp/test/IceStorm/stress/Subscriber.cpp +++ b/cpp/test/IceStorm/stress/Subscriber.cpp @@ -163,7 +163,7 @@ public: if(!_done && (IceUtilInternal::random(10) == 1 || ++_count == _total)) { _done = true; - current.con->close(true); + current.con->close(CloseForcefully); // Deactivate the OA. This ensures that the subscribers // that have subscribed with oneway QoS will be booted. current.adapter->deactivate(); diff --git a/csharp/src/Ice/ConnectionI.cs b/csharp/src/Ice/ConnectionI.cs index 8943a4d48bc..c851279b0db 100644 --- a/csharp/src/Ice/ConnectionI.cs +++ b/csharp/src/Ice/ConnectionI.cs @@ -181,29 +181,31 @@ namespace Ice } } - public void close(bool force) + public void close(ConnectionClose mode) { lock(this) { - if(force) + if(mode == ConnectionClose.CloseForcefully) { - setState(StateClosed, new ForcedCloseConnectionException()); + setState(StateClosed, new ConnectionManuallyClosedException(false)); + } + else if(mode == ConnectionClose.CloseGracefully) + { + setState(StateClosing, new ConnectionManuallyClosedException(true)); } else { + Debug.Assert(mode == ConnectionClose.CloseGracefullyAndWait); + // - // If we do a graceful shutdown, then we wait until all - // outstanding requests have been completed. Otherwise, - // the CloseConnectionException will cause all outstanding - // requests to be retried, regardless of whether the - // server has processed them or not. + // Wait until all outstanding requests have been completed. // while(_asyncRequests.Count != 0) { Monitor.Wait(this); } - setState(StateClosing, new CloseConnectionException()); + setState(StateClosing, new ConnectionManuallyClosedException(true)); } } } @@ -330,13 +332,13 @@ namespace Ice // We send a heartbeat if there was no activity in the last // (timeout / 4) period. Sending a heartbeat sooner than // really needed is safer to ensure that the receiver will - // receive in time the heartbeat. Sending the heartbeat if + // receive the heartbeat in time. Sending the heartbeat if // there was no activity in the last (timeout / 2) period // isn't enough since monitor() is called only every (timeout // / 2) period. // // Note that this doesn't imply that we are sending 4 heartbeats - // per timeout period because the monitor() method is sill only + // per timeout period because the monitor() method is still only // called every (timeout / 2) period. // if(acm.heartbeat == ACMHeartbeat.HeartbeatAlways || @@ -345,7 +347,7 @@ namespace Ice { if(acm.heartbeat != ACMHeartbeat.HeartbeatOnInvocation || _dispatchCount > 0) { - heartbeat(); + sendHeartbeatNow(); } } @@ -583,6 +585,149 @@ namespace Ice } } + public void heartbeat() + { + heartbeatAsync().Wait(); + } + + private class HeartbeatCompletionCallback : AsyncResultCompletionCallback + { + public HeartbeatCompletionCallback(Ice.Connection connection, + Ice.Communicator communicator, + Instance instance, + object cookie, + Ice.AsyncCallback callback) + : base(communicator, instance, "heartbeat", cookie, callback) + { + _connection = connection; + } + + public override Ice.Connection getConnection() + { + return _connection; + } + + protected override Ice.AsyncCallback getCompletedCallback() + { + return (Ice.AsyncResult result) => + { + try + { + result.throwLocalException(); + } + catch(Ice.Exception ex) + { + if(exceptionCallback_ != null) + { + exceptionCallback_.Invoke(ex); + } + } + }; + } + + private Ice.Connection _connection; + } + + private class HeartbeatTaskCompletionCallback : TaskCompletionCallback<object> + { + public HeartbeatTaskCompletionCallback(System.IProgress<bool> progress, + CancellationToken cancellationToken) : + base(progress, cancellationToken) + { + } + + public override bool handleResponse(bool ok, OutgoingAsyncBase og) + { + SetResult(null); + return false; + } + } + + private class HeartbeatAsync : OutgoingAsyncBase + { + public HeartbeatAsync(Ice.ConnectionI connection, + Instance instance, + OutgoingAsyncCompletionCallback completionCallback) : + base(instance, completionCallback) + { + _connection = connection; + } + + public void invoke() + { + try + { + os_.writeBlob(IceInternal.Protocol.magic); + Ice.Util.currentProtocol.write__(os_); + Ice.Util.currentProtocolEncoding.write__(os_); + os_.writeByte(IceInternal.Protocol.validateConnectionMsg); + os_.writeByte((byte)0); + os_.writeInt(IceInternal.Protocol.headerSize); // Message size. + + int status = _connection.sendAsyncRequest(this, false, false, 0); + + if((status & AsyncStatusSent) != 0) + { + sentSynchronously_ = true; + if((status & AsyncStatusInvokeSentCallback) != 0) + { + invokeSent(); + } + } + } + catch(RetryException ex) + { + try + { + throw ex.get(); + } + catch(Ice.LocalException ee) + { + if(exception(ee)) + { + invokeExceptionAsync(); + } + } + } + catch(Ice.Exception ex) + { + if(exception(ex)) + { + invokeExceptionAsync(); + } + } + } + + private readonly Ice.ConnectionI _connection; + } + + public Task heartbeatAsync(IProgress<bool> progress = null, CancellationToken cancel = new CancellationToken()) + { + var completed = new HeartbeatTaskCompletionCallback(progress, cancel); + var outgoing = new HeartbeatAsync(this, _instance, completed); + outgoing.invoke(); + return completed.Task; + } + + public AsyncResult begin_heartbeat(AsyncCallback cb = null, object cookie = null) + { + var result = new HeartbeatCompletionCallback(this, _communicator, _instance, cookie, cb); + var outgoing = new HeartbeatAsync(this, _instance, result); + outgoing.invoke(); + return result; + } + + public void end_heartbeat(AsyncResult r) + { + if(r != null && r.getConnection() != this) + { + const string msg = "Connection for call to end_heartbeat does not match connection that was used " + + "to call corresponding begin_heartbeat method"; + throw new ArgumentException(msg); + } + AsyncResultI.check(r, "heartbeat").wait(); + } + public void setACM(Optional<int> timeout, Optional<ACMClose> close, Optional<ACMHeartbeat> heartbeat) { lock(this) @@ -1415,7 +1560,7 @@ namespace Ice // Trace the cause of unexpected connection closures // if(!(_exception is CloseConnectionException || - _exception is ForcedCloseConnectionException || + _exception is ConnectionManuallyClosedException || _exception is ConnectionTimeoutException || _exception is CommunicatorDestroyedException || _exception is ObjectAdapterDeactivatedException)) @@ -1723,7 +1868,7 @@ namespace Ice // Don't warn about certain expected exceptions. // if(!(_exception is CloseConnectionException || - _exception is ForcedCloseConnectionException || + _exception is ConnectionManuallyClosedException || _exception is ConnectionTimeoutException || _exception is CommunicatorDestroyedException || _exception is ObjectAdapterDeactivatedException || @@ -1902,7 +2047,7 @@ namespace Ice if(_observer != null && state == StateClosed && _exception != null) { if(!(_exception is CloseConnectionException || - _exception is ForcedCloseConnectionException || + _exception is ConnectionManuallyClosedException || _exception is ConnectionTimeoutException || _exception is CommunicatorDestroyedException || _exception is ObjectAdapterDeactivatedException || @@ -1931,8 +2076,7 @@ namespace Ice private void initiateShutdown() { - Debug.Assert(_state == StateClosing); - Debug.Assert(_dispatchCount == 0); + Debug.Assert(_state == StateClosing && _dispatchCount == 0); if(_shutdownInitiated) { @@ -1958,7 +2102,7 @@ namespace Ice setState(StateClosingPending); // - // Notify the the transceiver of the graceful connection closure. + // Notify the transceiver of the graceful connection closure. // int op = _transceiver.closing(true, _exception); if(op != 0) @@ -1970,7 +2114,7 @@ namespace Ice } } - private void heartbeat() + private void sendHeartbeatNow() { Debug.Assert(_state == StateActive); @@ -2456,7 +2600,7 @@ namespace Ice setState(StateClosingPending, new CloseConnectionException()); // - // Notify the the transceiver of the graceful connection closure. + // Notify the transceiver of the graceful connection closure. // int op = _transceiver.closing(false, _exception); if(op != 0) @@ -2540,7 +2684,7 @@ namespace Ice { info.outAsync = null; } - Monitor.PulseAll(this); // Notify threads blocked in close(false) + Monitor.PulseAll(this); // Notify threads blocked in close() } break; } diff --git a/csharp/test/Ice/acm/AllTests.cs b/csharp/test/Ice/acm/AllTests.cs index 4059809084d..4e553fe894f 100644 --- a/csharp/test/Ice/acm/AllTests.cs +++ b/csharp/test/Ice/acm/AllTests.cs @@ -93,7 +93,7 @@ class LoggerI : Ice.Logger private bool _started; private List<string> _messages = new List<string>(); -}; +} abstract class TestCase { @@ -251,7 +251,7 @@ abstract class TestCase protected int _heartbeat; protected bool _closed; -}; +} public class AllTests : TestCommon.AllTests { @@ -270,7 +270,7 @@ public class AllTests : TestCommon.AllTests test(_heartbeat >= 2); } } - }; + } class InvocationHeartbeatOnHoldTest : TestCase { @@ -298,7 +298,7 @@ public class AllTests : TestCommon.AllTests waitForClosed(); } } - }; + } class InvocationNoHeartbeatTest : TestCase { @@ -328,7 +328,7 @@ public class AllTests : TestCommon.AllTests } } } - }; + } class InvocationHeartbeatCloseOnIdleTest : TestCase { @@ -351,7 +351,7 @@ public class AllTests : TestCommon.AllTests test(!_closed); } } - }; + } class CloseOnIdleTest : TestCase { @@ -370,7 +370,7 @@ public class AllTests : TestCommon.AllTests test(_heartbeat == 0); } } - }; + } class CloseOnInvocationTest : TestCase { @@ -389,7 +389,7 @@ public class AllTests : TestCommon.AllTests test(!_closed); } } - }; + } class CloseOnIdleAndInvocationTest : TestCase { @@ -419,7 +419,7 @@ public class AllTests : TestCommon.AllTests waitForClosed(); } - }; + } class ForcefulCloseOnIdleAndInvocationTest : TestCase { @@ -440,7 +440,7 @@ public class AllTests : TestCommon.AllTests test(_heartbeat == 0); } } - }; + } class HeartbeatOnIdleTest : TestCase { @@ -458,7 +458,7 @@ public class AllTests : TestCommon.AllTests test(_heartbeat >= 3); } } - }; + } class HeartbeatAlwaysTest : TestCase { @@ -480,7 +480,31 @@ public class AllTests : TestCommon.AllTests test(_heartbeat >= 3); } } - }; + } + + class HeartbeatManualTest : TestCase + { + public HeartbeatManualTest(RemoteCommunicatorPrx com) : base("manual heartbeats", com) + { + // + // Disable heartbeats. + // + setClientACM(10, -1, 0); + setServerACM(10, -1, 0); + } + + public override void runTestCase(RemoteObjectAdapterPrx adapter, TestIntfPrx proxy) + { + proxy.startHeartbeatCount(); + Ice.Connection con = proxy.ice_getConnection(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + proxy.waitForHeartbeatCount(5); + } + } class SetACMTest : TestCase { @@ -511,9 +535,10 @@ public class AllTests : TestCommon.AllTests test(acm.close == Ice.ACMClose.CloseOnInvocationAndIdle); test(acm.heartbeat == Ice.ACMHeartbeat.HeartbeatAlways); - proxy.waitForHeartbeat(2); + proxy.startHeartbeatCount(); + proxy.waitForHeartbeatCount(2); } - }; + } public static void allTests(TestCommon.Application app) { @@ -535,6 +560,7 @@ public class AllTests : TestCommon.AllTests tests.Add(new HeartbeatOnIdleTest(com)); tests.Add(new HeartbeatAlwaysTest(com)); + tests.Add(new HeartbeatManualTest(com)); tests.Add(new SetACMTest(com)); foreach(TestCase test in tests) diff --git a/csharp/test/Ice/acm/Test.ice b/csharp/test/Ice/acm/Test.ice index 5ab98180dd3..d78abd6eb0f 100644 --- a/csharp/test/Ice/acm/Test.ice +++ b/csharp/test/Ice/acm/Test.ice @@ -17,7 +17,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/csharp/test/Ice/acm/TestI.cs b/csharp/test/Ice/acm/TestI.cs index 0dd3455daf7..970c1c22148 100644 --- a/csharp/test/Ice/acm/TestI.cs +++ b/csharp/test/Ice/acm/TestI.cs @@ -34,7 +34,8 @@ public class RemoteCommunicatorI : RemoteCommunicatorDisp_ } properties.setProperty(name + ".ThreadPool.Size", "2"); Ice.ObjectAdapter adapter = com.createObjectAdapterWithEndpoints(name, protocol + " -h \"" + host + "\""); - return RemoteObjectAdapterPrxHelper.uncheckedCast(current.adapter.addWithUUID(new RemoteObjectAdapterI(adapter))); + return RemoteObjectAdapterPrxHelper.uncheckedCast( + current.adapter.addWithUUID(new RemoteObjectAdapterI(adapter))); } public override void @@ -42,7 +43,7 @@ public class RemoteCommunicatorI : RemoteCommunicatorDisp_ { current.adapter.getCommunicator().shutdown(); } -}; +} public class RemoteObjectAdapterI : RemoteObjectAdapterDisp_ { @@ -82,7 +83,7 @@ public class RemoteObjectAdapterI : RemoteObjectAdapterDisp_ private Ice.ObjectAdapter _adapter; private TestIntfPrx _testIntf; -}; +} public class TestI : TestIntfDisp_ { @@ -117,7 +118,7 @@ public class TestI : TestIntfDisp_ { lock(this) { - --_count; + ++_count; System.Threading.Monitor.PulseAll(this); } } @@ -126,8 +127,7 @@ public class TestI : TestIntfDisp_ { lock(this) { - _count = count; - while(_count > 0) + while(_count < count) { System.Threading.Monitor.Wait(this); } @@ -135,14 +135,19 @@ public class TestI : TestIntfDisp_ } private int _count = 0; - }; + } - public override void waitForHeartbeat(int count, Ice.Current current) + public override void startHeartbeatCount(Ice.Current current) { + _callback = new HeartbeatCallbackI(); + current.con.setHeartbeatCallback(_callback.heartbeat); + } - - HeartbeatCallbackI callback = new HeartbeatCallbackI(); - current.con.setHeartbeatCallback(callback.heartbeat); - callback.waitForCount(count); + public override void waitForHeartbeatCount(int count, Ice.Current current) + { + System.Diagnostics.Debug.Assert(_callback != null); + _callback.waitForCount(count); } -}; + + private HeartbeatCallbackI _callback; +} diff --git a/csharp/test/Ice/ami/AllTests.cs b/csharp/test/Ice/ami/AllTests.cs index 20c936e1fbe..dd91682ff69 100644 --- a/csharp/test/Ice/ami/AllTests.cs +++ b/csharp/test/Ice/ami/AllTests.cs @@ -1904,7 +1904,7 @@ public class AllTests : TestCommon.AllTests test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests(cb.completedAsync, cookie); r.whenSent(cb.sentAsync); @@ -1940,7 +1940,7 @@ public class AllTests : TestCommon.AllTests test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests(); r.whenCompleted(cb.exception); @@ -1974,9 +1974,9 @@ public class AllTests : TestCommon.AllTests b1.opBatch(); FlushCallback cb = new FlushCallback(); System.Threading.Tasks.Task t = b1.ice_flushBatchRequestsAsync( - progress: new Progress(sentSynchronoully => + progress: new Progress(sentSynchronously => { - cb.sent(sentSynchronoully); + cb.sent(sentSynchronously); })); cb.check(); @@ -1989,12 +1989,12 @@ public class AllTests : TestCommon.AllTests test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); System.Threading.Tasks.Task t = b1.ice_flushBatchRequestsAsync( - progress:new Progress(sentSynchronoully => + progress:new Progress(sentSynchronously => { - cb.sent(sentSynchronoully); + cb.sent(sentSynchronously); })); cb.check(); t.Wait(); @@ -2058,7 +2058,7 @@ public class AllTests : TestCommon.AllTests test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests( (Ice.AsyncResult result) => @@ -2110,7 +2110,7 @@ public class AllTests : TestCommon.AllTests test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests(); r.whenCompleted( @@ -2166,7 +2166,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); Task t = b1.ice_getConnection().flushBatchRequestsAsync( progress: new Progress( sentSynchronously => @@ -2205,7 +2205,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushExCallback cb = new FlushExCallback(cookie); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests(cb.completedAsync, cookie); r.whenSent(cb.sentAsync); @@ -2242,7 +2242,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushExCallback cb = new FlushExCallback(); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests(); r.whenCompleted(cb.exception); @@ -2297,7 +2297,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushExCallback cb = new FlushExCallback(cookie); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests( (Ice.AsyncResult result) => @@ -2350,7 +2350,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushExCallback cb = new FlushExCallback(); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests(); r.whenCompleted( @@ -2404,7 +2404,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); SentCallback cb = new SentCallback(); Task t = communicator.flushBatchRequestsAsync( progress:new Progress( @@ -2459,7 +2459,7 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); SentCallback cb = new SentCallback(); Task t = communicator.flushBatchRequestsAsync( new Progress( @@ -2486,8 +2486,8 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); SentCallback cb = new SentCallback(); Task t = communicator.flushBatchRequestsAsync( new Progress( @@ -2528,7 +2528,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = communicator.begin_flushBatchRequests(cb.completedAsync, cookie); r.whenSent(cb.sentAsync); @@ -2577,7 +2577,7 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = communicator.begin_flushBatchRequests(cb.completedAsync, cookie); r.whenSent(cb.sentAsync); @@ -2601,8 +2601,8 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = communicator.begin_flushBatchRequests(cb.completedAsync, cookie); r.whenSent(cb.sentAsync); @@ -2639,7 +2639,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests(); r.whenCompleted(cb.exception); @@ -2689,7 +2689,7 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests(); r.whenCompleted(cb.exception); @@ -2714,8 +2714,8 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests(); r.whenCompleted(cb.exception); @@ -2767,7 +2767,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = communicator.begin_flushBatchRequests( (Ice.AsyncResult result) => @@ -2831,7 +2831,7 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = communicator.begin_flushBatchRequests( (Ice.AsyncResult result) => @@ -2863,8 +2863,8 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(cookie); Ice.AsyncResult r = communicator.begin_flushBatchRequests( (Ice.AsyncResult result) => @@ -2917,7 +2917,7 @@ public class AllTests : TestCommon.AllTests TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests(); r.whenCompleted( @@ -2983,7 +2983,7 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests(); r.whenCompleted( @@ -3016,8 +3016,8 @@ public class AllTests : TestCommon.AllTests b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests(); r.whenCompleted( @@ -3356,9 +3356,28 @@ public class AllTests : TestCommon.AllTests if(p.ice_getConnection() != null) { - Write("testing close connection with sending queue... "); + Write("testing graceful close connection with wait... "); Flush(); { + // + // Local case: begin several requests, close the connection gracefully, and make sure it waits + // for the requests to complete. + // + List<Task> results = new List<Task>(); + for(int i = 0; i < 3; ++i) + { + results.Add(p.sleepAsync(50)); + } + p.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + foreach(Task t in results) + { + t.Wait(); + } + } + { + // + // Remote case. + // byte[] seq = new byte[1024 * 10]; // @@ -3379,7 +3398,7 @@ public class AllTests : TestCommon.AllTests } ProgresCallback cb = new ProgresCallback(); - p.closeAsync(false, progress:cb); + p.closeAsync(CloseMode.CloseGracefullyAndWait, progress:cb); if(!cb.SentSynchronously) { @@ -3408,6 +3427,88 @@ public class AllTests : TestCommon.AllTests } } WriteLine("ok"); + + Write("testing graceful close connection without wait... "); + Flush(); + { + // + // Local case: start a lengthy operation and then close the connection gracefully on the client side + // without waiting for the pending invocation to complete. There will be no retry and we expect the + // invocation to fail with ConnectionManuallyClosedException. + // + // This test requires two threads in the server's thread pool: one will block in sleep() and the other + // will process the CloseConnection message. + // + Task t = p.sleepAsync(100); + p.ice_getConnection().close(Ice.ConnectionClose.CloseGracefully); + try + { + t.Wait(); + test(false); + } + catch(System.AggregateException ex) + { + test(ex.InnerException is Ice.ConnectionManuallyClosedException); + test((ex.InnerException as Ice.ConnectionManuallyClosedException).graceful); + } + + // + // Remote case: the server closes the connection gracefully. Our call to TestIntf::close() + // completes successfully and then the connection should be closed immediately afterward, + // despite the fact that there's a pending call to sleep(). The call to sleep() should be + // automatically retried and complete successfully. + // + Ice.Connection con = p.ice_getConnection(); + CallbackBase cb = new CallbackBase(); + con.setCloseCallback(_ => + { + cb.called(); + }); + t = p.sleepAsync(100); + p.close(CloseMode.CloseGracefully); + cb.check(); + t.Wait(); + p.ice_ping(); + test(p.ice_getConnection() != con); + } + WriteLine("ok"); + + Write("testing forceful close connection... "); + Flush(); + { + // + // Local case: start a lengthy operation and then close the connection forcefully on the client side. + // There will be no retry and we expect the invocation to fail with ConnectionManuallyClosedException. + // + Task t = p.sleepAsync(100); + p.ice_getConnection().close(Ice.ConnectionClose.CloseForcefully); + try + { + t.Wait(); + test(false); + } + catch(System.AggregateException ex) + { + test(ex.InnerException is Ice.ConnectionManuallyClosedException); + test(!(ex.InnerException as Ice.ConnectionManuallyClosedException).graceful); + } + + // + // Remote case: the server closes the connection forcefully. This causes the request to fail + // with a ConnectionLostException. Since the close() operation is not idempotent, the client + // will not retry. + // + try + { + p.close(CloseMode.CloseForcefully); + test(false); + } + catch(Ice.ConnectionLostException) + { + // Expected. + } + } + WriteLine("ok"); } p.shutdown(); diff --git a/csharp/test/Ice/ami/Client.cs b/csharp/test/Ice/ami/Client.cs index bc64d64276a..fe52f0320b0 100644 --- a/csharp/test/Ice/ami/Client.cs +++ b/csharp/test/Ice/ami/Client.cs @@ -28,6 +28,8 @@ public class Client : TestCommon.Application { Ice.InitializationData initData = base.getInitData(ref args); initData.properties.setProperty("Ice.Warn.AMICallback", "0"); + initData.properties.setProperty("Ice.Warn.Connections", "0"); + // // Limit the send buffer size, this test relies on the socket // send() blocking after sending a given amount of data. diff --git a/csharp/test/Ice/ami/Server.cs b/csharp/test/Ice/ami/Server.cs index d07fb4e30bf..0202e74127c 100644 --- a/csharp/test/Ice/ami/Server.cs +++ b/csharp/test/Ice/ami/Server.cs @@ -47,11 +47,16 @@ public class Server : TestCommon.Application initData.properties.setProperty("Ice.Default.CollocationOptimized", "0"); // + // This test kills connections, so we don't want warnings. + // + initData.properties.setProperty("Ice.Warn.Connections", "0"); + + // // Limit the recv buffer size, this test relies on the socket // send() blocking after sending a given amount of data. // initData.properties.setProperty("Ice.TCP.RcvSize", "50000"); - return initData; + return initData; } public static int Main(string[] args) diff --git a/csharp/test/Ice/ami/Test.ice b/csharp/test/Ice/ami/Test.ice index 1bd696fdcd1..b9f88916103 100644 --- a/csharp/test/Ice/ami/Test.ice +++ b/csharp/test/Ice/ami/Test.ice @@ -18,6 +18,13 @@ exception TestIntfException { }; +enum CloseMode +{ + CloseForcefully, + CloseGracefully, + CloseGracefullyAndWait +}; + interface TestIntf { void op(); @@ -28,7 +35,8 @@ interface TestIntf void opBatch(); int opBatchCount(); bool waitForBatch(int count); - void close(bool force); + void close(CloseMode mode); + void sleep(int ms); void shutdown(); bool supportsFunctionalTests(); diff --git a/csharp/test/Ice/ami/TestI.cs b/csharp/test/Ice/ami/TestI.cs index 0aabc62501a..a18fc738cbe 100644 --- a/csharp/test/Ice/ami/TestI.cs +++ b/csharp/test/Ice/ami/TestI.cs @@ -85,9 +85,15 @@ public class TestI : TestIntfDisp_ } override public void - close(bool force, Ice.Current current) + close(CloseMode mode, Ice.Current current) { - current.con.close(force); + current.con.close((Ice.ConnectionClose)((int)mode)); + } + + override public void + sleep(int ms, Ice.Current current) + { + Thread.Sleep(ms); } override public void diff --git a/csharp/test/Ice/background/AllTests.cs b/csharp/test/Ice/background/AllTests.cs index 0a81a8e6d7a..0594a541224 100644 --- a/csharp/test/Ice/background/AllTests.cs +++ b/csharp/test/Ice/background/AllTests.cs @@ -327,7 +327,7 @@ public class AllTests configuration.buffered(true); backgroundController.buffered(true); background.begin_op(); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); background.begin_op(); OpAMICallback cb = new OpAMICallback(); @@ -367,7 +367,7 @@ public class AllTests System.Console.Out.WriteLine(ex); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); for(int i = 0; i < 4; ++i) { @@ -433,7 +433,7 @@ public class AllTests } configuration.connectException(new Ice.SocketException()); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); Thread.Sleep(10); configuration.connectException(null); try @@ -463,7 +463,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); for(int i = 0; i < 4; ++i) { @@ -544,7 +544,7 @@ public class AllTests } configuration.initializeException(new Ice.SocketException()); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); Thread.Sleep(10); configuration.initializeException(null); try @@ -563,11 +563,11 @@ public class AllTests test(false); } - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); background.ice_ping(); ctl.initializeException(true); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); Thread.Sleep(10); ctl.initializeException(false); try @@ -588,7 +588,7 @@ public class AllTests try { - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); background.op(); } catch(Ice.LocalException) @@ -620,7 +620,7 @@ public class AllTests { CloseCallback cb = new CloseCallback(); prx.ice_getConnection().setCloseCallback(cb.closed); - prx.ice_getConnection().close(false); + prx.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); cb.check(); } @@ -823,7 +823,7 @@ public class AllTests // in the flush to report a CloseConnectionException). Instead we flush a second time // with the same callback to wait for the first flush to complete. // - //backgroundBatchOneway.ice_getConnection().close(false); + //backgroundBatchOneway.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); backgroundBatchOneway.end_ice_flushBatchRequests(r3); closeConnection(backgroundBatchOneway); } @@ -1159,10 +1159,10 @@ public class AllTests Thread.Sleep(10); background.ice_ping(); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); Thread.Sleep(10); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); } thread1.destroy(); diff --git a/csharp/test/Ice/binding/AllTests.cs b/csharp/test/Ice/binding/AllTests.cs index 90bde85dd9b..949d6c12b19 100644 --- a/csharp/test/Ice/binding/AllTests.cs +++ b/csharp/test/Ice/binding/AllTests.cs @@ -159,7 +159,7 @@ public class AllTests : TestCommon.AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.Remove(test1.getAdapterName()); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -181,7 +181,7 @@ public class AllTests : TestCommon.AllTests foreach(RemoteObjectAdapterPrx adpt in adapters) { - adpt.getTestIntf().ice_getConnection().close(false); + adpt.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -206,7 +206,7 @@ public class AllTests : TestCommon.AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.Remove(test1.getAdapterName()); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -290,7 +290,7 @@ public class AllTests : TestCommon.AllTests { try { - a.getTestIntf().ice_getConnection().close(false); + a.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } catch(Ice.LocalException) { @@ -331,7 +331,7 @@ public class AllTests : TestCommon.AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.Remove(getAdapterNameWithAMI(test1)); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -353,7 +353,7 @@ public class AllTests : TestCommon.AllTests foreach(RemoteObjectAdapterPrx adpt in adapters) { - adpt.getTestIntf().ice_getConnection().close(false); + adpt.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -378,7 +378,7 @@ public class AllTests : TestCommon.AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.Remove(getAdapterNameWithAMI(test1)); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -411,7 +411,7 @@ public class AllTests : TestCommon.AllTests while(names.Count > 0) { names.Remove(obj.getAdapterName()); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } obj = TestIntfPrxHelper.uncheckedCast(obj.ice_endpointSelection(Ice.EndpointSelectionType.Random)); @@ -423,7 +423,7 @@ public class AllTests : TestCommon.AllTests while(names.Count > 0) { names.Remove(obj.getAdapterName()); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } deactivate(com, adapters); @@ -480,11 +480,11 @@ public class AllTests : TestCommon.AllTests adapters.Add(com.createObjectAdapter("Adapter36", endpoints[2].ToString())); for(i = 0; i < nRetry && obj.getAdapterName().Equals("Adapter36"); i++); test(i == nRetry); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); adapters.Add(com.createObjectAdapter("Adapter35", endpoints[1].ToString())); for(i = 0; i < nRetry && obj.getAdapterName().Equals("Adapter35"); i++); test(i == nRetry); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); adapters.Add(com.createObjectAdapter("Adapter34", endpoints[0].ToString())); for(i = 0; i < nRetry && obj.getAdapterName().Equals("Adapter34"); i++); test(i == nRetry); @@ -761,7 +761,7 @@ public class AllTests : TestCommon.AllTests for(i = 0; i < 5; i++) { test(obj.getAdapterName().Equals("Adapter82")); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } TestIntfPrx testSecure = TestIntfPrxHelper.uncheckedCast(obj.ice_secure(true)); @@ -777,7 +777,7 @@ public class AllTests : TestCommon.AllTests for(i = 0; i < 5; i++) { test(obj.getAdapterName().Equals("Adapter81")); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } com.createObjectAdapter("Adapter83", (obj.ice_getEndpoints()[1]).ToString()); // Reactive tcp OA. @@ -785,7 +785,7 @@ public class AllTests : TestCommon.AllTests for(i = 0; i < 5; i++) { test(obj.getAdapterName().Equals("Adapter83")); - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } com.deactivateObjectAdapter((RemoteObjectAdapterPrx)adapters[0]); diff --git a/csharp/test/Ice/hold/AllTests.cs b/csharp/test/Ice/hold/AllTests.cs index 41c2aab173f..41773301b3c 100644 --- a/csharp/test/Ice/hold/AllTests.cs +++ b/csharp/test/Ice/hold/AllTests.cs @@ -187,7 +187,7 @@ public class AllTests : TestCommon.AllTests { result.waitForSent(); holdSerialized.ice_ping(); // Ensure everything's dispatched. - holdSerialized.ice_getConnection().close(false); + holdSerialized.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } result.waitForCompleted(); diff --git a/csharp/test/Ice/location/AllTests.cs b/csharp/test/Ice/location/AllTests.cs index adf84b5abda..aa4a23a3a22 100644 --- a/csharp/test/Ice/location/AllTests.cs +++ b/csharp/test/Ice/location/AllTests.cs @@ -533,7 +533,7 @@ public class AllTests : TestCommon.AllTests Flush(); hello = HelloPrxHelper.checkedCast(communicator.stringToProxy("hello")); obj.migrateHello(); - hello.ice_getConnection().close(false); + hello.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); hello.sayHello(); obj.migrateHello(); hello.sayHello(); diff --git a/csharp/test/Ice/metrics/AllTests.cs b/csharp/test/Ice/metrics/AllTests.cs index e22d7b65c12..41cc8468930 100644 --- a/csharp/test/Ice/metrics/AllTests.cs +++ b/csharp/test/Ice/metrics/AllTests.cs @@ -271,7 +271,7 @@ public class AllTests : TestCommon.AllTests { if(proxy.ice_getCachedConnection() != null) { - proxy.ice_getCachedConnection().close(false); + proxy.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } try @@ -284,7 +284,7 @@ public class AllTests : TestCommon.AllTests if(proxy.ice_getCachedConnection() != null) { - proxy.ice_getCachedConnection().close(false); + proxy.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -486,8 +486,8 @@ public class AllTests : TestCommon.AllTests if(!collocated) { - metrics.ice_getConnection().close(false); - metrics.ice_connectionId("Con1").ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + metrics.ice_connectionId("Con1").ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -590,7 +590,7 @@ public class AllTests : TestCommon.AllTests map = toMap(serverMetrics.getMetricsView("View", out timestamp)["Connection"]); test(map["holding"].current == 1); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); map = toMap(clientMetrics.getMetricsView("View", out timestamp)["Connection"]); test(map["closing"].current == 1); @@ -605,7 +605,7 @@ public class AllTests : TestCommon.AllTests props["IceMX.Metrics.View.Map.Connection.GroupBy"] = "none"; updateProps(clientProps, serverProps, update, props, "Connection"); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); metrics.ice_timeout(500).ice_ping(); controller.hold(); @@ -661,7 +661,7 @@ public class AllTests : TestCommon.AllTests testAttribute(clientMetrics, clientProps, update, "Connection", "mcastHost", ""); testAttribute(clientMetrics, clientProps, update, "Connection", "mcastPort", ""); - m.ice_getConnection().close(false); + m.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -681,7 +681,7 @@ public class AllTests : TestCommon.AllTests IceMX.Metrics m1 = clientMetrics.getMetricsView("View", out timestamp)["ConnectionEstablishment"][0]; test(m1.current == 0 && m1.total == 1 && m1.id.Equals(hostAndPort)); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); controller.hold(); try { @@ -733,7 +733,7 @@ public class AllTests : TestCommon.AllTests try { prx.ice_ping(); - prx.ice_getConnection().close(false); + prx.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } catch(Ice.LocalException) { @@ -743,8 +743,6 @@ public class AllTests : TestCommon.AllTests m1 = clientMetrics.getMetricsView("View", out timestamp)["EndpointLookup"][0]; test(m1.current <= 1 && m1.total == 1); - prx.ice_getConnection().close(false); - bool dnsException = false; try { diff --git a/csharp/test/Ice/metrics/MetricsAMDI.cs b/csharp/test/Ice/metrics/MetricsAMDI.cs index e208bddfd38..d85fa95c919 100644 --- a/csharp/test/Ice/metrics/MetricsAMDI.cs +++ b/csharp/test/Ice/metrics/MetricsAMDI.cs @@ -41,7 +41,7 @@ public sealed class MetricsI : MetricsDisp_ override public Task failAsync(Ice.Current current) { - current.con.close(true); + current.con.close(Ice.ConnectionClose.CloseForcefully); return null; } diff --git a/csharp/test/Ice/metrics/MetricsI.cs b/csharp/test/Ice/metrics/MetricsI.cs index 6df99c878a9..4139191de9e 100644 --- a/csharp/test/Ice/metrics/MetricsI.cs +++ b/csharp/test/Ice/metrics/MetricsI.cs @@ -40,7 +40,7 @@ public sealed class MetricsI : MetricsDisp_ override public void fail(Ice.Current current) { - current.con.close(true); + current.con.close(Ice.ConnectionClose.CloseForcefully); } override public void opWithUserException(Ice.Current current) diff --git a/csharp/test/Ice/operations/BatchOneways.cs b/csharp/test/Ice/operations/BatchOneways.cs index aae9fedbe09..ee21049b0c7 100644 --- a/csharp/test/Ice/operations/BatchOneways.cs +++ b/csharp/test/Ice/operations/BatchOneways.cs @@ -100,7 +100,7 @@ class BatchOneways batch1.ice_ping(); batch2.ice_ping(); batch1.ice_flushBatchRequests(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch1.ice_ping(); batch2.ice_ping(); @@ -108,7 +108,7 @@ class BatchOneways batch2.ice_getConnection(); batch1.ice_ping(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch1.ice_ping(); batch2.ice_ping(); } diff --git a/csharp/test/Ice/operations/BatchOnewaysAMI.cs b/csharp/test/Ice/operations/BatchOnewaysAMI.cs index 87615c540ae..e482b261eb1 100644 --- a/csharp/test/Ice/operations/BatchOnewaysAMI.cs +++ b/csharp/test/Ice/operations/BatchOnewaysAMI.cs @@ -91,7 +91,7 @@ class BatchOnewaysAMI batch1.begin_ice_ping(); batch2.begin_ice_ping(); batch1.end_ice_flushBatchRequests(batch1.begin_ice_flushBatchRequests()); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch1.begin_ice_ping(); batch2.begin_ice_ping(); @@ -99,7 +99,7 @@ class BatchOnewaysAMI batch2.ice_getConnection(); batch1.begin_ice_ping(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch1.begin_ice_ping(); batch2.begin_ice_ping(); diff --git a/csharp/test/Ice/retry/RetryI.cs b/csharp/test/Ice/retry/RetryI.cs index d7370c7edaa..952527cf15d 100644 --- a/csharp/test/Ice/retry/RetryI.cs +++ b/csharp/test/Ice/retry/RetryI.cs @@ -19,7 +19,7 @@ public sealed class RetryI : Test.RetryDisp_ { if(current.con != null) { - current.con.close(true); + current.con.close(Ice.ConnectionClose.CloseForcefully); } else { diff --git a/csharp/test/Ice/timeout/AllTests.cs b/csharp/test/Ice/timeout/AllTests.cs index e466f5f64d9..7c405f5872b 100644 --- a/csharp/test/Ice/timeout/AllTests.cs +++ b/csharp/test/Ice/timeout/AllTests.cs @@ -248,7 +248,7 @@ public class AllTests : TestCommon.AllTests Test.TimeoutPrx to = Test.TimeoutPrxHelper.checkedCast(obj.ice_timeout(100)); Ice.Connection connection = to.ice_getConnection(); timeout.holdAdapter(500); - connection.close(false); + connection.close(Ice.ConnectionClose.CloseGracefullyAndWait); try { connection.getInfo(); // getInfo() doesn't throw in the closing state. @@ -263,9 +263,10 @@ public class AllTests : TestCommon.AllTests connection.getInfo(); test(false); } - catch(Ice.CloseConnectionException) + catch(Ice.ConnectionManuallyClosedException ex) { // Expected. + test(ex.graceful); } timeout.op(); // Ensure adapter is active. } diff --git a/csharp/test/Ice/udp/AllTests.cs b/csharp/test/Ice/udp/AllTests.cs index da166bcafc3..710a0acb287 100644 --- a/csharp/test/Ice/udp/AllTests.cs +++ b/csharp/test/Ice/udp/AllTests.cs @@ -126,7 +126,7 @@ public class AllTests // test(seq.Length > 16384); } - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); communicator.getProperties().setProperty("Ice.UDP.SndSize", "64000"); seq = new byte[50000]; try diff --git a/csharp/test/IceSSL/configuration/AllTests.cs b/csharp/test/IceSSL/configuration/AllTests.cs index 425d99f00f0..64ae2a9932c 100644 --- a/csharp/test/IceSSL/configuration/AllTests.cs +++ b/csharp/test/IceSSL/configuration/AllTests.cs @@ -948,7 +948,7 @@ public class AllTests // verifier.reset(); verifier.returnValue(false); - server.ice_getConnection().close(false); + server.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { server.ice_ping(); diff --git a/java-compat/src/Ice/src/main/java/Ice/Callback_Connection_heartbeat.java b/java-compat/src/Ice/src/main/java/Ice/Callback_Connection_heartbeat.java new file mode 100644 index 00000000000..e02ef2906c7 --- /dev/null +++ b/java-compat/src/Ice/src/main/java/Ice/Callback_Connection_heartbeat.java @@ -0,0 +1,55 @@ +// ********************************************************************** +// +// Copyright (c) 2003-2016 ZeroC, Inc. All rights reserved. +// +// This copy of Ice is licensed to you under the terms described in the +// ICE_LICENSE file included in this distribution. +// +// ********************************************************************** + +package Ice; + +/** + * Asynchronous callback base class for Connection.begin_heartbeat. + **/ +public abstract class Callback_Connection_heartbeat extends IceInternal.CallbackBase +{ + /** + * Called when the invocation raises an Ice run-time exception. + * + * @param ex The Ice run-time exception raised by the operation. + **/ + public abstract void exception(LocalException ex); + + /** + * Called when a queued invocation is sent successfully. + **/ + public void sent(boolean sentSynchronously) + { + } + + @Override + public final void _iceCompleted(AsyncResult __result) + { + try + { + __result.getConnection().end_heartbeat(__result); + } + catch(LocalException __ex) + { + exception(__ex); + } + } + + @Override + public final void _iceSent(AsyncResult __result) + { + sent(__result.sentSynchronously()); + } + + @Override + public final boolean _iceHasSentCallback() + { + return true; + } +} diff --git a/java-compat/src/Ice/src/main/java/Ice/ConnectionI.java b/java-compat/src/Ice/src/main/java/Ice/ConnectionI.java index f4f1b32f4bf..84131a38cfc 100644 --- a/java-compat/src/Ice/src/main/java/Ice/ConnectionI.java +++ b/java-compat/src/Ice/src/main/java/Ice/ConnectionI.java @@ -155,25 +155,27 @@ public final class ConnectionI extends IceInternal.EventHandler } @Override - synchronized public void close(boolean force) + synchronized public void close(ConnectionClose mode) { if(Thread.interrupted()) { throw new Ice.OperationInterruptedException(); } - if(force) + if(mode == ConnectionClose.CloseForcefully) { - setState(StateClosed, new ForcedCloseConnectionException()); + setState(StateClosed, new ConnectionManuallyClosedException(false)); + } + else if(mode == ConnectionClose.CloseGracefully) + { + setState(StateClosing, new ConnectionManuallyClosedException(true)); } else { + assert(mode == ConnectionClose.CloseGracefullyAndWait); + // - // If we do a graceful shutdown, then we wait until all - // outstanding requests have been completed. Otherwise, - // the CloseConnectionException will cause all outstanding - // requests to be retried, regardless of whether the - // server has processed them or not. + // Wait until all outstanding requests have been completed. // while(!_asyncRequests.isEmpty()) { @@ -187,7 +189,7 @@ public final class ConnectionI extends IceInternal.EventHandler } } - setState(StateClosing, new CloseConnectionException()); + setState(StateClosing, new ConnectionManuallyClosedException(true)); } } @@ -279,14 +281,14 @@ public final class ConnectionI extends IceInternal.EventHandler // We send a heartbeat if there was no activity in the last // (timeout / 4) period. Sending a heartbeat sooner than // really needed is safer to ensure that the receiver will - // receive in time the heartbeat. Sending the heartbeat if + // receive the heartbeat in time. Sending the heartbeat if // there was no activity in the last (timeout / 2) period // isn't enough since monitor() is called only every (timeout // / 2) period. // // Note that this doesn't imply that we are sending 4 // heartbeats per timeout period because the monitor() method - // is sill only called every (timeout / 2) period. + // is still only called every (timeout / 2) period. // if(acm.heartbeat == ACMHeartbeat.HeartbeatAlways || (acm.heartbeat != ACMHeartbeat.HeartbeatOff && _writeStream.isEmpty() && @@ -518,6 +520,155 @@ public final class ConnectionI extends IceInternal.EventHandler } @Override + public void heartbeat() + { + end_heartbeat(begin_heartbeat()); + } + + private static final String __heartbeat_name = "heartbeat"; + + @Override + public AsyncResult begin_heartbeat() + { + return begin_heartbeatInternal(null); + } + + @Override + public AsyncResult begin_heartbeat(Callback cb) + { + return begin_heartbeatInternal(cb); + } + + @Override + public AsyncResult begin_heartbeat(Callback_Connection_heartbeat cb) + { + return begin_heartbeatInternal(cb); + } + + @Override + public AsyncResult begin_heartbeat(IceInternal.Functional_VoidCallback __responseCb, + final IceInternal.Functional_GenericCallback1<Ice.Exception> __exceptionCb, + IceInternal.Functional_BoolCallback __sentCb) + { + return begin_heartbeatInternal(new IceInternal.Functional_CallbackBase(false, __exceptionCb, __sentCb) + { + @Override + public final void _iceCompleted(AsyncResult __result) + { + try + { + __result.getConnection().end_heartbeat(__result); + } + catch(Exception __ex) + { + __exceptionCb.apply(__ex); + } + } + }); + } + + static class HeartbeatAsync extends IceInternal.OutgoingAsyncBase + { + public static HeartbeatAsync check(AsyncResult r, Connection con, String operation) + { + check(r, operation); + if(!(r instanceof HeartbeatAsync)) + { + throw new IllegalArgumentException("Incorrect AsyncResult object for end_" + operation + " method"); + } + if(r.getConnection() != con) + { + throw new IllegalArgumentException("Connection for call to end_" + operation + + " does not match connection that was used to call corresponding " + + "begin_" + operation + " method"); + } + return (HeartbeatAsync)r; + } + + public HeartbeatAsync(ConnectionI con, Communicator communicator, IceInternal.Instance instance, + String operation, IceInternal.CallbackBase callback) + { + super(communicator, instance, operation, callback); + _connection = con; + } + + @Override + public Connection getConnection() + { + return _connection; + } + + public void invoke() + { + try + { + _os.writeBlob(IceInternal.Protocol.magic); + ProtocolVersion.ice_write(_os, IceInternal.Protocol.currentProtocol); + EncodingVersion.ice_write(_os, IceInternal.Protocol.currentProtocolEncoding); + _os.writeByte(IceInternal.Protocol.validateConnectionMsg); + _os.writeByte((byte) 0); + _os.writeInt(IceInternal.Protocol.headerSize); // Message size. + + int status; + if(_instance.queueRequests()) + { + status = _instance.getQueueExecutor().execute(new Callable<Integer>() + { + @Override + public Integer call() throws IceInternal.RetryException + { + return _connection.sendAsyncRequest(HeartbeatAsync.this, false, false, 0); + } + }); + } + else + { + status = _connection.sendAsyncRequest(this, false, false, 0); + } + + if((status & IceInternal.AsyncStatus.Sent) > 0) + { + _sentSynchronously = true; + if((status & IceInternal.AsyncStatus.InvokeSentCallback) > 0) + { + invokeSent(); + } + } + } + catch(IceInternal.RetryException ex) + { + if(completed(ex.get())) + { + invokeCompletedAsync(); + } + } + catch(Ice.Exception ex) + { + if(completed(ex)) + { + invokeCompletedAsync(); + } + } + } + + private Ice.ConnectionI _connection; + } + + private AsyncResult begin_heartbeatInternal(IceInternal.CallbackBase cb) + { + HeartbeatAsync result = new HeartbeatAsync(this, _communicator, _instance, __heartbeat_name, cb); + result.invoke(); + return result; + } + + @Override + public void end_heartbeat(AsyncResult ir) + { + HeartbeatAsync r = HeartbeatAsync.check(ir, this, __heartbeat_name); + r.waitForResponseOrUserEx(); + } + + @Override synchronized public void setACM(Ice.IntOptional timeout, Ice.Optional<ACMClose> close, Ice.Optional<ACMHeartbeat> heartbeat) { @@ -703,8 +854,7 @@ public final class ConnectionI extends IceInternal.EventHandler public synchronized void invokeException(int requestId, LocalException ex, int invokeNum, boolean amd) { // - // Fatal exception while invoking a request. Since - // sendResponse/sendNoResponse isn't + // Fatal exception while invoking a request. Since sendResponse/sendNoResponse isn't // called in case of a fatal exception we decrement _dispatchCount here. // @@ -968,7 +1118,7 @@ public final class ConnectionI extends IceInternal.EventHandler } else { - assert (_state <= StateClosingPending); + assert(_state <= StateClosingPending); // // We parse messages first, if we receive a close @@ -1144,7 +1294,8 @@ public final class ConnectionI extends IceInternal.EventHandler // if(info.invokeNum > 0) { - invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, info.adapter); + invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, + info.adapter); // // Don't increase dispatchedCount, the dispatch count is @@ -1301,7 +1452,7 @@ public final class ConnectionI extends IceInternal.EventHandler // Trace the cause of unexpected connection closures // if(!(_exception instanceof CloseConnectionException || - _exception instanceof ForcedCloseConnectionException || + _exception instanceof ConnectionManuallyClosedException || _exception instanceof ConnectionTimeoutException || _exception instanceof CommunicatorDestroyedException || _exception instanceof ObjectAdapterDeactivatedException)) @@ -1650,7 +1801,7 @@ public final class ConnectionI extends IceInternal.EventHandler // Don't warn about certain expected exceptions. // if(!(_exception instanceof CloseConnectionException || - _exception instanceof ForcedCloseConnectionException || + _exception instanceof ConnectionManuallyClosedException || _exception instanceof ConnectionTimeoutException || _exception instanceof CommunicatorDestroyedException || _exception instanceof ObjectAdapterDeactivatedException || @@ -1840,7 +1991,7 @@ public final class ConnectionI extends IceInternal.EventHandler if(_observer != null && state == StateClosed && _exception != null) { if(!(_exception instanceof CloseConnectionException || - _exception instanceof ForcedCloseConnectionException || + _exception instanceof ConnectionManuallyClosedException || _exception instanceof ConnectionTimeoutException || _exception instanceof CommunicatorDestroyedException || _exception instanceof ObjectAdapterDeactivatedException || @@ -1869,8 +2020,7 @@ public final class ConnectionI extends IceInternal.EventHandler private void initiateShutdown() { - assert (_state == StateClosing); - assert (_dispatchCount == 0); + assert(_state == StateClosing && _dispatchCount == 0); if(_shutdownInitiated) { @@ -1897,8 +2047,7 @@ public final class ConnectionI extends IceInternal.EventHandler setState(StateClosingPending); // - // Notify the the transceiver of the graceful connection - // closure. + // Notify the the transceiver of the graceful connection closure. // int op = _transceiver.closing(true, _exception); if(op != 0) @@ -1910,7 +2059,7 @@ public final class ConnectionI extends IceInternal.EventHandler } } - private void heartbeat() + private void sendHeartbeatNow() { assert (_state == StateActive); @@ -1948,8 +2097,7 @@ public final class ConnectionI extends IceInternal.EventHandler } // - // Update the connection description once the transceiver is - // initialized. + // Update the connection description once the transceiver is initialized. // _desc = _transceiver.toString(); _initialized = true; @@ -2108,8 +2256,7 @@ public final class ConnectionI extends IceInternal.EventHandler } else if(_state == StateClosingPending && _writeStream.pos() == 0) { - // Message wasn't sent, empty the _writeStream, we're not going to - // send more data. + // Message wasn't sent, empty the _writeStream, we're not going to send more data. OutgoingMessage message = _sendStreams.getFirst(); _writeStream.swap(message.stream); return IceInternal.SocketOperation.None; @@ -2416,8 +2563,7 @@ public final class ConnectionI extends IceInternal.EventHandler setState(StateClosingPending, new CloseConnectionException()); // - // Notify the the transceiver of the graceful connection - // closure. + // Notify the the transceiver of the graceful connection closure. // int op = _transceiver.closing(false, _exception); if(op != 0) diff --git a/java-compat/src/Ice/src/main/java/IceInternal/IncomingConnectionFactory.java b/java-compat/src/Ice/src/main/java/IceInternal/IncomingConnectionFactory.java index 9405e3dcc17..108df4607e0 100644 --- a/java-compat/src/Ice/src/main/java/IceInternal/IncomingConnectionFactory.java +++ b/java-compat/src/Ice/src/main/java/IceInternal/IncomingConnectionFactory.java @@ -141,7 +141,7 @@ public final class IncomingConnectionFactory extends EventHandler implements Ice // for(Ice.ConnectionI c : connections) { - c.close(true); + c.close(Ice.ConnectionClose.CloseForcefully); } throw e; } diff --git a/java-compat/src/Ice/src/main/java/IceInternal/OutgoingConnectionFactory.java b/java-compat/src/Ice/src/main/java/IceInternal/OutgoingConnectionFactory.java index 94b80c0e2fb..b51856cf3e6 100644 --- a/java-compat/src/Ice/src/main/java/IceInternal/OutgoingConnectionFactory.java +++ b/java-compat/src/Ice/src/main/java/IceInternal/OutgoingConnectionFactory.java @@ -133,7 +133,7 @@ public final class OutgoingConnectionFactory { for(Ice.ConnectionI c : l) { - c.close(true); + c.close(Ice.ConnectionClose.CloseForcefully); } } throw new Ice.OperationInterruptedException(); diff --git a/java-compat/test/src/main/java/test/Ice/acm/AllTests.java b/java-compat/test/src/main/java/test/Ice/acm/AllTests.java index a4369b684b8..05465c8e3cb 100644 --- a/java-compat/test/src/main/java/test/Ice/acm/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/acm/AllTests.java @@ -575,6 +575,31 @@ public class AllTests } } + static class HeartbeatManualTest extends TestCase + { + public HeartbeatManualTest(Application app, RemoteCommunicatorPrx com, java.io.PrintWriter out) + { + super(app, "manual heartbeats", com, out); + // + // Disable heartbeats. + // + setClientACM(10, -1, 0); + setServerACM(10, -1, 0); + } + + public void runTestCase(RemoteObjectAdapterPrx adapter, TestIntfPrx proxy) + { + proxy.startHeartbeatCount(); + Ice.Connection con = proxy.ice_getConnection(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + proxy.waitForHeartbeatCount(5); + } + } + static class SetACMTest extends TestCase { public SetACMTest(Application app, RemoteCommunicatorPrx com, java.io.PrintWriter out) @@ -607,7 +632,8 @@ public class AllTests test(acm.heartbeat == Ice.ACMHeartbeat.HeartbeatAlways); // Make sure the client sends few heartbeats to the server - proxy.waitForHeartbeat(2); + proxy.startHeartbeatCount(); + proxy.waitForHeartbeatCount(2); } } @@ -634,6 +660,7 @@ public class AllTests tests.add(new HeartbeatOnIdleTest(app, com, out)); tests.add(new HeartbeatAlwaysTest(app, com, out)); + tests.add(new HeartbeatManualTest(app, com, out)); tests.add(new SetACMTest(app, com, out)); for(TestCase test : tests) diff --git a/java-compat/test/src/main/java/test/Ice/acm/Test.ice b/java-compat/test/src/main/java/test/Ice/acm/Test.ice index 117192a2df8..7f71427c741 100644 --- a/java-compat/test/src/main/java/test/Ice/acm/Test.ice +++ b/java-compat/test/src/main/java/test/Ice/acm/Test.ice @@ -18,7 +18,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/java-compat/test/src/main/java/test/Ice/acm/TestI.java b/java-compat/test/src/main/java/test/Ice/acm/TestI.java index 00620c07dd0..a83cb645525 100644 --- a/java-compat/test/src/main/java/test/Ice/acm/TestI.java +++ b/java-compat/test/src/main/java/test/Ice/acm/TestI.java @@ -49,27 +49,34 @@ public class TestI extends _TestIntfDisp } } - public void waitForHeartbeat(int count, Ice.Current current) + public void startHeartbeatCount(Ice.Current current) { - final Ice.Holder<Integer> c = new Ice.Holder<Integer>(count); + _counter = new Ice.Holder<Integer>(0); Ice.HeartbeatCallback callback = new Ice.HeartbeatCallback() { - synchronized public void heartbeat(Ice.Connection connection) + public void heartbeat(Ice.Connection connection) { - --c.value; - notifyAll(); + synchronized(_counter) + { + ++_counter.value; + _counter.notifyAll(); + } } }; current.con.setHeartbeatCallback(callback); + } - synchronized(callback) + public void waitForHeartbeatCount(int count, Ice.Current current) + { + assert(_counter != null); + synchronized(_counter) { - while(c.value > 0) + while(_counter.value < count) { try { - callback.wait(); + _counter.wait(); } catch(InterruptedException ex) { @@ -77,4 +84,6 @@ public class TestI extends _TestIntfDisp } } } -}; + + private Ice.Holder<Integer> _counter; +} diff --git a/java-compat/test/src/main/java/test/Ice/ami/AMI.java b/java-compat/test/src/main/java/test/Ice/ami/AMI.java index c018ccb8c62..c707f20e6c7 100644 --- a/java-compat/test/src/main/java/test/Ice/ami/AMI.java +++ b/java-compat/test/src/main/java/test/Ice/ami/AMI.java @@ -19,6 +19,7 @@ import test.Ice.ami.Test.Callback_TestIntf_op; import test.Ice.ami.Test.Callback_TestIntf_opWithResult; import test.Ice.ami.Test.Callback_TestIntf_opWithUE; import test.Ice.ami.Test.Callback_TestIntf_opWithPayload; +import test.Ice.ami.Test.CloseMode; import test.Util.Application; public class AMI @@ -546,6 +547,18 @@ public class AMI } } + static class CloseCallback extends CallbackBase implements Ice.CloseCallback + { + CloseCallback() + { + } + + public void closed(Ice.Connection con) + { + called(); + } + } + enum ThrowType { LocalException, OtherException }; static class Thrower extends CallbackBase @@ -1817,7 +1830,7 @@ public class AMI test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests( new Ice.Callback() @@ -1879,7 +1892,7 @@ public class AMI TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.ice_getConnection(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests( new Ice.Callback_Object_ice_flushBatchRequests() @@ -1948,7 +1961,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushExCallback cb = new FlushExCallback(); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests( new Ice.Callback() @@ -2010,7 +2023,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushExCallback cb = new FlushExCallback(); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests( new Ice.Callback_Connection_flushBatchRequests() @@ -2077,7 +2090,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( new Ice.Callback() @@ -2151,7 +2164,7 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( new Ice.Callback() @@ -2188,8 +2201,8 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( new Ice.Callback() @@ -2251,7 +2264,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast( p.ice_getConnection().createProxy(p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( new Ice.Callback_Communicator_flushBatchRequests() @@ -2325,7 +2338,7 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( new Ice.Callback_Communicator_flushBatchRequests() @@ -2362,8 +2375,8 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( new Ice.Callback_Communicator_flushBatchRequests() @@ -2588,9 +2601,36 @@ public class AMI if(p.ice_getConnection() != null) { - out.print("testing close connection with sending queue... "); + out.print("testing graceful close connection with wait... "); out.flush(); { + // + // Local case: begin several requests, close the connection gracefully, and make sure it waits + // for the requests to complete. + // + java.util.List<Ice.AsyncResult> results = new java.util.ArrayList<>(); + for(int i = 0; i < 3; ++i) + { + results.add(p.begin_sleep(50)); + } + p.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + for(Ice.AsyncResult r : results) + { + r.waitForCompleted(); + try + { + r.throwLocalException(); + } + catch(Throwable ex) + { + test(false); + } + } + } + { + // + // Remote case. + // byte[] seq = new byte[1024 * 10]; // @@ -2604,12 +2644,12 @@ public class AMI { done = true; p.ice_ping(); - java.util.List<Ice.AsyncResult> results = new java.util.ArrayList<Ice.AsyncResult>(); + java.util.List<Ice.AsyncResult> results = new java.util.ArrayList<>(); for(int i = 0; i < maxQueue; ++i) { results.add(p.begin_opWithPayload(seq)); } - if(!p.begin_close(false).isSent()) + if(!p.begin_close(CloseMode.CloseGracefullyAndWait).isSent()) { for(int i = 0; i < maxQueue; i++) { @@ -2643,6 +2683,98 @@ public class AMI } } out.println("ok"); + + out.print("testing graceful close connection without wait... "); + out.flush(); + { + // + // Local case: start a lengthy operation and then close the connection gracefully on the client side + // without waiting for the pending invocation to complete. There will be no retry and we expect the + // invocation to fail with ConnectionManuallyClosedException. + // + // This test requires two threads in the server's thread pool: one will block in sleep() and the other + // will process the CloseConnection message. + // + p.ice_ping(); + Ice.Connection con = p.ice_getConnection(); + Ice.AsyncResult r = p.begin_sleep(100); + con.close(Ice.ConnectionClose.CloseGracefully); + r.waitForCompleted(); + try + { + r.throwLocalException(); + test(false); + } + catch(Ice.ConnectionManuallyClosedException ex) + { + test(ex.graceful); + } + + // + // Remote case: the server closes the connection gracefully. Our call to TestIntf::close() + // completes successfully and then the connection should be closed immediately afterward, + // despite the fact that there's a pending call to sleep(). The call to sleep() should be + // automatically retried and complete successfully. + // + p.ice_ping(); + con = p.ice_getConnection(); + CloseCallback cb = new CloseCallback(); + con.setCloseCallback(cb); + r = p.begin_sleep(100); + p.close(CloseMode.CloseGracefully); + cb.check(); + r.waitForCompleted(); + try + { + r.throwLocalException(); + } + catch(Ice.LocalException ex) + { + test(false); + } + p.ice_ping(); + test(p.ice_getConnection() != con); + } + out.println("ok"); + + out.print("testing forceful close connection... "); + out.flush(); + { + // + // Local case: start a lengthy operation and then close the connection forcefully on the client side. + // There will be no retry and we expect the invocation to fail with ConnectionManuallyClosedException. + // + p.ice_ping(); + Ice.Connection con = p.ice_getConnection(); + Ice.AsyncResult r = p.begin_sleep(100); + con.close(Ice.ConnectionClose.CloseForcefully); + r.waitForCompleted(); + try + { + r.throwLocalException(); + test(false); + } + catch(Ice.ConnectionManuallyClosedException ex) + { + test(!ex.graceful); + } + + // + // Remote case: the server closes the connection forcefully. This causes the request to fail + // with a ConnectionLostException. Since the close() operation is not idempotent, the client + // will not retry. + // + try + { + p.close(CloseMode.CloseForcefully); + test(false); + } + catch(Ice.ConnectionLostException ex) + { + // Expected. + } + } + out.println("ok"); } } } diff --git a/java-compat/test/src/main/java/test/Ice/ami/Client.java b/java-compat/test/src/main/java/test/Ice/ami/Client.java index 097bb922ce6..b261e603777 100644 --- a/java-compat/test/src/main/java/test/Ice/ami/Client.java +++ b/java-compat/test/src/main/java/test/Ice/ami/Client.java @@ -24,6 +24,7 @@ public class Client extends test.Util.Application Ice.InitializationData initData = super.getInitData(argsH); initData.properties.setProperty("Ice.Package.Test", "test.Ice.ami"); initData.properties.setProperty("Ice.Warn.AMICallback", "0"); + initData.properties.setProperty("Ice.Warn.Connections", "0"); // // Limit the send buffer size, this test relies on the socket diff --git a/java-compat/test/src/main/java/test/Ice/ami/Server.java b/java-compat/test/src/main/java/test/Ice/ami/Server.java index 1b826ef52e9..62dfc59af45 100644 --- a/java-compat/test/src/main/java/test/Ice/ami/Server.java +++ b/java-compat/test/src/main/java/test/Ice/ami/Server.java @@ -34,6 +34,12 @@ public class Server extends test.Util.Application initData.properties.setProperty("TestAdapter.Endpoints", getTestEndpoint(initData.properties, 0)); initData.properties.setProperty("ControllerAdapter.Endpoints", getTestEndpoint(initData.properties, 1)); initData.properties.setProperty("ControllerAdapter.ThreadPool.Size", "1"); + + // + // This test kills connections, so we don't want warnings. + // + initData.properties.setProperty("Ice.Warn.Connections", "0"); + // // Limit the recv buffer size, this test relies on the socket // send() blocking after sending a given amount of data. diff --git a/java-compat/test/src/main/java/test/Ice/ami/Test.ice b/java-compat/test/src/main/java/test/Ice/ami/Test.ice index 82b3fe6d383..bbc4d908d65 100644 --- a/java-compat/test/src/main/java/test/Ice/ami/Test.ice +++ b/java-compat/test/src/main/java/test/Ice/ami/Test.ice @@ -20,6 +20,13 @@ exception TestIntfException { }; +enum CloseMode +{ + CloseForcefully, + CloseGracefully, + CloseGracefullyAndWait +}; + interface TestIntf { void op(); @@ -30,7 +37,8 @@ interface TestIntf void opBatch(); int opBatchCount(); bool waitForBatch(int count); - void close(bool force); + void close(CloseMode mode); + void sleep(int ms); void shutdown(); bool supportsFunctionalTests(); diff --git a/java-compat/test/src/main/java/test/Ice/ami/TestI.java b/java-compat/test/src/main/java/test/Ice/ami/TestI.java index b0c6df0cf4a..af4c50e5d33 100644 --- a/java-compat/test/src/main/java/test/Ice/ami/TestI.java +++ b/java-compat/test/src/main/java/test/Ice/ami/TestI.java @@ -10,6 +10,7 @@ package test.Ice.ami; import test.Ice.ami.Test._TestIntfDisp; +import test.Ice.ami.Test.CloseMode; import test.Ice.ami.Test.TestIntfException; public class TestI extends _TestIntfDisp @@ -129,9 +130,22 @@ public class TestI extends _TestIntfDisp @Override public void - close(boolean force, Ice.Current current) + close(CloseMode mode, Ice.Current current) { - current.con.close(force); + current.con.close(Ice.ConnectionClose.valueOf(mode.value())); + } + + @Override + public void + sleep(int ms, Ice.Current current) + { + try + { + Thread.sleep(ms); + } + catch(InterruptedException ex) + { + } } @Override diff --git a/java-compat/test/src/main/java/test/Ice/ami/lambda/AMI.java b/java-compat/test/src/main/java/test/Ice/ami/lambda/AMI.java index eb572f481e2..8d886c20d78 100644 --- a/java-compat/test/src/main/java/test/Ice/ami/lambda/AMI.java +++ b/java-compat/test/src/main/java/test/Ice/ami/lambda/AMI.java @@ -845,7 +845,7 @@ public class AMI test(p.opBatchCount() == 0); TestIntfPrx b1 = (TestIntfPrx)p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = b1.begin_ice_flushBatchRequests( null, @@ -901,7 +901,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushExCallback cb = new FlushExCallback(); Ice.AsyncResult r = b1.ice_getConnection().begin_flushBatchRequests( null, @@ -946,7 +946,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrxHelper.uncheckedCast(p.ice_getConnection().createProxy( p.ice_getIdentity()).ice_batchOneway()); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( null, @@ -998,7 +998,7 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( null, @@ -1024,8 +1024,8 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final FlushCallback cb = new FlushCallback(); Ice.AsyncResult r = communicator.begin_flushBatchRequests( null, diff --git a/java-compat/test/src/main/java/test/Ice/background/AllTests.java b/java-compat/test/src/main/java/test/Ice/background/AllTests.java index 187c7bbf4f2..e3f92a9f4d9 100644 --- a/java-compat/test/src/main/java/test/Ice/background/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/background/AllTests.java @@ -398,7 +398,7 @@ public class AllTests configuration.buffered(true); backgroundController.buffered(true); background.begin_op(); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); background.begin_op(); OpAMICallbackNoOp cb = new OpAMICallbackNoOp(); @@ -439,7 +439,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); for(int i = 0; i < 4; ++i) { @@ -505,7 +505,7 @@ public class AllTests } configuration.connectException(new Ice.SocketException()); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -548,7 +548,7 @@ public class AllTests ex.printStackTrace(); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); for(int i = 0; i < 4; i++) { @@ -611,7 +611,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -623,7 +623,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -667,7 +667,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -702,7 +702,7 @@ public class AllTests } configuration.initializeException(new Ice.SocketException()); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -728,7 +728,7 @@ public class AllTests } configuration.initializeSocketStatus(IceInternal.SocketOperation.Write); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); try { @@ -743,7 +743,7 @@ public class AllTests configuration.initializeSocketStatus(IceInternal.SocketOperation.None); ctl.initializeException(true); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -771,7 +771,7 @@ public class AllTests try { ctl.initializeSocketStatus(IceInternal.SocketOperation.Write); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); background.op(); ctl.initializeSocketStatus(IceInternal.SocketOperation.None); } @@ -806,7 +806,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -852,7 +852,7 @@ public class AllTests ex.printStackTrace(); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -922,7 +922,7 @@ public class AllTests ex.printStackTrace(); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -952,7 +952,7 @@ public class AllTests backgroundBatchOneway.op(); ctl.resumeAdapter(); backgroundBatchOneway.ice_flushBatchRequests(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); // // Send bigger requests to test with auto-flushing. @@ -964,7 +964,7 @@ public class AllTests backgroundBatchOneway.opWithPayload(seq); ctl.resumeAdapter(); backgroundBatchOneway.ice_flushBatchRequests(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); // // Then try the same thing with async flush. @@ -976,7 +976,7 @@ public class AllTests backgroundBatchOneway.op(); ctl.resumeAdapter(); backgroundBatchOneway.begin_ice_flushBatchRequests(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); ctl.holdAdapter(); backgroundBatchOneway.opWithPayload(seq); @@ -986,7 +986,7 @@ public class AllTests ctl.resumeAdapter(); r = backgroundBatchOneway.begin_ice_flushBatchRequests(); backgroundBatchOneway.end_ice_flushBatchRequests(r); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } private static void @@ -1328,7 +1328,7 @@ public class AllTests } background.ice_ping(); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -1337,7 +1337,7 @@ public class AllTests { } - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(Ice.ConnectionClose.CloseForcefully); } thread1._destroy(); diff --git a/java-compat/test/src/main/java/test/Ice/binding/AllTests.java b/java-compat/test/src/main/java/test/Ice/binding/AllTests.java index 4aadfe0c3ff..062a5c87393 100644 --- a/java-compat/test/src/main/java/test/Ice/binding/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/binding/AllTests.java @@ -176,7 +176,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(test1.getAdapterName()); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -198,7 +198,7 @@ public class AllTests for(RemoteObjectAdapterPrx p : adapters) { - p.getTestIntf().ice_getConnection().close(false); + p.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -224,7 +224,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(test1.getAdapterName()); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -332,7 +332,7 @@ public class AllTests { try { - a.getTestIntf().ice_getConnection().close(false); + a.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } catch(Ice.LocalException ex) { @@ -374,7 +374,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(getAdapterNameWithAMI(test1)); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -396,7 +396,7 @@ public class AllTests for(RemoteObjectAdapterPrx p : adapters) { - p.getTestIntf().ice_getConnection().close(false); + p.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -422,7 +422,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(getAdapterNameWithAMI(test1)); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -455,7 +455,7 @@ public class AllTests while(!names.isEmpty()) { names.remove(test.getAdapterName()); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } test = TestIntfPrxHelper.uncheckedCast(test.ice_endpointSelection(Ice.EndpointSelectionType.Random)); @@ -467,7 +467,7 @@ public class AllTests while(!names.isEmpty()) { names.remove(test.getAdapterName()); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } deactivate(com, adapters); @@ -531,11 +531,11 @@ public class AllTests adapters.add(com.createObjectAdapter("Adapter36", endpoints[2].toString())); for(i = 0; i < nRetry && test.getAdapterName().equals("Adapter36"); i++); test(i == nRetry); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); adapters.add(com.createObjectAdapter("Adapter35", endpoints[1].toString())); for(i = 0; i < nRetry && test.getAdapterName().equals("Adapter35"); i++); test(i == nRetry); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); adapters.add(com.createObjectAdapter("Adapter34", endpoints[0].toString())); for(i = 0; i < nRetry && test.getAdapterName().equals("Adapter34"); i++); test(i == nRetry); @@ -832,7 +832,7 @@ public class AllTests for(i = 0; i < 5; i++) { test(test.getAdapterName().equals("Adapter82")); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } TestIntfPrx testSecure = TestIntfPrxHelper.uncheckedCast(test.ice_secure(true)); @@ -848,7 +848,7 @@ public class AllTests for(i = 0; i < 5; i++) { test(test.getAdapterName().equals("Adapter81")); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } com.createObjectAdapter("Adapter83", (test.ice_getEndpoints()[1]).toString()); // Reactive tcp OA. @@ -856,7 +856,7 @@ public class AllTests for(i = 0; i < 5; i++) { test(test.getAdapterName().equals("Adapter83")); - test.ice_getConnection().close(false); + test.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } com.deactivateObjectAdapter(adapters.get(0)); @@ -1051,7 +1051,7 @@ public class AllTests // Close the connection now to free a FD (it could be done after the sleep but // there could be race condiutation since the connection might not be closed // immediately due to threading). - test.ice_connectionId("0").ice_getConnection().close(false); + test.ice_connectionId("0").ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); // // The server closed the acceptor, wait one second and retry after freeing a FD. diff --git a/java-compat/test/src/main/java/test/Ice/hold/AllTests.java b/java-compat/test/src/main/java/test/Ice/hold/AllTests.java index b27fbaf331c..bd642b9882f 100644 --- a/java-compat/test/src/main/java/test/Ice/hold/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/hold/AllTests.java @@ -206,7 +206,7 @@ public class AllTests { result.waitForSent(); holdSerialized.ice_ping(); // Ensure everything's dispatched - holdSerialized.ice_getConnection().close(false); + holdSerialized.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } result.waitForCompleted(); diff --git a/java-compat/test/src/main/java/test/Ice/interrupt/AllTests.java b/java-compat/test/src/main/java/test/Ice/interrupt/AllTests.java index 8d954f051ec..ed523af5dbe 100644 --- a/java-compat/test/src/main/java/test/Ice/interrupt/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/interrupt/AllTests.java @@ -379,8 +379,7 @@ public class AllTests out.flush(); { final Thread mainThread = Thread.currentThread(); - - p.ice_getConnection().close(false); + p.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); AsyncResult r = p.begin_ice_getConnection(); mainThread.interrupt(); @@ -394,7 +393,7 @@ public class AllTests // Expected } - p.ice_getConnection().close(false); + p.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); final CallbackBase cb = new CallbackBase(); mainThread.interrupt(); diff --git a/java-compat/test/src/main/java/test/Ice/location/AllTests.java b/java-compat/test/src/main/java/test/Ice/location/AllTests.java index 8b6d3fb74ee..f8a3ab46d65 100644 --- a/java-compat/test/src/main/java/test/Ice/location/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/location/AllTests.java @@ -611,7 +611,7 @@ public class AllTests out.flush(); hello = HelloPrxHelper.checkedCast(communicator.stringToProxy("hello")); obj.migrateHello(); - hello.ice_getConnection().close(false); + hello.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); hello.sayHello(); obj.migrateHello(); hello.sayHello(); diff --git a/java-compat/test/src/main/java/test/Ice/metrics/AMDMetricsI.java b/java-compat/test/src/main/java/test/Ice/metrics/AMDMetricsI.java index 865db0c83ef..42bb7ee43d7 100644 --- a/java-compat/test/src/main/java/test/Ice/metrics/AMDMetricsI.java +++ b/java-compat/test/src/main/java/test/Ice/metrics/AMDMetricsI.java @@ -28,7 +28,7 @@ public final class AMDMetricsI extends _MetricsDisp public void fail_async(AMD_Metrics_fail cb, Ice.Current current) { - current.con.close(true); + current.con.close(Ice.ConnectionClose.CloseForcefully); cb.ice_response(); } diff --git a/java-compat/test/src/main/java/test/Ice/metrics/AllTests.java b/java-compat/test/src/main/java/test/Ice/metrics/AllTests.java index ea00e4fc8dd..52de5aef9c1 100644 --- a/java-compat/test/src/main/java/test/Ice/metrics/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/metrics/AllTests.java @@ -313,7 +313,7 @@ public class AllTests { if(proxy.ice_getCachedConnection() != null) { - proxy.ice_getCachedConnection().close(false); + proxy.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } try @@ -326,7 +326,7 @@ public class AllTests if(proxy.ice_getCachedConnection() != null) { - proxy.ice_getCachedConnection().close(false); + proxy.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -545,8 +545,8 @@ public class AllTests if(!collocated) { - metrics.ice_getConnection().close(false); - metrics.ice_connectionId("Con1").ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + metrics.ice_connectionId("Con1").ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -649,7 +649,7 @@ public class AllTests map = toMap(serverMetrics.getMetricsView("View", timestamp).get("Connection")); test(map.get("holding").current == 1); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); map = toMap(clientMetrics.getMetricsView("View", timestamp).get("Connection")); test(map.get("closing").current == 1); @@ -664,7 +664,7 @@ public class AllTests props.put("IceMX.Metrics.View.Map.Connection.GroupBy", "none"); updateProps(clientProps, serverProps, update, props, "Connection"); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); metrics.ice_timeout(500).ice_ping(); controller.hold(); @@ -726,7 +726,7 @@ public class AllTests testAttribute(clientMetrics, clientProps, update, "Connection", "mcastHost", "", out); testAttribute(clientMetrics, clientProps, update, "Connection", "mcastPort", "", out); - m.ice_getConnection().close(false); + m.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -746,7 +746,7 @@ public class AllTests IceMX.Metrics m1 = clientMetrics.getMetricsView("View", timestamp).get("ConnectionEstablishment")[0]; test(m1.current == 0 && m1.total == 1 && m1.id.equals(hostAndPort)); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); controller.hold(); try { @@ -806,7 +806,7 @@ public class AllTests m1 = clientMetrics.getMetricsView("View", timestamp).get("EndpointLookup")[0]; test(m1.current <= 1 && m1.total == 1 && m1.id.equals(prx.ice_getConnection().getEndpoint().toString())); - prx.ice_getConnection().close(false); + prx.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); boolean dnsException = false; try diff --git a/java-compat/test/src/main/java/test/Ice/metrics/MetricsI.java b/java-compat/test/src/main/java/test/Ice/metrics/MetricsI.java index a7cf0f3dccf..37a3a440351 100644 --- a/java-compat/test/src/main/java/test/Ice/metrics/MetricsI.java +++ b/java-compat/test/src/main/java/test/Ice/metrics/MetricsI.java @@ -27,7 +27,7 @@ public final class MetricsI extends _MetricsDisp public void fail(Ice.Current current) { - current.con.close(true); + current.con.close(Ice.ConnectionClose.CloseForcefully); } @Override diff --git a/java-compat/test/src/main/java/test/Ice/operations/BatchOneways.java b/java-compat/test/src/main/java/test/Ice/operations/BatchOneways.java index 75ed24a392e..d3240fc5a06 100644 --- a/java-compat/test/src/main/java/test/Ice/operations/BatchOneways.java +++ b/java-compat/test/src/main/java/test/Ice/operations/BatchOneways.java @@ -115,7 +115,7 @@ class BatchOneways batch1.ice_ping(); batch2.ice_ping(); batch1.ice_flushBatchRequests(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch1.ice_ping(); batch2.ice_ping(); @@ -123,7 +123,7 @@ class BatchOneways batch2.ice_getConnection(); batch1.ice_ping(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch1.ice_ping(); batch2.ice_ping(); } diff --git a/java-compat/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java b/java-compat/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java index 34d0cc00a43..985abb4e945 100644 --- a/java-compat/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java +++ b/java-compat/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java @@ -107,7 +107,7 @@ class BatchOnewaysAMI batch.begin_ice_ping(); batch2.begin_ice_ping(); batch.end_ice_flushBatchRequests(batch.begin_ice_flushBatchRequests()); - batch.ice_getConnection().close(false); + batch.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch.begin_ice_ping(); batch2.begin_ice_ping(); @@ -115,7 +115,7 @@ class BatchOnewaysAMI batch2.ice_getConnection(); batch.begin_ice_ping(); - batch.ice_getConnection().close(false); + batch.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); batch.begin_ice_ping().throwLocalException(); batch2.begin_ice_ping().throwLocalException(); } diff --git a/java-compat/test/src/main/java/test/Ice/retry/RetryI.java b/java-compat/test/src/main/java/test/Ice/retry/RetryI.java index 453b1ddd2c7..ab0d3ec70f7 100644 --- a/java-compat/test/src/main/java/test/Ice/retry/RetryI.java +++ b/java-compat/test/src/main/java/test/Ice/retry/RetryI.java @@ -25,7 +25,7 @@ public final class RetryI extends _RetryDisp { if(current.con != null) { - current.con.close(true); + current.con.close(Ice.ConnectionClose.CloseForcefully); } else { diff --git a/java-compat/test/src/main/java/test/Ice/timeout/AllTests.java b/java-compat/test/src/main/java/test/Ice/timeout/AllTests.java index 4c92ee8885b..28172f950c3 100644 --- a/java-compat/test/src/main/java/test/Ice/timeout/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/timeout/AllTests.java @@ -329,7 +329,7 @@ public class AllTests TimeoutPrx to = TimeoutPrxHelper.checkedCast(obj.ice_timeout(100 * mult)); Ice.Connection connection = to.ice_getConnection(); timeout.holdAdapter(500); - connection.close(false); + connection.close(Ice.ConnectionClose.CloseGracefullyAndWait); try { connection.getInfo(); // getInfo() doesn't throw in the closing state. @@ -350,9 +350,10 @@ public class AllTests connection.getInfo(); test(false); } - catch(Ice.CloseConnectionException ex) + catch(Ice.ConnectionManuallyClosedException ex) { // Expected. + test(ex.graceful); } timeout.op(); // Ensure adapter is active. } diff --git a/java-compat/test/src/main/java/test/Ice/udp/AllTests.java b/java-compat/test/src/main/java/test/Ice/udp/AllTests.java index 22e8c47e0ae..5d000d818d0 100644 --- a/java-compat/test/src/main/java/test/Ice/udp/AllTests.java +++ b/java-compat/test/src/main/java/test/Ice/udp/AllTests.java @@ -130,7 +130,7 @@ public class AllTests { test(seq.length > 16384); } - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); communicator.getProperties().setProperty("Ice.UDP.SndSize", "64000"); seq = new byte[50000]; try diff --git a/java-compat/test/src/main/java/test/IceSSL/configuration/AllTests.java b/java-compat/test/src/main/java/test/IceSSL/configuration/AllTests.java index 6e836a3f832..a13868fbeb8 100644 --- a/java-compat/test/src/main/java/test/IceSSL/configuration/AllTests.java +++ b/java-compat/test/src/main/java/test/IceSSL/configuration/AllTests.java @@ -891,7 +891,7 @@ public class AllTests // verifier.reset(); verifier.returnValue(false); - server.ice_getConnection().close(false); + server.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); try { server.ice_ping(); diff --git a/java/src/Ice/src/main/java/com/zeroc/Ice/ConnectionI.java b/java/src/Ice/src/main/java/com/zeroc/Ice/ConnectionI.java index ef086dbf88a..13b2d7a1dcc 100644 --- a/java/src/Ice/src/main/java/com/zeroc/Ice/ConnectionI.java +++ b/java/src/Ice/src/main/java/com/zeroc/Ice/ConnectionI.java @@ -165,25 +165,27 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } @Override - synchronized public void close(boolean force) + synchronized public void close(ConnectionClose mode) { if(Thread.interrupted()) { throw new OperationInterruptedException(); } - if(force) + if(mode == ConnectionClose.CloseForcefully) { - setState(StateClosed, new ForcedCloseConnectionException()); + setState(StateClosed, new ConnectionManuallyClosedException(false)); + } + else if(mode == ConnectionClose.CloseGracefully) + { + setState(StateClosing, new ConnectionManuallyClosedException(true)); } else { + assert(mode == ConnectionClose.CloseGracefullyAndWait); + // - // If we do a graceful shutdown, then we wait until all - // outstanding requests have been completed. Otherwise, - // the CloseConnectionException will cause all outstanding - // requests to be retried, regardless of whether the - // server has processed them or not. + // Wait until all outstanding requests have been completed. // while(!_asyncRequests.isEmpty()) { @@ -197,7 +199,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } } - setState(StateClosing, new CloseConnectionException()); + setState(StateClosing, new ConnectionManuallyClosedException(true)); } } @@ -289,14 +291,14 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler // We send a heartbeat if there was no activity in the last // (timeout / 4) period. Sending a heartbeat sooner than // really needed is safer to ensure that the receiver will - // receive in time the heartbeat. Sending the heartbeat if + // receive the heartbeat in time. Sending the heartbeat if // there was no activity in the last (timeout / 2) period // isn't enough since monitor() is called only every (timeout // / 2) period. // // Note that this doesn't imply that we are sending 4 // heartbeats per timeout period because the monitor() method - // is sill only called every (timeout / 2) period. + // is still only called every (timeout / 2) period. // if(acm.heartbeat == ACMHeartbeat.HeartbeatAlways || (acm.heartbeat != ACMHeartbeat.HeartbeatOff && _writeStream.isEmpty() && @@ -304,7 +306,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler { if(acm.heartbeat != ACMHeartbeat.HeartbeatOnInvocation || _dispatchCount > 0) { - heartbeat(); + sendHeartbeatNow(); } } @@ -480,6 +482,107 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } @Override + public void heartbeat() + { + ObjectPrx.waitForResponseForCompletion(heartbeatAsync()); + } + + private class HeartbeatAsync extends com.zeroc.IceInternal.OutgoingAsyncBaseI<Void> + { + public HeartbeatAsync(Communicator communicator, com.zeroc.IceInternal.Instance instance) + { + super(communicator, instance, "heartbeat"); + } + + @Override + public Connection getConnection() + { + return ConnectionI.this; + } + + @Override + protected void markSent() + { + super.markSent(); + + assert((_state & StateOK) != 0); + complete(null); + } + + @Override + protected void markCompleted() + { + if(_exception != null) + { + completeExceptionally(_exception); + } + super.markCompleted(); + } + + public void invoke() + { + try + { + _os.writeBlob(Protocol.magic); + ProtocolVersion.ice_write(_os, Protocol.currentProtocol); + EncodingVersion.ice_write(_os, Protocol.currentProtocolEncoding); + _os.writeByte(Protocol.validateConnectionMsg); + _os.writeByte((byte) 0); + _os.writeInt(Protocol.headerSize); // Message size. + + int status; + if(_instance.queueRequests()) + { + status = _instance.getQueueExecutor().execute(new Callable<Integer>() + { + @Override + public Integer call() + throws com.zeroc.IceInternal.RetryException + { + return ConnectionI.this.sendAsyncRequest(HeartbeatAsync.this, false, false, 0); + } + }); + } + else + { + status = ConnectionI.this.sendAsyncRequest(this, false, false, 0); + } + + if((status & AsyncStatus.Sent) > 0) + { + _sentSynchronously = true; + if((status & AsyncStatus.InvokeSentCallback) > 0) + { + invokeSent(); + } + } + } + catch(com.zeroc.IceInternal.RetryException ex) + { + if(completed(ex.get())) + { + invokeCompletedAsync(); + } + } + catch(com.zeroc.Ice.Exception ex) + { + if(completed(ex)) + { + invokeCompletedAsync(); + } + } + } + } + + @Override + public java.util.concurrent.CompletableFuture<Void> heartbeatAsync() + { + HeartbeatAsync __f = new HeartbeatAsync(_communicator, _instance); + __f.invoke(); + return __f; + } + + @Override synchronized public void setACM(java.util.OptionalInt timeout, java.util.Optional<ACMClose> close, java.util.Optional<ACMHeartbeat> heartbeat) { @@ -664,8 +767,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler public synchronized void invokeException(int requestId, LocalException ex, int invokeNum, boolean amd) { // - // Fatal exception while invoking a request. Since - // sendResponse/sendNoResponse isn't + // Fatal exception while invoking a request. Since sendResponse/sendNoResponse isn't // called in case of a fatal exception we decrement _dispatchCount here. // @@ -931,7 +1033,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } else { - assert (_state <= StateClosingPending); + assert(_state <= StateClosingPending); // // We parse messages first, if we receive a close @@ -1107,7 +1209,8 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler // if(info.invokeNum > 0) { - invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, info.adapter); + invokeAll(info.stream, info.invokeNum, info.requestId, info.compress, info.servantManager, + info.adapter); // // Don't increase dispatchedCount, the dispatch count is @@ -1264,7 +1367,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler // Trace the cause of unexpected connection closures // if(!(_exception instanceof CloseConnectionException || - _exception instanceof ForcedCloseConnectionException || + _exception instanceof ConnectionManuallyClosedException || _exception instanceof ConnectionTimeoutException || _exception instanceof CommunicatorDestroyedException || _exception instanceof ObjectAdapterDeactivatedException)) @@ -1614,7 +1717,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler // Don't warn about certain expected exceptions. // if(!(_exception instanceof CloseConnectionException || - _exception instanceof ForcedCloseConnectionException || + _exception instanceof ConnectionManuallyClosedException || _exception instanceof ConnectionTimeoutException || _exception instanceof CommunicatorDestroyedException || _exception instanceof ObjectAdapterDeactivatedException || @@ -1804,7 +1907,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler if(_observer != null && state == StateClosed && _exception != null) { if(!(_exception instanceof CloseConnectionException || - _exception instanceof ForcedCloseConnectionException || + _exception instanceof ConnectionManuallyClosedException || _exception instanceof ConnectionTimeoutException || _exception instanceof CommunicatorDestroyedException || _exception instanceof ObjectAdapterDeactivatedException || @@ -1833,8 +1936,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler private void initiateShutdown() { - assert (_state == StateClosing); - assert (_dispatchCount == 0); + assert(_state == StateClosing && _dispatchCount == 0); if(_shutdownInitiated) { @@ -1861,8 +1963,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler setState(StateClosingPending); // - // Notify the the transceiver of the graceful connection - // closure. + // Notify the transceiver of the graceful connection closure. // int op = _transceiver.closing(true, _exception); if(op != 0) @@ -1874,7 +1975,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } } - private void heartbeat() + private void sendHeartbeatNow() { assert (_state == StateActive); @@ -2071,8 +2172,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } else if(_state == StateClosingPending && _writeStream.pos() == 0) { - // Message wasn't sent, empty the _writeStream, we're not going to - // send more data. + // Message wasn't sent, empty the _writeStream, we're not going to send more data. OutgoingMessage message = _sendStreams.getFirst(); _writeStream.swap(message.stream); return SocketOperation.None; @@ -2149,9 +2249,8 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler } // - // If all the messages were sent and we are in the closing state, we - // schedule the close timeout to wait for the peer to close the - // connection. + // If all the messages were sent and we are in the closing state, we schedule + // the close timeout to wait for the peer to close the connection. // if(_state == StateClosing && _shutdownInitiated) { @@ -2376,8 +2475,7 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler setState(StateClosingPending, new CloseConnectionException()); // - // Notify the the transceiver of the graceful connection - // closure. + // Notify the transceiver of the graceful connection closure. // int op = _transceiver.closing(false, _exception); if(op != 0) @@ -2393,9 +2491,8 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler { if(_state >= StateClosing) { - TraceUtil.trace("received request during closing\n" - + "(ignored by server, client will retry)", info.stream, _logger, - _traceLevels); + TraceUtil.trace("received request during closing\n(ignored by server, client will retry)", + info.stream, _logger, _traceLevels); } else { @@ -2413,9 +2510,8 @@ public final class ConnectionI extends com.zeroc.IceInternal.EventHandler { if(_state >= StateClosing) { - TraceUtil.trace("received batch request during closing\n" - + "(ignored by server, client will retry)", info.stream, _logger, - _traceLevels); + TraceUtil.trace("received batch request during closing\n(ignored by server, client will retry)", + info.stream, _logger, _traceLevels); } else { diff --git a/java/src/Ice/src/main/java/com/zeroc/IceInternal/IncomingConnectionFactory.java b/java/src/Ice/src/main/java/com/zeroc/IceInternal/IncomingConnectionFactory.java index 337e56f05f7..98dd813fb66 100644 --- a/java/src/Ice/src/main/java/com/zeroc/IceInternal/IncomingConnectionFactory.java +++ b/java/src/Ice/src/main/java/com/zeroc/IceInternal/IncomingConnectionFactory.java @@ -137,7 +137,7 @@ public final class IncomingConnectionFactory extends EventHandler implements Con // for(ConnectionI c : connections) { - c.close(true); + c.close(com.zeroc.Ice.ConnectionClose.CloseForcefully); } throw e; } diff --git a/java/src/Ice/src/main/java/com/zeroc/IceInternal/OutgoingConnectionFactory.java b/java/src/Ice/src/main/java/com/zeroc/IceInternal/OutgoingConnectionFactory.java index 3e62623a17f..9bd778ecfb4 100644 --- a/java/src/Ice/src/main/java/com/zeroc/IceInternal/OutgoingConnectionFactory.java +++ b/java/src/Ice/src/main/java/com/zeroc/IceInternal/OutgoingConnectionFactory.java @@ -138,7 +138,7 @@ public final class OutgoingConnectionFactory { for(ConnectionI c : l) { - c.close(true); + c.close(com.zeroc.Ice.ConnectionClose.CloseForcefully); } } throw new com.zeroc.Ice.OperationInterruptedException(); diff --git a/java/test/src/main/java/test/Ice/acm/AllTests.java b/java/test/src/main/java/test/Ice/acm/AllTests.java index 8208748c069..97820ba81e6 100644 --- a/java/test/src/main/java/test/Ice/acm/AllTests.java +++ b/java/test/src/main/java/test/Ice/acm/AllTests.java @@ -568,6 +568,31 @@ public class AllTests } } + static class HeartbeatManualTest extends TestCase + { + public HeartbeatManualTest(Application app, RemoteCommunicatorPrx com, java.io.PrintWriter out) + { + super(app, "manual heartbeats", com, out); + // + // Disable heartbeats. + // + setClientACM(10, -1, 0); + setServerACM(10, -1, 0); + } + + public void runTestCase(RemoteObjectAdapterPrx adapter, TestIntfPrx proxy) + { + proxy.startHeartbeatCount(); + com.zeroc.Ice.Connection con = proxy.ice_getConnection(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + con.heartbeat(); + proxy.waitForHeartbeatCount(5); + } + } + static class SetACMTest extends TestCase { public SetACMTest(Application app, RemoteCommunicatorPrx com, java.io.PrintWriter out) @@ -600,7 +625,8 @@ public class AllTests test(acm.heartbeat == ACMHeartbeat.HeartbeatAlways); // Make sure the client sends few heartbeats to the server - proxy.waitForHeartbeat(2); + proxy.startHeartbeatCount(); + proxy.waitForHeartbeatCount(2); } } @@ -627,6 +653,7 @@ public class AllTests tests.add(new HeartbeatOnIdleTest(app, com, out)); tests.add(new HeartbeatAlwaysTest(app, com, out)); + tests.add(new HeartbeatManualTest(app, com, out)); tests.add(new SetACMTest(app, com, out)); for(TestCase test : tests) diff --git a/java/test/src/main/java/test/Ice/acm/Test.ice b/java/test/src/main/java/test/Ice/acm/Test.ice index 117192a2df8..7f71427c741 100644 --- a/java/test/src/main/java/test/Ice/acm/Test.ice +++ b/java/test/src/main/java/test/Ice/acm/Test.ice @@ -18,7 +18,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/java/test/src/main/java/test/Ice/acm/TestI.java b/java/test/src/main/java/test/Ice/acm/TestI.java index 7b9c369e4f3..43e6fb38d0d 100644 --- a/java/test/src/main/java/test/Ice/acm/TestI.java +++ b/java/test/src/main/java/test/Ice/acm/TestI.java @@ -50,35 +50,29 @@ public class TestI implements TestIntf } } - static class Counter - { - Counter(int v) - { - value = v; - } - - int value; - } - - public void waitForHeartbeat(int count, com.zeroc.Ice.Current current) + public void startHeartbeatCount(com.zeroc.Ice.Current current) { - final Counter c = new Counter(count); + _counter = new Counter(); current.con.setHeartbeatCallback(con -> { - synchronized(c) + synchronized(_counter) { - --c.value; - c.notifyAll(); + ++_counter.value; + _counter.notifyAll(); } }); + } - synchronized(c) + public void waitForHeartbeatCount(int count, com.zeroc.Ice.Current current) + { + assert(_counter != null); + synchronized(_counter) { - while(c.value > 0) + while(_counter.value < count) { try { - c.wait(); + _counter.wait(); } catch(InterruptedException ex) { @@ -86,4 +80,11 @@ public class TestI implements TestIntf } } } + + static class Counter + { + int value; + } + + private Counter _counter; } diff --git a/java/test/src/main/java/test/Ice/ami/AMI.java b/java/test/src/main/java/test/Ice/ami/AMI.java index 85a543317a4..0c6b125897a 100644 --- a/java/test/src/main/java/test/Ice/ami/AMI.java +++ b/java/test/src/main/java/test/Ice/ami/AMI.java @@ -17,6 +17,7 @@ import java.util.concurrent.CompletionException; import com.zeroc.Ice.InvocationFuture; import com.zeroc.Ice.Util; +import test.Ice.ami.Test.CloseMode; import test.Ice.ami.Test.TestIntfPrx; import test.Ice.ami.Test.TestIntfControllerPrx; import test.Ice.ami.Test.TestIntfException; @@ -347,7 +348,7 @@ public class AMI test(p.opBatchCount() == 0); TestIntfPrx b1 = p.ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); CompletableFuture<Void> r = b1.ice_flushBatchRequestsAsync(); Util.getInvocationFuture(r).whenSent((sentSynchronously, ex) -> { @@ -395,7 +396,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity())). ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); CompletableFuture<Void> r = b1.ice_getConnection().flushBatchRequestsAsync(); Util.getInvocationFuture(r).whenSent((sentSynchronously, ex) -> { @@ -444,7 +445,7 @@ public class AMI TestIntfPrx b1 = TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity())). ice_batchOneway(); b1.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); CompletableFuture<Void> r = communicator.flushBatchRequestsAsync(); Util.getInvocationFuture(r).whenSent((sentSynchronously, ex) -> { @@ -500,7 +501,7 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); + b1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); CompletableFuture<Void> r = communicator.flushBatchRequestsAsync(); Util.getInvocationFuture(r).whenSent((sentSynchronously, ex) -> { @@ -528,8 +529,8 @@ public class AMI b2.ice_getConnection(); // Ensure connection is established. b1.opBatch(); b2.opBatch(); - b1.ice_getConnection().close(false); - b2.ice_getConnection().close(false); + b1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); CompletableFuture<Void> r = communicator.flushBatchRequestsAsync(); Util.getInvocationFuture(r).whenSent((sentSynchronously, ex) -> { @@ -756,9 +757,35 @@ public class AMI if(p.ice_getConnection() != null) { - out.print("testing close connection with sending queue... "); + out.print("testing graceful close connection with wait... "); out.flush(); { + // + // Local case: begin several requests, close the connection gracefully, and make sure it waits + // for the requests to complete. + // + java.util.List<CompletableFuture<Void>> results = new java.util.ArrayList<>(); + for(int i = 0; i < 3; ++i) + { + results.add(p.sleepAsync(50)); + } + p.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); + for(CompletableFuture<Void> f : results) + { + try + { + f.join(); + } + catch(Throwable ex) + { + test(false); + } + } + } + { + // + // Remote case. + // byte[] seq = new byte[1024 * 10]; // @@ -777,7 +804,7 @@ public class AMI { results.add(Util.getInvocationFuture(p.opWithPayloadAsync(seq))); } - if(!Util.getInvocationFuture(p.closeAsync(false)).isSent()) + if(!Util.getInvocationFuture(p.closeAsync(CloseMode.CloseGracefullyAndWait)).isSent()) { for(int i = 0; i < maxQueue; i++) { @@ -803,6 +830,98 @@ public class AMI } } out.println("ok"); + + out.print("testing graceful close connection without wait... "); + out.flush(); + { + // + // Local case: start a lengthy operation and then close the connection gracefully on the client side + // without waiting for the pending invocation to complete. There will be no retry and we expect the + // invocation to fail with ConnectionManuallyClosedException. + // + // This test requires two threads in the server's thread pool: one will block in sleep() and the other + // will process the CloseConnection message. + // + p.ice_ping(); + com.zeroc.Ice.Connection con = p.ice_getConnection(); + CompletableFuture<Void> f = p.sleepAsync(100); + con.close(com.zeroc.Ice.ConnectionClose.CloseGracefully); + try + { + f.join(); + test(false); + } + catch(CompletionException ex) + { + test(ex.getCause() instanceof com.zeroc.Ice.ConnectionManuallyClosedException); + test(((com.zeroc.Ice.ConnectionManuallyClosedException)ex.getCause()).graceful); + } + catch(Throwable ex) + { + test(false); + } + + // + // Remote case: the server closes the connection gracefully. Our call to TestIntf::close() + // completes successfully and then the connection should be closed immediately afterward, + // despite the fact that there's a pending call to sleep(). The call to sleep() should be + // automatically retried and complete successfully. + // + p.ice_ping(); + con = p.ice_getConnection(); + Callback cb = new Callback(); + con.setCloseCallback(c -> cb.called()); + f = p.sleepAsync(100); + p.close(CloseMode.CloseGracefully); + cb.check(); + f.join(); + p.ice_ping(); + test(p.ice_getConnection() != con); + } + out.println("ok"); + + out.print("testing forceful close connection... "); + out.flush(); + { + // + // Local case: start a lengthy operation and then close the connection forcefully on the client side. + // There will be no retry and we expect the invocation to fail with ConnectionManuallyClosedException. + // + p.ice_ping(); + com.zeroc.Ice.Connection con = p.ice_getConnection(); + CompletableFuture<Void> f = p.sleepAsync(100); + con.close(com.zeroc.Ice.ConnectionClose.CloseForcefully); + try + { + f.join(); + test(false); + } + catch(CompletionException ex) + { + test(ex.getCause() instanceof com.zeroc.Ice.ConnectionManuallyClosedException); + test(!((com.zeroc.Ice.ConnectionManuallyClosedException)ex.getCause()).graceful); + } + catch(Throwable ex) + { + test(false); + } + + // + // Remote case: the server closes the connection forcefully. This causes the request to fail + // with a ConnectionLostException. Since the close() operation is not idempotent, the client + // will not retry. + // + try + { + p.close(CloseMode.CloseForcefully); + test(false); + } + catch(com.zeroc.Ice.ConnectionLostException ex) + { + // Expected. + } + } + out.println("ok"); } } } diff --git a/java/test/src/main/java/test/Ice/ami/Client.java b/java/test/src/main/java/test/Ice/ami/Client.java index f0992f5bece..50fb9fdddee 100644 --- a/java/test/src/main/java/test/Ice/ami/Client.java +++ b/java/test/src/main/java/test/Ice/ami/Client.java @@ -24,6 +24,7 @@ public class Client extends test.Util.Application GetInitDataResult r = super.getInitData(args); r.initData.properties.setProperty("Ice.Package.Test", "test.Ice.ami"); r.initData.properties.setProperty("Ice.Warn.AMICallback", "0"); + r.initData.properties.setProperty("Ice.Warn.Connections", "0"); // // Limit the send buffer size, this test relies on the socket diff --git a/java/test/src/main/java/test/Ice/ami/Server.java b/java/test/src/main/java/test/Ice/ami/Server.java index 90a41a5eaf5..c244441f2e5 100644 --- a/java/test/src/main/java/test/Ice/ami/Server.java +++ b/java/test/src/main/java/test/Ice/ami/Server.java @@ -33,6 +33,12 @@ public class Server extends test.Util.Application r.initData.properties.setProperty("TestAdapter.Endpoints", getTestEndpoint(r.initData.properties, 0)); r.initData.properties.setProperty("ControllerAdapter.Endpoints", getTestEndpoint(r.initData.properties, 1)); r.initData.properties.setProperty("ControllerAdapter.ThreadPool.Size", "1"); + + // + // This test kills connections, so we don't want warnings. + // + r.initData.properties.setProperty("Ice.Warn.Connections", "0"); + // // Limit the recv buffer size, this test relies on the socket // send() blocking after sending a given amount of data. diff --git a/java/test/src/main/java/test/Ice/ami/Test.ice b/java/test/src/main/java/test/Ice/ami/Test.ice index 82b3fe6d383..bbc4d908d65 100644 --- a/java/test/src/main/java/test/Ice/ami/Test.ice +++ b/java/test/src/main/java/test/Ice/ami/Test.ice @@ -20,6 +20,13 @@ exception TestIntfException { }; +enum CloseMode +{ + CloseForcefully, + CloseGracefully, + CloseGracefullyAndWait +}; + interface TestIntf { void op(); @@ -30,7 +37,8 @@ interface TestIntf void opBatch(); int opBatchCount(); bool waitForBatch(int count); - void close(bool force); + void close(CloseMode mode); + void sleep(int ms); void shutdown(); bool supportsFunctionalTests(); diff --git a/java/test/src/main/java/test/Ice/ami/TestI.java b/java/test/src/main/java/test/Ice/ami/TestI.java index 0153f138dd0..ee7f4eef6f9 100644 --- a/java/test/src/main/java/test/Ice/ami/TestI.java +++ b/java/test/src/main/java/test/Ice/ami/TestI.java @@ -9,6 +9,7 @@ package test.Ice.ami; +import test.Ice.ami.Test.CloseMode; import test.Ice.ami.Test.TestIntf; import test.Ice.ami.Test.TestIntfException; @@ -123,9 +124,22 @@ public class TestI implements TestIntf @Override public void - close(boolean force, com.zeroc.Ice.Current current) + close(CloseMode mode, com.zeroc.Ice.Current current) { - current.con.close(force); + current.con.close(com.zeroc.Ice.ConnectionClose.valueOf(mode.value())); + } + + @Override + public void + sleep(int ms, com.zeroc.Ice.Current current) + { + try + { + Thread.sleep(ms); + } + catch(InterruptedException ex) + { + } } @Override diff --git a/java/test/src/main/java/test/Ice/background/AllTests.java b/java/test/src/main/java/test/Ice/background/AllTests.java index ca465338b01..b174df088ed 100644 --- a/java/test/src/main/java/test/Ice/background/AllTests.java +++ b/java/test/src/main/java/test/Ice/background/AllTests.java @@ -295,7 +295,7 @@ public class AllTests configuration.buffered(true); backgroundController.buffered(true); background.opAsync(); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); background.opAsync(); java.util.List<CompletableFuture<Void>> results = new java.util.ArrayList<>(); @@ -334,7 +334,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); for(int i = 0; i < 4; ++i) { @@ -396,7 +396,7 @@ public class AllTests } configuration.connectException(new com.zeroc.Ice.SocketException()); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -439,7 +439,7 @@ public class AllTests ex.printStackTrace(); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); for(int i = 0; i < 4; i++) { @@ -498,7 +498,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -510,7 +510,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -554,7 +554,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -589,7 +589,7 @@ public class AllTests } configuration.initializeException(new com.zeroc.Ice.SocketException()); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -615,7 +615,7 @@ public class AllTests } configuration.initializeSocketStatus(com.zeroc.IceInternal.SocketOperation.Write); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); try { @@ -630,7 +630,7 @@ public class AllTests configuration.initializeSocketStatus(com.zeroc.IceInternal.SocketOperation.None); ctl.initializeException(true); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -658,7 +658,7 @@ public class AllTests try { ctl.initializeSocketStatus(com.zeroc.IceInternal.SocketOperation.Write); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); background.op(); ctl.initializeSocketStatus(com.zeroc.IceInternal.SocketOperation.None); } @@ -693,7 +693,7 @@ public class AllTests { test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -741,7 +741,7 @@ public class AllTests ex.printStackTrace(); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -815,7 +815,7 @@ public class AllTests ex.printStackTrace(); test(false); } - background.ice_getConnection().close(false); + background.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { @@ -845,7 +845,7 @@ public class AllTests backgroundBatchOneway.op(); ctl.resumeAdapter(); backgroundBatchOneway.ice_flushBatchRequests(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); // // Send bigger requests to test with auto-flushing. @@ -857,7 +857,7 @@ public class AllTests backgroundBatchOneway.opWithPayload(seq); ctl.resumeAdapter(); backgroundBatchOneway.ice_flushBatchRequests(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); // // Then try the same thing with async flush. @@ -869,7 +869,7 @@ public class AllTests backgroundBatchOneway.op(); ctl.resumeAdapter(); backgroundBatchOneway.ice_flushBatchRequestsAsync(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); ctl.holdAdapter(); backgroundBatchOneway.opWithPayload(seq); @@ -879,7 +879,7 @@ public class AllTests ctl.resumeAdapter(); r = backgroundBatchOneway.ice_flushBatchRequestsAsync(); r.join(); - backgroundBatchOneway.ice_getConnection().close(false); + backgroundBatchOneway.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } private static void readWriteTests(Configuration configuration, BackgroundPrx background, @@ -1274,7 +1274,7 @@ public class AllTests } background.ice_ping(); - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); try { Thread.sleep(10); @@ -1283,7 +1283,7 @@ public class AllTests { } - background.ice_getCachedConnection().close(true); + background.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseForcefully); } thread1._destroy(); diff --git a/java/test/src/main/java/test/Ice/binding/AllTests.java b/java/test/src/main/java/test/Ice/binding/AllTests.java index e464b71d8af..068307d7c96 100644 --- a/java/test/src/main/java/test/Ice/binding/AllTests.java +++ b/java/test/src/main/java/test/Ice/binding/AllTests.java @@ -16,6 +16,7 @@ import test.Ice.binding.Test.RemoteObjectAdapterPrx; import test.Ice.binding.Test.TestIntfPrx; import test.Util.Application; +import com.zeroc.Ice.ConnectionClose; import com.zeroc.Ice.Endpoint; import com.zeroc.Ice.EndpointSelectionType; @@ -132,7 +133,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(test1.getAdapterName()); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -154,7 +155,7 @@ public class AllTests for(RemoteObjectAdapterPrx p : adapters) { - p.getTestIntf().ice_getConnection().close(false); + p.getTestIntf().ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -179,7 +180,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(test1.getAdapterName()); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -287,7 +288,7 @@ public class AllTests { try { - a.getTestIntf().ice_getConnection().close(false); + a.getTestIntf().ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } catch(com.zeroc.Ice.LocalException ex) { @@ -328,7 +329,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(getAdapterNameWithAMI(test1)); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -350,7 +351,7 @@ public class AllTests for(RemoteObjectAdapterPrx p : adapters) { - p.getTestIntf().ice_getConnection().close(false); + p.getTestIntf().ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -375,7 +376,7 @@ public class AllTests test(test2.ice_getConnection() == test3.ice_getConnection()); names.remove(getAdapterNameWithAMI(test1)); - test1.ice_getConnection().close(false); + test1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } // @@ -408,7 +409,7 @@ public class AllTests while(!names.isEmpty()) { names.remove(test.getAdapterName()); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } test = test.ice_endpointSelection(EndpointSelectionType.Random); @@ -420,7 +421,7 @@ public class AllTests while(!names.isEmpty()) { names.remove(test.getAdapterName()); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } deactivate(rcom, adapters); @@ -484,11 +485,11 @@ public class AllTests adapters.add(rcom.createObjectAdapter("Adapter36", endpoints[2].toString())); for(i = 0; i < nRetry && test.getAdapterName().equals("Adapter36"); i++); test(i == nRetry); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); adapters.add(rcom.createObjectAdapter("Adapter35", endpoints[1].toString())); for(i = 0; i < nRetry && test.getAdapterName().equals("Adapter35"); i++); test(i == nRetry); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); adapters.add(rcom.createObjectAdapter("Adapter34", endpoints[0].toString())); for(i = 0; i < nRetry && test.getAdapterName().equals("Adapter34"); i++); test(i == nRetry); @@ -785,7 +786,7 @@ public class AllTests for(i = 0; i < 5; i++) { test(test.getAdapterName().equals("Adapter82")); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } TestIntfPrx testSecure = test.ice_secure(true); @@ -801,7 +802,7 @@ public class AllTests for(i = 0; i < 5; i++) { test(test.getAdapterName().equals("Adapter81")); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } rcom.createObjectAdapter("Adapter83", (test.ice_getEndpoints()[1]).toString()); // Reactive tcp OA. @@ -809,7 +810,7 @@ public class AllTests for(i = 0; i < 5; i++) { test(test.getAdapterName().equals("Adapter83")); - test.ice_getConnection().close(false); + test.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } rcom.deactivateObjectAdapter(adapters.get(0)); @@ -1004,7 +1005,7 @@ public class AllTests // Close the connection now to free a FD (it could be done after the sleep but // there could be race condiutation since the connection might not be closed // immediately due to threading). - test.ice_connectionId("0").ice_getConnection().close(false); + test.ice_connectionId("0").ice_getConnection().close(ConnectionClose.CloseGracefullyAndWait); // // The server closed the acceptor, wait one second and retry after freeing a FD. diff --git a/java/test/src/main/java/test/Ice/hold/AllTests.java b/java/test/src/main/java/test/Ice/hold/AllTests.java index 31db0c827da..36974038091 100644 --- a/java/test/src/main/java/test/Ice/hold/AllTests.java +++ b/java/test/src/main/java/test/Ice/hold/AllTests.java @@ -205,7 +205,7 @@ public class AllTests { f.waitForSent(); holdSerialized.ice_ping(); // Ensure everything's dispatched - holdSerialized.ice_getConnection().close(false); + holdSerialized.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } } r.join(); diff --git a/java/test/src/main/java/test/Ice/interrupt/AllTests.java b/java/test/src/main/java/test/Ice/interrupt/AllTests.java index 233a595197d..62a1d6034e1 100644 --- a/java/test/src/main/java/test/Ice/interrupt/AllTests.java +++ b/java/test/src/main/java/test/Ice/interrupt/AllTests.java @@ -336,7 +336,7 @@ public class AllTests { final Thread mainThread = Thread.currentThread(); - p.ice_getConnection().close(false); + p.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); CompletableFuture<com.zeroc.Ice.Connection> r = p.ice_getConnectionAsync(); mainThread.interrupt(); @@ -358,7 +358,7 @@ public class AllTests // Expected } - p.ice_getConnection().close(false); + p.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); final Callback cb = new Callback(); mainThread.interrupt(); diff --git a/java/test/src/main/java/test/Ice/location/AllTests.java b/java/test/src/main/java/test/Ice/location/AllTests.java index 60bcd506d8f..2b6fb9aa789 100644 --- a/java/test/src/main/java/test/Ice/location/AllTests.java +++ b/java/test/src/main/java/test/Ice/location/AllTests.java @@ -592,7 +592,7 @@ public class AllTests out.flush(); hello = HelloPrx.checkedCast(communicator.stringToProxy("hello")); obj.migrateHello(); - hello.ice_getConnection().close(false); + hello.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); hello.sayHello(); obj.migrateHello(); hello.sayHello(); diff --git a/java/test/src/main/java/test/Ice/metrics/AMDMetricsI.java b/java/test/src/main/java/test/Ice/metrics/AMDMetricsI.java index 916af54b6ae..c329b58a85b 100644 --- a/java/test/src/main/java/test/Ice/metrics/AMDMetricsI.java +++ b/java/test/src/main/java/test/Ice/metrics/AMDMetricsI.java @@ -29,7 +29,7 @@ public final class AMDMetricsI implements Metrics @Override public CompletionStage<Void> failAsync(com.zeroc.Ice.Current current) { - current.con.close(true); + current.con.close(com.zeroc.Ice.ConnectionClose.CloseForcefully); return CompletableFuture.completedFuture((Void)null); } diff --git a/java/test/src/main/java/test/Ice/metrics/AllTests.java b/java/test/src/main/java/test/Ice/metrics/AllTests.java index 051af2be8a2..5f7ada1e51f 100644 --- a/java/test/src/main/java/test/Ice/metrics/AllTests.java +++ b/java/test/src/main/java/test/Ice/metrics/AllTests.java @@ -260,7 +260,7 @@ public class AllTests { if(proxy.ice_getCachedConnection() != null) { - proxy.ice_getCachedConnection().close(false); + proxy.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } try @@ -273,7 +273,7 @@ public class AllTests if(proxy.ice_getCachedConnection() != null) { - proxy.ice_getCachedConnection().close(false); + proxy.ice_getCachedConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } } @@ -478,8 +478,9 @@ public class AllTests if(!collocated) { - metrics.ice_getConnection().close(false); - metrics.ice_connectionId("Con1").ice_getConnection().close(false); + metrics.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); + metrics.ice_connectionId("Con1").ice_getConnection().close( + com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -582,7 +583,7 @@ public class AllTests map = toMap(serverMetrics.getMetricsView("View").returnValue.get("Connection")); test(map.get("holding").current == 1); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); map = toMap(clientMetrics.getMetricsView("View").returnValue.get("Connection")); test(map.get("closing").current == 1); @@ -597,7 +598,7 @@ public class AllTests props.put("IceMX.Metrics.View.Map.Connection.GroupBy", "none"); updateProps(clientProps, serverProps, props, "Connection"); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); metrics.ice_timeout(500).ice_ping(); controller.hold(); @@ -659,7 +660,7 @@ public class AllTests testAttribute(clientMetrics, clientProps, "Connection", "mcastHost", "", out); testAttribute(clientMetrics, clientProps, "Connection", "mcastPort", "", out); - m.ice_getConnection().close(false); + m.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); waitForCurrent(clientMetrics, "View", "Connection", 0); waitForCurrent(serverMetrics, "View", "Connection", 0); @@ -680,7 +681,7 @@ public class AllTests clientMetrics.getMetricsView("View").returnValue.get("ConnectionEstablishment")[0]; test(m1.current == 0 && m1.total == 1 && m1.id.equals(hostAndPort)); - metrics.ice_getConnection().close(false); + metrics.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); controller.hold(); try { @@ -737,7 +738,7 @@ public class AllTests try { prx.ice_ping(); - prx.ice_getConnection().close(false); + prx.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); } catch(com.zeroc.Ice.LocalException ex) { diff --git a/java/test/src/main/java/test/Ice/metrics/MetricsI.java b/java/test/src/main/java/test/Ice/metrics/MetricsI.java index 8a8342431b1..7c9e1a527e5 100644 --- a/java/test/src/main/java/test/Ice/metrics/MetricsI.java +++ b/java/test/src/main/java/test/Ice/metrics/MetricsI.java @@ -25,7 +25,7 @@ public final class MetricsI implements Metrics @Override public void fail(com.zeroc.Ice.Current current) { - current.con.close(true); + current.con.close(com.zeroc.Ice.ConnectionClose.CloseForcefully); } @Override diff --git a/java/test/src/main/java/test/Ice/operations/BatchOneways.java b/java/test/src/main/java/test/Ice/operations/BatchOneways.java index d47af8c6bc2..30a5672e80d 100644 --- a/java/test/src/main/java/test/Ice/operations/BatchOneways.java +++ b/java/test/src/main/java/test/Ice/operations/BatchOneways.java @@ -109,7 +109,7 @@ class BatchOneways batch1.ice_ping(); batch2.ice_ping(); batch1.ice_flushBatchRequests(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); batch1.ice_ping(); batch2.ice_ping(); @@ -117,7 +117,7 @@ class BatchOneways batch2.ice_getConnection(); batch1.ice_ping(); - batch1.ice_getConnection().close(false); + batch1.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); batch1.ice_ping(); batch2.ice_ping(); } diff --git a/java/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java b/java/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java index c759e3dc6e9..1093418d3b1 100644 --- a/java/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java +++ b/java/test/src/main/java/test/Ice/operations/BatchOnewaysAMI.java @@ -100,7 +100,7 @@ class BatchOnewaysAMI batch.ice_pingAsync(); batch2.ice_pingAsync(); batch.ice_flushBatchRequestsAsync().join(); - batch.ice_getConnection().close(false); + batch.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); batch.ice_pingAsync(); batch2.ice_pingAsync(); @@ -108,7 +108,7 @@ class BatchOnewaysAMI batch2.ice_getConnection(); batch.ice_pingAsync(); - batch.ice_getConnection().close(false); + batch.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); test(!batch.ice_pingAsync().isCompletedExceptionally()); test(!batch2.ice_pingAsync().isCompletedExceptionally()); } diff --git a/java/test/src/main/java/test/Ice/retry/RetryI.java b/java/test/src/main/java/test/Ice/retry/RetryI.java index 827365878ee..23d09ead06e 100644 --- a/java/test/src/main/java/test/Ice/retry/RetryI.java +++ b/java/test/src/main/java/test/Ice/retry/RetryI.java @@ -24,7 +24,7 @@ public final class RetryI implements Retry { if(current.con != null) { - current.con.close(true); + current.con.close(com.zeroc.Ice.ConnectionClose.CloseForcefully); } else { diff --git a/java/test/src/main/java/test/Ice/timeout/AllTests.java b/java/test/src/main/java/test/Ice/timeout/AllTests.java index 0ba6091a244..1b8172762f9 100644 --- a/java/test/src/main/java/test/Ice/timeout/AllTests.java +++ b/java/test/src/main/java/test/Ice/timeout/AllTests.java @@ -262,7 +262,7 @@ public class AllTests TimeoutPrx to = TimeoutPrx.checkedCast(obj.ice_timeout(100 * mult)); com.zeroc.Ice.Connection connection = to.ice_getConnection(); timeout.holdAdapter(500); - connection.close(false); + connection.close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { connection.getInfo(); // getInfo() doesn't throw in the closing state. @@ -283,9 +283,10 @@ public class AllTests connection.getInfo(); test(false); } - catch(com.zeroc.Ice.CloseConnectionException ex) + catch(com.zeroc.Ice.ConnectionManuallyClosedException ex) { // Expected. + test(ex.graceful); } timeout.op(); // Ensure adapter is active. } diff --git a/java/test/src/main/java/test/Ice/udp/AllTests.java b/java/test/src/main/java/test/Ice/udp/AllTests.java index 302f9849122..8fddc3c1388 100644 --- a/java/test/src/main/java/test/Ice/udp/AllTests.java +++ b/java/test/src/main/java/test/Ice/udp/AllTests.java @@ -124,7 +124,7 @@ public class AllTests { test(seq.length > 16384); } - obj.ice_getConnection().close(false); + obj.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); communicator.getProperties().setProperty("Ice.UDP.SndSize", "64000"); seq = new byte[50000]; try diff --git a/java/test/src/main/java/test/IceSSL/configuration/AllTests.java b/java/test/src/main/java/test/IceSSL/configuration/AllTests.java index 6c21b6c5e08..1ece2537cac 100644 --- a/java/test/src/main/java/test/IceSSL/configuration/AllTests.java +++ b/java/test/src/main/java/test/IceSSL/configuration/AllTests.java @@ -889,7 +889,7 @@ public class AllTests // verifier.reset(); verifier.returnValue(false); - server.ice_getConnection().close(false); + server.ice_getConnection().close(com.zeroc.Ice.ConnectionClose.CloseGracefullyAndWait); try { server.ice_ping(); diff --git a/js/src/Ice/ConnectionI.js b/js/src/Ice/ConnectionI.js index c56e764a01e..4541098ae78 100644 --- a/js/src/Ice/ConnectionI.js +++ b/js/src/Ice/ConnectionI.js @@ -35,6 +35,7 @@ const InputStream = Ice.InputStream; const OutputStream = Ice.OutputStream; const BatchRequestQueue = Ice.BatchRequestQueue; const ConnectionFlushBatch = Ice.ConnectionFlushBatch; +const HeartbeatAsync = Ice.HeartbeatAsync; const Debug = Ice.Debug; const ExUtil = Ice.ExUtil; const HashMap = Ice.HashMap; @@ -49,6 +50,7 @@ const EncodingVersion = Ice.EncodingVersion; const ACM = Ice.ACM; const ACMClose = Ice.ACMClose; const ACMHeartbeat = Ice.ACMHeartbeat; +const ConnectionClose = Ice.ConnectionClose; const StateNotInitialized = 0; const StateNotValidated = 1; @@ -218,23 +220,26 @@ class ConnectionI } } - close(force) + close(mode) { const r = new AsyncResultBase(this._communicator, "close", this, null, null); - if(force) + if(mode == ConnectionClose.CloseForcefully) { - this.setState(StateClosed, new Ice.ForcedCloseConnectionException()); + this.setState(StateClosed, new Ice.ConnectionManuallyClosedException(false)); + r.resolve(); + } + else if(mode == ConnectionClose.CloseGracefully) + { + this.setState(StateClosing, new Ice.ConnectionManuallyClosedException(true)); r.resolve(); } else { + Debug.assert(mode == ConnectionClose.CloseGracefullyAndWait); + // - // If we do a graceful shutdown, then we wait until all - // outstanding requests have been completed. Otherwise, - // the CloseConnectionException will cause all outstanding - // requests to be retried, regardless of whether the - // server has processed them or not. + // Wait until all outstanding requests have been completed. // this._closePromises.push(r); this.checkClose(); @@ -246,13 +251,13 @@ class ConnectionI checkClose() { // - // If close(false) has been called, then we need to check if all + // If close(CloseGracefullyAndWait) has been called, then we need to check if all // requests have completed and we can transition to StateClosing. // We also complete outstanding promises. // if(this._asyncRequests.size === 0 && this._closePromises.length > 0) { - this.setState(StateClosing, new Ice.CloseConnectionException()); + this.setState(StateClosing, new Ice.ConnectionManuallyClosedException(true)); this._closePromises.forEach(p => p.resolve()); this._closePromises = []; } @@ -310,13 +315,13 @@ class ConnectionI // We send a heartbeat if there was no activity in the last // (timeout / 4) period. Sending a heartbeat sooner than // really needed is safer to ensure that the receiver will - // receive in time the heartbeat. Sending the heartbeat if + // receive the heartbeat in time. Sending the heartbeat if // there was no activity in the last (timeout / 2) period // isn't enough since monitor() is called only every (timeout // / 2) period. // // Note that this doesn't imply that we are sending 4 heartbeats - // per timeout period because the monitor() method is sill only + // per timeout period because the monitor() method is still only // called every (timeout / 2) period. // if(acm.heartbeat == Ice.ACMHeartbeat.HeartbeatAlways || @@ -325,7 +330,7 @@ class ConnectionI { if(acm.heartbeat != Ice.ACMHeartbeat.HeartbeatOnInvocation || this._dispatchCount > 0) { - this.heartbeat(); // Send heartbeat if idle in the last timeout / 2 period. + this.sendHeartbeatNow(); // Send heartbeat if idle in the last timeout / 2 period. } } @@ -488,6 +493,13 @@ class ConnectionI this._heartbeatCallback = callback; } + heartbeat() + { + const result = new HeartbeatAsync(this, this._communicator); + result.invoke(); + return result; + } + setACM(timeout, close, heartbeat) { if(this._monitor === null || this._state >= StateClosed) @@ -1008,7 +1020,7 @@ class ConnectionI // Trace the cause of unexpected connection closures // if(!(this._exception instanceof Ice.CloseConnectionException || - this._exception instanceof Ice.ForcedCloseConnectionException || + this._exception instanceof Ice.ConnectionManuallyClosedException || this._exception instanceof Ice.ConnectionTimeoutException || this._exception instanceof Ice.CommunicatorDestroyedException || this._exception instanceof Ice.ObjectAdapterDeactivatedException)) @@ -1211,7 +1223,7 @@ class ConnectionI // Don't warn about certain expected exceptions. // if(!(this._exception instanceof Ice.CloseConnectionException || - this._exception instanceof Ice.ForcedCloseConnectionException || + this._exception instanceof Ice.ConnectionManuallyClosedException || this._exception instanceof Ice.ConnectionTimeoutException || this._exception instanceof Ice.CommunicatorDestroyedException || this._exception instanceof Ice.ObjectAdapterDeactivatedException || @@ -1416,15 +1428,13 @@ class ConnectionI initiateShutdown() { - Debug.assert(this._state === StateClosing); - Debug.assert(this._dispatchCount === 0); + Debug.assert(this._state === StateClosing && this._dispatchCount === 0); Debug.assert(!this._shutdownInitiated); if(!this._endpoint.datagram()) { // - // Before we shut down, we send a close connection - // message. + // Before we shut down, we send a close connection message. // const os = new OutputStream(this._instance, Protocol.currentProtocolEncoding); os.writeBlob(Protocol.magic); @@ -1454,7 +1464,7 @@ class ConnectionI } } - heartbeat() + sendHeartbeatNow() { Debug.assert(this._state === StateActive); diff --git a/js/src/Ice/OutgoingAsync.js b/js/src/Ice/OutgoingAsync.js index 62f14f0f64a..bce53bc29a9 100644 --- a/js/src/Ice/OutgoingAsync.js +++ b/js/src/Ice/OutgoingAsync.js @@ -578,9 +578,42 @@ class ConnectionFlushBatch extends OutgoingAsyncBase } } +class HeartbeatAsync extends OutgoingAsyncBase +{ + constructor(con, communicator) + { + super(communicator, "heartbeat", con, null, null); + } + + invoke() + { + try + { + this._os.writeBlob(Protocol.magic); + Protocol.currentProtocol._write(this._os); + Protocol.currentProtocolEncoding._write(this._os); + this._os.writeByte(Protocol.validateConnectionMsg); + this._os.writeByte(0); + this._os.writeInt(Protocol.headerSize); // Message size. + + let status = this._connection.sendAsyncRequest(this, false, false, 0); + + if((status & AsyncStatus.Sent) > 0) + { + this._sentSynchronously = true; + } + } + catch(ex) + { + this.completedEx(ex); + } + } +} + Ice.OutgoingAsync = OutgoingAsync; Ice.ProxyFlushBatch = ProxyFlushBatch; Ice.ProxyGetConnection = ProxyGetConnection; Ice.ConnectionFlushBatch = ConnectionFlushBatch; +Ice.HeartbeatAsync = HeartbeatAsync; module.exports.Ice = Ice; diff --git a/js/test/Ice/acm/Client.js b/js/test/Ice/acm/Client.js index e18f45ec0ac..58f8efdf313 100644 --- a/js/test/Ice/acm/Client.js +++ b/js/test/Ice/acm/Client.js @@ -391,6 +391,37 @@ } } + class HeartbeatManualTest extends TestCase + { + constructor(com, out) + { + super("manual heartbeats", com, out); + // + // Disable heartbeats. + // + this.setClientACM(10, -1, 0); + this.setServerACM(10, -1, 0); + } + + runTestCase(adapter, proxy) + { + function sendHeartbeats(con) + { + var p = Promise.resolve(); + for(var i = 0; i < 5; ++i) + { + p = p.then(con.heartbeat()); + } + return p; + } + + return proxy.startHeartbeatCount().then( + () => proxy.ice_getConnection()).then( + con => sendHeartbeats(con)).then( + () => proxy.waitForHeartbeatCount(5)); + } + } + class SetACMTest extends TestCase { constructor(com, out) @@ -421,7 +452,7 @@ test(acm.close === Ice.ACMClose.CloseOnInvocationAndIdle); test(acm.heartbeat === Ice.ACMHeartbeat.HeartbeatAlways); - return proxy.waitForHeartbeat(2); + return proxy.startHeartbeatCount().then(() => proxy.waitForHeartbeatCount(2)); } } @@ -459,6 +490,7 @@ tests.push(new HeartbeatOnIdleTest(com, out)); tests.push(new HeartbeatAlwaysTest(com, out)); + tests.push(new HeartbeatManualTest(com, out)); tests.push(new SetACMTest(com, out)); } diff --git a/js/test/Ice/acm/Test.ice b/js/test/Ice/acm/Test.ice index 117192a2df8..7f71427c741 100644 --- a/js/test/Ice/acm/Test.ice +++ b/js/test/Ice/acm/Test.ice @@ -18,7 +18,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/js/test/Ice/ami/Client.js b/js/test/Ice/ami/Client.js index 07b9ae03db4..39b5aeae704 100644 --- a/js/test/Ice/ami/Client.js +++ b/js/test/Ice/ami/Client.js @@ -111,7 +111,7 @@ // test(batchCount === 0); b1.opBatch(); - b1.ice_getCachedConnection().close(false); + b1.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); return communicator.flushBatchRequests().then(() => p.opBatchCount()); } ).then(batchCount => @@ -156,7 +156,7 @@ b2 = prx; b1.opBatch(); b2.opBatch(); - b1.ice_getCachedConnection().close(false); + b1.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); return communicator.flushBatchRequests(); } ).then(() => p.waitForBatch(1) @@ -181,8 +181,8 @@ b2 = prx; b1.opBatch(); b2.opBatch(); - b1.ice_getCachedConnection().close(false); - b2.ice_getCachedConnection().close(false); + b1.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); + b2.ice_getCachedConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait); return communicator.flushBatchRequests(); } ).then(() => p.opBatchCount()); diff --git a/js/test/Ice/binding/Client.js b/js/test/Ice/binding/Client.js index ecba375108b..0e59e8256c7 100644 --- a/js/test/Ice/binding/Client.js +++ b/js/test/Ice/binding/Client.js @@ -260,7 +260,7 @@ ).then( function(conn) { - return conn.close(false); + return conn.close(Ice.ConnectionClose.CloseGracefullyAndWait); } ).then( function() @@ -339,7 +339,7 @@ }).then( function(c) { - return c.close(false); + return c.close(Ice.ConnectionClose.CloseGracefullyAndWait); } ); })); @@ -422,7 +422,7 @@ ).then( function(conn) { - return conn.close(false); + return conn.close(Ice.ConnectionClose.CloseGracefullyAndWait); } ).then( function() @@ -585,7 +585,7 @@ }).then( function(c) { - return c.close(false); + return c.close(Ice.ConnectionClose.CloseGracefullyAndWait); }, function(ex) { @@ -651,7 +651,7 @@ } return prx.ice_getConnection(); } - ).then(conn => conn.close(false) + ).then(conn => conn.close(Ice.ConnectionClose.CloseGracefullyAndWait) ).then(() => names.length > 0 ? f1() : prx); }; @@ -678,7 +678,7 @@ ).then( function(conn) { - return conn.close(false); + return conn.close(Ice.ConnectionClose.CloseGracefullyAndWait); } ).then( function() diff --git a/js/test/Ice/hold/Client.js b/js/test/Ice/hold/Client.js index 51e3bf70c12..03f31d4c353 100644 --- a/js/test/Ice/hold/Client.js +++ b/js/test/Ice/hold/Client.js @@ -230,7 +230,7 @@ ).then( function(con) { - return con.close(false); + return con.close(Ice.ConnectionClose.CloseGracefullyAndWait); } ); } diff --git a/js/test/Ice/location/Client.js b/js/test/Ice/location/Client.js index 5890344ed19..62168831822 100644 --- a/js/test/Ice/location/Client.js +++ b/js/test/Ice/location/Client.js @@ -1110,7 +1110,7 @@ ).then( function(con) { - return con.close(false); + return con.close(Ice.ConnectionClose.CloseGracefullyAndWait); } ).then( function() diff --git a/js/test/Ice/operations/BatchOneways.js b/js/test/Ice/operations/BatchOneways.js index d3a66fa51c1..b6660b97781 100644 --- a/js/test/Ice/operations/BatchOneways.js +++ b/js/test/Ice/operations/BatchOneways.js @@ -82,7 +82,7 @@ ).then(count => batch.ice_flushBatchRequests() ).then(() => prx.opByteSOnewayCallCount() ).then(() => batch.ice_getConnection() - ).then(con => bidir ? undefined : con.close(false) + ).then(con => bidir ? undefined : con.close(Ice.ConnectionClose.CloseGracefullyAndWait) ).then(() => Promise.all([batch.ice_ping(), batch2.ice_ping()]) ).then(() => { diff --git a/js/test/Ice/timeout/Client.js b/js/test/Ice/timeout/Client.js index 62790d63f54..9f2872dfecb 100644 --- a/js/test/Ice/timeout/Client.js +++ b/js/test/Ice/timeout/Client.js @@ -142,7 +142,7 @@ connection = con; return timeout.holdAdapter(1500); } - ).then(() => connection.close(false) + ).then(() => connection.close(Ice.ConnectionClose.CloseGracefullyAndWait) ).then(() => { try @@ -163,7 +163,7 @@ } catch(ex) { - test(ex instanceof Ice.CloseConnectionException); // Expected + test(ex instanceof Ice.ConnectionManuallyClosedException); // Expected } return timeout.op(); } diff --git a/objective-c/src/Ice/ConnectionI.mm b/objective-c/src/Ice/ConnectionI.mm index 3b0d642b934..6de819d2433 100644 --- a/objective-c/src/Ice/ConnectionI.mm +++ b/objective-c/src/Ice/ConnectionI.mm @@ -266,12 +266,12 @@ private: } @implementation ICEConnection --(void) close:(BOOL)force +-(void) close:(ICEConnectionClose)mode { NSException* nsex = nil; try { - CONNECTION->close(force); + CONNECTION->close((Ice::ConnectionClose)mode); } catch(const std::exception& ex) { @@ -379,6 +379,52 @@ private: { CONNECTION->setHeartbeatCallback(new HeartbeatCallbackI(self, callback)); } +-(void) heartbeat +{ + NSException* nsex = nil; + try + { + CONNECTION->heartbeat(); + } + catch(const std::exception& ex) + { + nsex = toObjCException(ex); + } + if(nsex != nil) + { + @throw nsex; + } +} +-(id<ICEAsyncResult>) begin_heartbeat +{ + return beginCppCall(^(Ice::AsyncResultPtr& result) + { + result = CONNECTION->begin_heartbeat(); + }); +} +-(id<ICEAsyncResult>) begin_heartbeat:(void(^)(ICEException*))exception +{ + return [self begin_heartbeat:exception sent:nil]; +} +-(id<ICEAsyncResult>) begin_heartbeat:(void(^)(ICEException*))exception sent:(void(^)(BOOL))sent +{ + return beginCppCall(^(Ice::AsyncResultPtr& result, const Ice::CallbackPtr& cb) + { + result = CONNECTION->begin_heartbeat(cb); + }, + ^(const Ice::AsyncResultPtr& result) + { + CONNECTION->end_heartbeat(result); + }, + exception, sent); +} +-(void) end_heartbeat:(id<ICEAsyncResult>)result +{ + endCppCall(^(const Ice::AsyncResultPtr& r) + { + CONNECTION->end_heartbeat(r); + }, result); +} -(void) setACM:(id)timeout close:(id)close heartbeat:(id)heartbeat { IceUtil::Optional<int> to; diff --git a/objective-c/test/Ice/acm/ACMTest.ice b/objective-c/test/Ice/acm/ACMTest.ice index d723f023cf1..a8f40466fc5 100644 --- a/objective-c/test/Ice/acm/ACMTest.ice +++ b/objective-c/test/Ice/acm/ACMTest.ice @@ -18,7 +18,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/objective-c/test/Ice/acm/AllTests.m b/objective-c/test/Ice/acm/AllTests.m index 09a49e3788a..8d53772a1ed 100644 --- a/objective-c/test/Ice/acm/AllTests.m +++ b/objective-c/test/Ice/acm/AllTests.m @@ -749,6 +749,40 @@ } @end +@interface HeartbeatManualTest : TestCase ++(id) testCase:(id<TestACMRemoteCommunicatorPrx>)com; ++(NSString*) getName; +-(void) runTestCase:(id<TestACMRemoteObjectAdapterPrx>)adapter proxy:(id<TestACMTestIntfPrx>)proxy; +@end + +@implementation HeartbeatManualTest ++(id) testCase:(id<TestACMRemoteCommunicatorPrx>)com +{ + id tc = [super testCase:com]; + // + // Disable heartbeats. + // + [tc setClientACM:10 close:-1 heartbeat:0]; + [tc setServerACM:10 close:-1 heartbeat:0]; + return tc; +} ++(NSString*) getName +{ + return @"manual heartbeats"; +} +-(void) runTestCase:(id<TestACMRemoteObjectAdapterPrx>)adapter proxy:(id<TestACMTestIntfPrx>)proxy +{ + [proxy startHeartbeatCount]; + id con = [proxy ice_getConnection]; + [con heartbeat]; + [con heartbeat]; + [con heartbeat]; + [con heartbeat]; + [con heartbeat]; + [proxy waitForHeartbeatCount:5]; +} +@end + @interface SetACMTest : TestCase +(id) testCase:(id<TestACMRemoteCommunicatorPrx>)com; +(NSString*) getName; @@ -788,7 +822,8 @@ test(acm.close == ICECloseOnInvocationAndIdle); test(acm.heartbeat == ICEHeartbeatAlways); - [proxy waitForHeartbeat:2]; + [proxy startHeartbeatCount]; + [proxy waitForHeartbeatCount:2]; } @end @@ -812,6 +847,7 @@ acmAllTests(id<ICECommunicator> communicator) [tests addObject:[HeartbeatOnIdleTest testCase:com]]; [tests addObject:[HeartbeatAlwaysTest testCase:com]]; + [tests addObject:[HeartbeatManualTest testCase:com]]; [tests addObject:[SetACMTest testCase:com]]; for(int i = 0; i < tests.count; ++i) diff --git a/objective-c/test/Ice/acm/TestI.h b/objective-c/test/Ice/acm/TestI.h index a4c24b41898..cacae9ee902 100644 --- a/objective-c/test/Ice/acm/TestI.h +++ b/objective-c/test/Ice/acm/TestI.h @@ -23,8 +23,17 @@ -(id) initWithAdapter:(id<ICEObjectAdapter>)adapter; @end +@interface ConnectionCallbackI : NSObject +{ + NSCondition* _cond; + int _count; +} +-(void) waitForCount:(int)count; +@end + @interface TestACMTestIntfI : TestACMTestIntf<TestACMTestIntf> { NSCondition* _cond; + ConnectionCallbackI* _callback; } @end diff --git a/objective-c/test/Ice/acm/TestI.m b/objective-c/test/Ice/acm/TestI.m index 2e24f62d711..8fe726732f9 100644 --- a/objective-c/test/Ice/acm/TestI.m +++ b/objective-c/test/Ice/acm/TestI.m @@ -17,7 +17,6 @@ -(void) waitForCount:(int)count; @end - @implementation ACMConnectionCallbackI -(id) init { @@ -33,17 +32,16 @@ -(void) heartbeat:(id<ICEConnection>)c { [_cond lock]; - --_count; + ++_count; [_cond signal]; [_cond unlock]; } -(void) waitForCount:(int)count { [_cond lock]; - _count = count; @try { - while(_count > 0) + while(_count < count) { [_cond wait]; } @@ -192,15 +190,18 @@ [_cond signal]; [_cond unlock]; } --(void) waitForHeartbeat:(int)count current:(ICECurrent*)current +-(void) startHeartbeatCount:(ICECurrent*)current { - ACMConnectionCallbackI* callback = [ACMConnectionCallbackI new]; + _callback = [ACMConnectionCallbackI new]; [current.con setHeartbeatCallback:^(id<ICEConnection> c) { - [callback heartbeat:c]; + [_callback heartbeat:c]; }]; - [callback waitForCount:count]; - ICE_RELEASE(callback); +} +-(void) waitForHeartbeatCount:(int)count current:(ICECurrent*)current +{ + [_callback waitForCount:count]; + ICE_RELEASE(_callback); } @end diff --git a/php/src/php5/Connection.cpp b/php/src/php5/Connection.cpp index 1d85343ed37..68827e63243 100644 --- a/php/src/php5/Connection.cpp +++ b/php/src/php5/Connection.cpp @@ -78,15 +78,22 @@ ZEND_METHOD(Ice_Connection, close) Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis() TSRMLS_CC); assert(_this); - zend_bool b; - if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("b"), &b TSRMLS_CC) != SUCCESS) + zval* mode; + if(zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, const_cast<char*>("z"), &mode TSRMLS_CC) != SUCCESS) { RETURN_NULL(); } + if(Z_TYPE_P(mode) != IS_LONG) + { + invalidArgument("value for 'mode' argument must be an enumerator of ConnectionClose" TSRMLS_CC); + RETURN_NULL(); + } + Ice::ConnectionClose cc = static_cast<Ice::ConnectionClose>(Z_LVAL_P(mode)); + try { - _this->close(b ? true : false); + _this->close(cc); } catch(const IceUtil::Exception& ex) { @@ -140,6 +147,27 @@ ZEND_METHOD(Ice_Connection, flushBatchRequests) } } +ZEND_METHOD(Ice_Connection, heartbeat) +{ + if(ZEND_NUM_ARGS() > 0) + { + WRONG_PARAM_COUNT; + } + + Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis() TSRMLS_CC); + assert(_this); + + try + { + _this->heartbeat(); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex TSRMLS_CC); + RETURN_NULL(); + } +} + ZEND_METHOD(Ice_Connection, setACM) { Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis() TSRMLS_CC); @@ -411,6 +439,7 @@ static zend_function_entry _connectionClassMethods[] = ZEND_ME(Ice_Connection, close, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, getEndpoint, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, flushBatchRequests, ICE_NULLPTR, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Connection, heartbeat, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, setACM, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, getACM, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, type, ICE_NULLPTR, ZEND_ACC_PUBLIC) diff --git a/php/src/php5/Util.cpp b/php/src/php5/Util.cpp index 24374f91049..1f3ea98fb8e 100644 --- a/php/src/php5/Util.cpp +++ b/php/src/php5/Util.cpp @@ -618,6 +618,10 @@ convertLocalException(const Ice::LocalException& ex, zval* zex TSRMLS_DC) { setStringMember(zex, "reason", e.reason TSRMLS_CC); } + catch(const Ice::ConnectionManuallyClosedException& e) + { + add_property_bool(zex, "graceful", e.graceful ? 1 : 0); + } catch(const Ice::LocalException&) { // diff --git a/php/src/php7/Connection.cpp b/php/src/php7/Connection.cpp index 2d598fc7da9..57c0b197767 100644 --- a/php/src/php7/Connection.cpp +++ b/php/src/php7/Connection.cpp @@ -78,15 +78,22 @@ ZEND_METHOD(Ice_Connection, close) Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis()); assert(_this); - zend_bool b; - if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("b"), &b) != SUCCESS) + zval* mode; + if(zend_parse_parameters(ZEND_NUM_ARGS(), const_cast<char*>("z"), &mode) != SUCCESS) { RETURN_NULL(); } + if(Z_TYPE_P(mode) != IS_LONG) + { + invalidArgument("value for 'mode' argument must be an enumerator of ConnectionClose"); + RETURN_NULL(); + } + Ice::ConnectionClose cc = static_cast<Ice::ConnectionClose>(Z_LVAL_P(mode)); + try { - _this->close(b ? true : false); + _this->close(cc); } catch(const IceUtil::Exception& ex) { @@ -140,6 +147,27 @@ ZEND_METHOD(Ice_Connection, flushBatchRequests) } } +ZEND_METHOD(Ice_Connection, heartbeat) +{ + if(ZEND_NUM_ARGS() > 0) + { + WRONG_PARAM_COUNT; + } + + Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis()); + assert(_this); + + try + { + _this->heartbeat(); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex); + RETURN_NULL(); + } +} + ZEND_METHOD(Ice_Connection, setACM) { Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis()); @@ -331,6 +359,27 @@ ZEND_METHOD(Ice_Connection, setBufferSize) } } +ZEND_METHOD(Ice_Connection, throwException) +{ + if(ZEND_NUM_ARGS() > 0) + { + WRONG_PARAM_COUNT; + } + + Ice::ConnectionPtr _this = Wrapper<Ice::ConnectionPtr>::value(getThis()); + assert(_this); + + try + { + _this->throwException(); + } + catch(const IceUtil::Exception& ex) + { + throwException(ex); + RETURN_NULL(); + } +} + #ifdef _WIN32 extern "C" #endif @@ -406,6 +455,7 @@ static zend_function_entry _connectionClassMethods[] = ZEND_ME(Ice_Connection, close, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, getEndpoint, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, flushBatchRequests, ICE_NULLPTR, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Connection, heartbeat, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, setACM, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, getACM, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, type, ICE_NULLPTR, ZEND_ACC_PUBLIC) @@ -413,6 +463,7 @@ static zend_function_entry _connectionClassMethods[] = ZEND_ME(Ice_Connection, toString, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, getInfo, ICE_NULLPTR, ZEND_ACC_PUBLIC) ZEND_ME(Ice_Connection, setBufferSize, ICE_NULLPTR, ZEND_ACC_PUBLIC) + ZEND_ME(Ice_Connection, throwException, ICE_NULLPTR, ZEND_ACC_PUBLIC) {0, 0, 0} }; diff --git a/php/src/php7/Util.cpp b/php/src/php7/Util.cpp index 0399c940cb1..bb38b45066a 100644 --- a/php/src/php7/Util.cpp +++ b/php/src/php7/Util.cpp @@ -565,6 +565,10 @@ convertLocalException(const Ice::LocalException& ex, zval* zex) { setStringMember(zex, "reason", e.reason); } + catch(const Ice::ConnectionManuallyClosedException& e) + { + add_property_bool(zex, "graceful", e.graceful ? 1 : 0); + } catch(const Ice::LocalException&) { // diff --git a/php/test/Ice/acm/Client.php b/php/test/Ice/acm/Client.php index 387cf2b1d4e..b1b3a0bbd84 100644 --- a/php/test/Ice/acm/Client.php +++ b/php/test/Ice/acm/Client.php @@ -30,16 +30,13 @@ function test($b) } } -function allTests($communicator) +function testSetACM($communicator, $com) { global $NS; echo "testing setACM/getACM... "; flush(); - $ref = "communicator:default -p 12010"; - $com = $communicator->stringToProxy($ref)->ice_uncheckedCast("::Test::RemoteCommunicator"); - $adapter = $com->createObjectAdapter(-1, -1, -1); $initData = $NS ? eval("return new Ice\\InitializationData;") : new Ice_InitializationData; @@ -83,21 +80,60 @@ function allTests($communicator) test($acm->close == $CloseOnInvocationAndIdle); test($acm->heartbeat == $HeartbeatAlways); - $proxy->waitForHeartbeat(2); + $proxy->startHeartbeatCount(); + $proxy->waitForHeartbeatCount(2); $adapter->deactivate(); $testCommunicator->destroy(); echo "ok\n"; +} + +function testHeartbeatManual($communicator, $com) +{ + global $NS; - echo "shutting down... "; + echo "testing manual heartbeats... "; flush(); - $com->shutdown(); + + $adapter = $com->createObjectAdapter(10, -1, 0); + + $initData = $NS ? eval("return new Ice\\InitializationData;") : new Ice_InitializationData; + $initData->properties = $communicator->getProperties()->clone(); + $initData->properties->setProperty("Ice.ACM.Timeout", "10"); + $initData->properties->setProperty("Ice.ACM.Client.Timeout", "10"); + $initData->properties->setProperty("Ice.ACM.Client.Close", "0"); + $initData->properties->setProperty("Ice.ACM.Client.Heartbeat", "0"); + $testCommunicator = $NS ? eval("return Ice\\initialize(\$initData);") : Ice_initialize($initData); + $proxy = $testCommunicator->stringToProxy($adapter->getTestIntf()->ice_toString())->ice_uncheckedCast( + "::Test::TestIntf"); + $con = $proxy->ice_getConnection(); + + $proxy->startHeartbeatCount(); + $con->heartbeat(); + $con->heartbeat(); + $con->heartbeat(); + $con->heartbeat(); + $con->heartbeat(); + $proxy->waitForHeartbeatCount(5); + + $adapter->deactivate(); + $testCommunicator->destroy(); echo "ok\n"; } +function allTests($communicator) +{ + $ref = "communicator:default -p 12010"; + $com = $communicator->stringToProxy($ref)->ice_uncheckedCast("::Test::RemoteCommunicator"); + + testSetACM($communicator, $com); + testHeartbeatManual($communicator, $com); + + $com->shutdown(); +} + $communicator = $NS ? eval("return Ice\\initialize(\$argv);") : eval("return Ice_initialize(\$argv);"); - allTests($communicator); $communicator->destroy(); diff --git a/php/test/Ice/acm/Test.ice b/php/test/Ice/acm/Test.ice index 5ab98180dd3..d78abd6eb0f 100644 --- a/php/test/Ice/acm/Test.ice +++ b/php/test/Ice/acm/Test.ice @@ -17,7 +17,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/php/test/Ice/binding/Client.php b/php/test/Ice/binding/Client.php index 378981ce357..291188e5e2c 100644 --- a/php/test/Ice/binding/Client.php +++ b/php/test/Ice/binding/Client.php @@ -61,6 +61,9 @@ function allTests($communicator) $random = $NS ? constant("Ice\\EndpointSelectionType::Random") : constant("Ice_EndpointSelectionType::Random"); $ordered = $NS ? constant("Ice\\EndpointSelectionType::Ordered") : constant("Ice_EndpointSelectionType::Ordered"); + $closeGracefullyAndWait = + $NS ? constant("Ice\\ConnectionClose::CloseGracefullyAndWait") : + constant("Ice_ConnectionClose::CloseGracefullyAndWait"); $ref = "communicator:default -p 12010"; $com = $communicator->stringToProxy($ref)->ice_uncheckedCast("::Test::RemoteCommunicator"); @@ -130,7 +133,7 @@ function allTests($communicator) { unset($names[$key]); } - $test1->ice_getConnection()->close(false); + $test1->ice_getConnection()->close($closeGracefullyAndWait); } // @@ -151,7 +154,7 @@ function allTests($communicator) foreach($adapters as $p) { - $p->getTestIntf()->ice_getConnection()->close(false); + $p->getTestIntf()->ice_getConnection()->close($closeGracefullyAndWait); } } @@ -179,7 +182,7 @@ function allTests($communicator) { unset($names[$key]); } - $test1->ice_getConnection()->close(false); + $test1->ice_getConnection()->close($closeGracefullyAndWait); } // @@ -213,7 +216,7 @@ function allTests($communicator) { unset($names[$key]); } - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); } $test = $test->ice_endpointSelection($random)->ice_uncheckedCast("::Test::TestIntf"); @@ -227,7 +230,7 @@ function allTests($communicator) { unset($names[$key]); } - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); } deactivate($com, $adapters); @@ -285,11 +288,11 @@ function allTests($communicator) $adapters[] = $com->createObjectAdapter("Adapter36", $endpoints[2]->toString()); for($i = 0; $i < $nRetry && $test->getAdapterName() == "Adapter36"; $i++); test($i == $nRetry); - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); $adapters[] = $com->createObjectAdapter("Adapter35", $endpoints[1]->toString()); for($i = 0; $i < $nRetry && $test->getAdapterName() == "Adapter35"; $i++); test($i == $nRetry); - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); $adapters[] = $com->createObjectAdapter("Adapter34", $endpoints[0]->toString()); for($i = 0; $i < $nRetry && $test->getAdapterName() == "Adapter34"; $i++); test($i == $nRetry); @@ -475,7 +478,7 @@ function allTests($communicator) for($i = 0; $i < 5; $i++) { test($test->getAdapterName() == "Adapter82"); - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); } $testSecure = $test->ice_secure(true)->ice_uncheckedCast("::Test::TestIntf"); @@ -491,7 +494,7 @@ function allTests($communicator) for($i = 0; $i < 5; $i++) { test($test->getAdapterName() == "Adapter81"); - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); } $endpts = $test->ice_getEndpoints(); @@ -500,7 +503,7 @@ function allTests($communicator) for($i = 0; $i < 5; $i++) { test($test->getAdapterName() == "Adapter83"); - $test->ice_getConnection()->close(false); + $test->ice_getConnection()->close($closeGracefullyAndWait); } $com->deactivateObjectAdapter($adapters[0]); diff --git a/python/modules/IcePy/Connection.cpp b/python/modules/IcePy/Connection.cpp index ec1e4f8db68..8e9e9286d02 100644 --- a/python/modules/IcePy/Connection.cpp +++ b/python/modules/IcePy/Connection.cpp @@ -37,18 +37,18 @@ struct ConnectionObject Ice::CommunicatorPtr* communicator; }; -class CloseCallbackI : public Ice::CloseCallback +class CloseCallbackWrapper : public Ice::CloseCallback { public: - CloseCallbackI(PyObject* cb, PyObject* con) : + CloseCallbackWrapper(PyObject* cb, PyObject* con) : _cb(cb), _con(con) { Py_INCREF(cb); Py_INCREF(con); } - virtual ~CloseCallbackI() + virtual ~CloseCallbackWrapper() { AdoptThread adoptThread; // Ensure the current thread is able to call into Python. @@ -94,18 +94,18 @@ private: PyObject* _con; }; -class HeartbeatCallbackI : public Ice::HeartbeatCallback +class HeartbeatCallbackWrapper : public Ice::HeartbeatCallback { public: - HeartbeatCallbackI(PyObject* cb, PyObject* con) : + HeartbeatCallbackWrapper(PyObject* cb, PyObject* con) : _cb(cb), _con(con) { Py_INCREF(cb); Py_INCREF(con); } - virtual ~HeartbeatCallbackI() + virtual ~HeartbeatCallbackWrapper() { AdoptThread adoptThread; // Ensure the current thread is able to call into Python. @@ -151,6 +151,62 @@ private: PyObject* _con; }; +class HeartbeatAsyncCallback : public IceUtil::Shared +{ +public: + + HeartbeatAsyncCallback(PyObject* ex, PyObject* sent, const string& op) : + _ex(ex), _sent(sent), _op(op) + { + assert(_ex); + Py_INCREF(_ex); + Py_XINCREF(_sent); + } + + ~HeartbeatAsyncCallback() + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + Py_DECREF(_ex); + Py_XDECREF(_sent); + } + + void exception(const Ice::Exception& ex) + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + + PyObjectHandle exh = convertException(ex); + assert(exh.get()); + PyObjectHandle args = Py_BuildValue(STRCAST("(O)"), exh.get()); + PyObjectHandle tmp = PyObject_Call(_ex, args.get(), 0); + if(PyErr_Occurred()) + { + throwPythonException(); // Callback raised an exception. + } + } + + void sent(bool sentSynchronously) + { + if(_sent) + { + AdoptThread adoptThread; // Ensure the current thread is able to call into Python. + PyObjectHandle args = Py_BuildValue(STRCAST("(O)"), sentSynchronously ? getTrue() : getFalse()); + PyObjectHandle tmp = PyObject_Call(_sent, args.get(), 0); + if(PyErr_Occurred()) + { + throwPythonException(); // Callback raised an exception. + } + } + } + +protected: + + PyObject* _ex; + PyObject* _sent; + std::string _op; +}; +typedef IceUtil::Handle<HeartbeatAsyncCallback> HeartbeatAsyncCallbackPtr; + } #ifdef WIN32 @@ -241,17 +297,22 @@ extern "C" static PyObject* connectionClose(ConnectionObject* self, PyObject* args) { - int force; - if(!PyArg_ParseTuple(args, STRCAST("i"), &force)) + PyObject* closeType = lookupType("Ice.ConnectionClose"); + PyObject* mode; + if(!PyArg_ParseTuple(args, STRCAST("O!"), closeType, &mode)) { return 0; } + PyObjectHandle v = PyObject_GetAttrString(mode, STRCAST("_value")); + assert(v.get()); + Ice::ConnectionClose cc = static_cast<Ice::ConnectionClose>(PyLong_AsLong(v.get())); + assert(self->connection); try { AllowThreads allowThreads; // Release Python's global interpreter lock during blocking invocations. - (*self->connection)->close(force > 0); + (*self->connection)->close(cc); } catch(const Ice::Exception& ex) { @@ -532,7 +593,7 @@ connectionSetCloseCallback(ConnectionObject* self, PyObject* args) return 0; } - Ice::CloseCallbackPtr wrapper = new CloseCallbackI(cb, reinterpret_cast<PyObject*>(self)); + Ice::CloseCallbackPtr wrapper = new CloseCallbackWrapper(cb, reinterpret_cast<PyObject*>(self)); try { AllowThreads allowThreads; // Release Python's global interpreter lock during blocking invocations. @@ -563,7 +624,7 @@ connectionSetHeartbeatCallback(ConnectionObject* self, PyObject* args) return 0; } - Ice::HeartbeatCallbackPtr wrapper = new HeartbeatCallbackI(cb, reinterpret_cast<PyObject*>(self)); + Ice::HeartbeatCallbackPtr wrapper = new HeartbeatCallbackWrapper(cb, reinterpret_cast<PyObject*>(self)); try { AllowThreads allowThreads; // Release Python's global interpreter lock during blocking invocations. @@ -583,6 +644,127 @@ connectionSetHeartbeatCallback(ConnectionObject* self, PyObject* args) extern "C" #endif static PyObject* +connectionHeartbeat(ConnectionObject* self) +{ + assert(self->connection); + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + (*self->connection)->heartbeat(); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +connectionBeginHeartbeat(ConnectionObject* self, PyObject* args, PyObject* kwds) +{ + assert(self->connection); + + static char* argNames[] = + { + const_cast<char*>("_ex"), + const_cast<char*>("_sent"), + 0 + }; + PyObject* ex = Py_None; + PyObject* sent = Py_None; + if(!PyArg_ParseTupleAndKeywords(args, kwds, STRCAST("|OO"), argNames, &ex, &sent)) + { + return 0; + } + + if(ex == Py_None) + { + ex = 0; + } + if(sent == Py_None) + { + sent = 0; + } + + if(!ex && sent) + { + PyErr_Format(PyExc_RuntimeError, + STRCAST("exception callback must also be provided when sent callback is used")); + return 0; + } + + Ice::Callback_Connection_heartbeatPtr cb; + if(ex || sent) + { + HeartbeatAsyncCallbackPtr d = new HeartbeatAsyncCallback(ex, sent, "heartbeat"); + cb = Ice::newCallback_Connection_heartbeat(d, &HeartbeatAsyncCallback::exception, + &HeartbeatAsyncCallback::sent); + } + + Ice::AsyncResultPtr result; + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during remote invocations. + + if(cb) + { + result = (*self->connection)->begin_heartbeat(cb); + } + else + { + result = (*self->connection)->begin_heartbeat(); + } + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + PyObjectHandle communicator = getCommunicatorWrapper(*self->communicator); + return createAsyncResult(result, 0, reinterpret_cast<PyObject*>(self), communicator.get()); +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* +connectionEndHeartbeat(ConnectionObject* self, PyObject* args) +{ + assert(self->connection); + + PyObject* result; + if(!PyArg_ParseTuple(args, STRCAST("O!"), &AsyncResultType, &result)) + { + return 0; + } + + Ice::AsyncResultPtr r = getAsyncResult(result); + try + { + AllowThreads allowThreads; // Release Python's global interpreter lock during blocking invocations. + (*self->connection)->end_flushBatchRequests(r); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + Py_INCREF(Py_None); + return Py_None; +} + +#ifdef WIN32 +extern "C" +#endif +static PyObject* connectionSetACM(ConnectionObject* self, PyObject* args) { assert(self->connection); @@ -853,6 +1035,27 @@ connectionSetBufferSize(ConnectionObject* self, PyObject* args) return Py_None; } +#ifdef WIN32 +extern "C" +#endif +static PyObject* +connectionThrowException(ConnectionObject* self) +{ + assert(self->connection); + try + { + (*self->connection)->throwException(); + } + catch(const Ice::Exception& ex) + { + setPythonException(ex); + return 0; + } + + Py_INCREF(Py_None); + return Py_None; +} + static PyMethodDef ConnectionMethods[] = { { STRCAST("close"), reinterpret_cast<PyCFunction>(connectionClose), METH_VARARGS, @@ -875,6 +1078,12 @@ static PyMethodDef ConnectionMethods[] = PyDoc_STR(STRCAST("setCloseCallback(Ice.CloseCallback) -> None")) }, { STRCAST("setHeartbeatCallback"), reinterpret_cast<PyCFunction>(connectionSetHeartbeatCallback), METH_VARARGS, PyDoc_STR(STRCAST("setHeartbeatCallback(Ice.HeartbeatCallback) -> None")) }, + { STRCAST("heartbeat"), reinterpret_cast<PyCFunction>(connectionHeartbeat), METH_NOARGS, + PyDoc_STR(STRCAST("heartbeat() -> None")) }, + { STRCAST("begin_heartbeat"), reinterpret_cast<PyCFunction>(connectionBeginHeartbeat), + METH_VARARGS | METH_KEYWORDS, PyDoc_STR(STRCAST("begin_heartbeat([_ex][, _sent]) -> Ice.AsyncResult")) }, + { STRCAST("end_heartbeat"), reinterpret_cast<PyCFunction>(connectionEndHeartbeat), METH_VARARGS, + PyDoc_STR(STRCAST("end_heartbeat(Ice.AsyncResult) -> None")) }, { STRCAST("setACM"), reinterpret_cast<PyCFunction>(connectionSetACM), METH_VARARGS, PyDoc_STR(STRCAST("setACM(int, Ice.ACMClose, Ice.ACMHeartbeat) -> None")) }, { STRCAST("getACM"), reinterpret_cast<PyCFunction>(connectionGetACM), METH_NOARGS, @@ -891,6 +1100,8 @@ static PyMethodDef ConnectionMethods[] = PyDoc_STR(STRCAST("getEndpoint() -> Ice.Endpoint")) }, { STRCAST("setBufferSize"), reinterpret_cast<PyCFunction>(connectionSetBufferSize), METH_VARARGS, PyDoc_STR(STRCAST("setBufferSize(int, int) -> None")) }, + { STRCAST("throwException"), reinterpret_cast<PyCFunction>(connectionThrowException), METH_NOARGS, + PyDoc_STR(STRCAST("throwException() -> None")) }, { 0, 0 } /* sentinel */ }; diff --git a/python/modules/IcePy/Util.cpp b/python/modules/IcePy/Util.cpp index 953bbee84a0..c3b8f18462c 100644 --- a/python/modules/IcePy/Util.cpp +++ b/python/modules/IcePy/Util.cpp @@ -857,6 +857,10 @@ convertLocalException(const Ice::LocalException& ex, PyObject* p) IcePy::PyObjectHandle m = IcePy::createString(e.reason); PyObject_SetAttrString(p, STRCAST("reason"), m.get()); } + catch(const Ice::ConnectionManuallyClosedException& e) + { + PyObject_SetAttrString(p, STRCAST("graceful"), e.graceful ? IcePy::getTrue() : IcePy::getFalse()); + } catch(const Ice::LocalException&) { // diff --git a/python/test/Ice/acm/AllTests.py b/python/test/Ice/acm/AllTests.py index c10edd1de77..1c7bb57709b 100644 --- a/python/test/Ice/acm/AllTests.py +++ b/python/test/Ice/acm/AllTests.py @@ -294,6 +294,25 @@ def allTests(communicator): with self.m: test(self._heartbeat >= 3) + class HeartbeatManualTest(TestCase): + def __init__(self, com): + TestCase.__init__(self, "manual heartbeats", com) + # + # Disable heartbeats. + # + self.setClientACM(10, -1, 0) + self.setServerACM(10, -1, 0) + + def runTestCase(self, adapter, proxy): + proxy.startHeartbeatCount() + con = proxy.ice_getConnection() + con.heartbeat() + con.heartbeat() + con.heartbeat() + con.heartbeat() + con.heartbeat() + proxy.waitForHeartbeatCount(5) + class SetACMTest(TestCase): def __init__(self, com): TestCase.__init__(self, "setACM/getACM", com) @@ -318,7 +337,8 @@ def allTests(communicator): test(acm.close == Ice.ACMClose.CloseOnInvocationAndIdle) test(acm.heartbeat == Ice.ACMHeartbeat.HeartbeatAlways) - proxy.waitForHeartbeat(2) + proxy.startHeartbeatCount() + proxy.waitForHeartbeatCount(2) tests.append(InvocationHeartbeatTest(com)) tests.append(InvocationHeartbeatOnHoldTest(com)) @@ -332,6 +352,7 @@ def allTests(communicator): tests.append(HeartbeatOnIdleTest(com)) tests.append(HeartbeatAlwaysTest(com)) + tests.append(HeartbeatManualTest(com)) tests.append(SetACMTest(com)) for p in tests: diff --git a/python/test/Ice/acm/Test.ice b/python/test/Ice/acm/Test.ice index 5ab98180dd3..d78abd6eb0f 100644 --- a/python/test/Ice/acm/Test.ice +++ b/python/test/Ice/acm/Test.ice @@ -17,7 +17,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/python/test/Ice/acm/TestI.py b/python/test/Ice/acm/TestI.py index 10565ca9540..41f5afe93eb 100644 --- a/python/test/Ice/acm/TestI.py +++ b/python/test/Ice/acm/TestI.py @@ -9,6 +9,21 @@ import Ice, Test, threading +class ConnectionCallbackI(): + def __init__(self): + self.m = threading.Condition() + self.count = 0 + + def heartbeat(self, con): + with self.m: + self.count += 1 + self.m.notifyAll() + + def waitForCount(self, count): + with self.m: + while self.count < count: + self.m.wait() + class RemoteCommunicatorI(Test._RemoteCommunicatorDisp): def createObjectAdapter(self, timeout, close, heartbeat, current=None): com = current.adapter.getCommunicator() @@ -68,26 +83,9 @@ class TestIntfI(Test._TestIntfDisp): with self.m: self.m.notifyAll() - def waitForHeartbeat(self, count, current=None): - - class ConnectionCallbackI(): - - def __init__(self): - self.m = threading.Condition() - self.count = 0 - - def heartbeat(self, con): - with self.m: - self.count -= 1 - self.m.notifyAll() - - def waitForCount(self, count): - with self.m: - self.count = count - while self.count > 0: - self.m.wait() - - callback = ConnectionCallbackI() - current.con.setHeartbeatCallback(lambda con: callback.heartbeat(con)) - callback.waitForCount(2) + def startHeartbeatCount(self, current=None): + self.callback = ConnectionCallbackI() + current.con.setHeartbeatCallback(lambda con: self.callback.heartbeat(con)) + def waitForHeartbeatCount(self, count, current=None): + self.callback.waitForCount(2) diff --git a/python/test/Ice/ami/AllTests.py b/python/test/Ice/ami/AllTests.py index 549915ee12e..71d6237b61a 100644 --- a/python/test/Ice/ami/AllTests.py +++ b/python/test/Ice/ami/AllTests.py @@ -769,7 +769,7 @@ def allTests(communicator, collocated): test(p.opBatchCount() == 0) b1 = p.ice_batchOneway() b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushCallback() r = b1.begin_ice_flushBatchRequests(cb.exception, cb.sent) cb.check() @@ -783,7 +783,7 @@ def allTests(communicator, collocated): test(p.opBatchCount() == 0) b1 = p.ice_batchOneway() b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushCallback(cookie) r = b1.begin_ice_flushBatchRequests(lambda ex: cb.exceptionWC(ex, cookie), lambda ss: cb.sentWC(ss, cookie)) cb.check() @@ -830,7 +830,7 @@ def allTests(communicator, collocated): test(p.opBatchCount() == 0) b1 = Test.TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity()).ice_batchOneway()) b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushExCallback() r = b1.ice_getConnection().begin_flushBatchRequests(cb.exception, cb.sent) cb.check() @@ -844,7 +844,7 @@ def allTests(communicator, collocated): test(p.opBatchCount() == 0) b1 = Test.TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity()).ice_batchOneway()) b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushExCallback(cookie) r = b1.ice_getConnection().begin_flushBatchRequests(lambda ex: cb.exceptionWC(ex, cookie), lambda ss: cb.sentWC(ss, cookie)) @@ -876,7 +876,7 @@ def allTests(communicator, collocated): test(p.opBatchCount() == 0) b1 = Test.TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity()).ice_batchOneway()) b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushCallback() r = communicator.begin_flushBatchRequests(cb.exception, cb.sent) cb.check() @@ -916,7 +916,7 @@ def allTests(communicator, collocated): b2.ice_getConnection() # Ensure connection is established. b1.opBatch() b2.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushCallback() r = communicator.begin_flushBatchRequests(cb.exception, cb.sent) cb.check() @@ -936,8 +936,8 @@ def allTests(communicator, collocated): b2.ice_getConnection() # Ensure connection is established. b1.opBatch() b2.opBatch() - b1.ice_getConnection().close(False) - b2.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FlushCallback() r = communicator.begin_flushBatchRequests(cb.exception, cb.sent) cb.check() @@ -1120,9 +1120,27 @@ def allTests(communicator, collocated): print("ok") if p.ice_getConnection(): - sys.stdout.write("testing close connection with sending queue... ") + sys.stdout.write("testing graceful close connection with wait... ") sys.stdout.flush() + # + # Local case: begin several requests, close the connection gracefully, and make sure it waits + # for the requests to complete. + # + results = [] + for i in range(0, 3): + results.append(p.begin_sleep(50)) + p.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) + for r in results: + r.waitForCompleted() + try: + r.throwLocalException() + except: + test(False) + + # + # Remote case. + # if sys.version_info[0] == 2: b = [chr(random.randint(0, 255)) for x in range(0, 10*1024)] seq = ''.join(b) @@ -1143,7 +1161,7 @@ def allTests(communicator, collocated): results = [] for i in range(0, maxQueue): results.append(p.begin_opWithPayload(seq)) - if not p.begin_close(False).isSent(): + if not p.begin_close(Test.CloseMode.CloseGracefullyAndWait).isSent(): for i in range(0, maxQueue): r = p.begin_opWithPayload(seq) results.append(r) @@ -1163,6 +1181,83 @@ def allTests(communicator, collocated): print("ok") + sys.stdout.write("testing graceful close connection without wait... ") + sys.stdout.flush() + + # + # Local case: start a lengthy operation and then close the connection gracefully on the client side + # without waiting for the pending invocation to complete. There will be no retry and we expect the + # invocation to fail with ConnectionManuallyClosedException. + # + # This test requires two threads in the server's thread pool: one will block in sleep() and the other + # will process the CloseConnection message. + # + p.ice_ping() + con = p.ice_getConnection() + r = p.begin_sleep(1000) + con.close(Ice.ConnectionClose.CloseGracefully) + r.waitForCompleted() + try: + r.throwLocalException() + test(False) + except Ice.ConnectionManuallyClosedException, ex: + test(ex.graceful) + + # + # Remote case: the server closes the connection gracefully. Our call to TestIntf::close() + # completes successfully and then the connection should be closed immediately afterward, + # despite the fact that there's a pending call to sleep(). The call to sleep() should be + # automatically retried and complete successfully. + # + p.ice_ping() + con = p.ice_getConnection() + cb = CallbackBase() + con.setCloseCallback(lambda c: cb.called()) + r = p.begin_sleep(250) + p.close(Test.CloseMode.CloseGracefully) + cb.check() + r.waitForCompleted() + try: + r.throwLocalException() + except: + test(false) + p.ice_ping() + test(p.ice_getConnection() != con) + + print("ok") + + sys.stdout.write("testing forceful close connection... ") + sys.stdout.flush() + + # + # Local case: start a lengthy operation and then close the connection forcefully on the client side. + # There will be no retry and we expect the invocation to fail with ConnectionManuallyClosedException. + # + p.ice_ping() + con = p.ice_getConnection() + r = p.begin_sleep(100) + con.close(Ice.ConnectionClose.CloseForcefully) + r.waitForCompleted() + try: + r.throwLocalException() + test(False) + except Ice.ConnectionManuallyClosedException, ex: + test(not ex.graceful) + + # + # Remote case: the server closes the connection forcefully. This causes the request to fail + # with a ConnectionLostException. Since the close() operation is not idempotent, the client + # will not retry. + # + try: + p.close(Test.CloseMode.CloseForcefully) + test(False) + except Ice.ConnectionLostException: + # Expected. + pass + + print("ok") + def allTestsFuture(communicator, collocated): sref = "test:default -p 12010" obj = communicator.stringToProxy(sref) @@ -1404,7 +1499,7 @@ def allTestsFuture(communicator, collocated): test(p.opBatchCount() == 0) b1 = p.ice_batchOneway() b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FutureFlushCallback() f = b1.ice_flushBatchRequestsAsync() f.add_sent_callback(cb.sent) @@ -1436,7 +1531,7 @@ def allTestsFuture(communicator, collocated): test(p.opBatchCount() == 0) b1 = Test.TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity()).ice_batchOneway()) b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FutureFlushExCallback() f = b1.ice_getConnection().flushBatchRequestsAsync() f.add_done_callback(cb.exception) @@ -1473,7 +1568,7 @@ def allTestsFuture(communicator, collocated): test(p.opBatchCount() == 0) b1 = Test.TestIntfPrx.uncheckedCast(p.ice_getConnection().createProxy(p.ice_getIdentity()).ice_batchOneway()) b1.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FutureFlushCallback() f = communicator.flushBatchRequestsAsync() f.add_sent_callback(cb.sent) @@ -1517,7 +1612,7 @@ def allTestsFuture(communicator, collocated): b2.ice_getConnection() # Ensure connection is established. b1.opBatch() b2.opBatch() - b1.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FutureFlushCallback() f = communicator.flushBatchRequestsAsync() f.add_sent_callback(cb.sent) @@ -1539,8 +1634,8 @@ def allTestsFuture(communicator, collocated): b2.ice_getConnection() # Ensure connection is established. b1.opBatch() b2.opBatch() - b1.ice_getConnection().close(False) - b2.ice_getConnection().close(False) + b1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) + b2.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) cb = FutureFlushCallback() f = communicator.flushBatchRequestsAsync() f.add_sent_callback(cb.sent) @@ -1723,47 +1818,4 @@ def allTestsFuture(communicator, collocated): print("ok") - if p.ice_getConnection(): - sys.stdout.write("testing close connection with sending queue... ") - sys.stdout.flush() - - if sys.version_info[0] == 2: - b = [chr(random.randint(0, 255)) for x in range(0, 10*1024)] - seq = ''.join(b) - else: - b = [random.randint(0, 255) for x in range(0, 10*1024)] - seq = bytes(b) - - # - # Send multiple opWithPayload, followed by a close and followed by multiple opWithPaylod. - # The goal is to make sure that none of the opWithPayload fail even if the server closes - # the connection gracefully in between. - # - maxQueue = 2 - done = False - while not done and maxQueue < 50: - done = True - p.ice_ping() - results = [] - for i in range(0, maxQueue): - results.append(p.opWithPayloadAsync(seq)) - if not p.closeAsync(False).is_sent(): - for i in range(0, maxQueue): - f = p.opWithPayloadAsync(seq) - results.append(f) - if f.is_sent(): - done = False - maxQueue = maxQueue * 2 - break - else: - maxQueue = maxQueue * 2 - done = False - for f in results: - try: - f.result() - except Ice.LocalException: - test(False) - - print("ok") - p.shutdown() diff --git a/python/test/Ice/ami/Client.py b/python/test/Ice/ami/Client.py index 1966ff4bd6d..6fa2db022ca 100755 --- a/python/test/Ice/ami/Client.py +++ b/python/test/Ice/ami/Client.py @@ -33,6 +33,7 @@ try: initData = Ice.InitializationData() initData.properties = Ice.createProperties(sys.argv) initData.properties.setProperty('Ice.Warn.AMICallback', '0') + initData.properties.setProperty('Ice.Warn.Connections', '0') # # Limit the send buffer size, this test relies on the socket diff --git a/python/test/Ice/ami/Server.py b/python/test/Ice/ami/Server.py index 1872276ecb0..a042727fc01 100755 --- a/python/test/Ice/ami/Server.py +++ b/python/test/Ice/ami/Server.py @@ -44,13 +44,13 @@ try: # # This test kills connections, so we don't want warnings. # - initData.properties.setProperty("Ice.Warn.Connections", "0"); + initData.properties.setProperty("Ice.Warn.Connections", "0") # # Limit the recv buffer size, this test relies on the socket # send() blocking after sending a given amount of data. # - initData.properties.setProperty("Ice.TCP.RcvSize", "50000"); + initData.properties.setProperty("Ice.TCP.RcvSize", "50000") with Ice.initialize(sys.argv, initData) as communicator: status = run(sys.argv, communicator) diff --git a/python/test/Ice/ami/Test.ice b/python/test/Ice/ami/Test.ice index 4bda44b5c41..9787dd9a1fc 100644 --- a/python/test/Ice/ami/Test.ice +++ b/python/test/Ice/ami/Test.ice @@ -19,6 +19,13 @@ exception TestIntfException { }; +enum CloseMode +{ + CloseForcefully, + CloseGracefully, + CloseGracefullyAndWait +}; + interface TestIntf { void op(); @@ -29,7 +36,8 @@ interface TestIntf void opBatch(); int opBatchCount(); bool waitForBatch(int count); - void close(bool force); + void close(CloseMode mode); + void sleep(int ms); void shutdown(); bool supportsFunctionalTests(); diff --git a/python/test/Ice/ami/TestI.py b/python/test/Ice/ami/TestI.py index 3770a2c6101..53585189f29 100644 --- a/python/test/Ice/ami/TestI.py +++ b/python/test/Ice/ami/TestI.py @@ -7,7 +7,7 @@ # # ********************************************************************** -import Ice, Test, threading +import Ice, Test, threading, time class TestIntfI(Test._TestIntfDisp): def __init__(self): @@ -43,8 +43,11 @@ class TestIntfI(Test._TestIntfDisp): self._batchCount = 0 return result - def close(self, force, current=None): - current.con.close(force) + def close(self, mode, current=None): + current.con.close(Ice.ConnectionClose.valueOf(mode.value)) + + def sleep(self, ms, current=None): + time.sleep(ms / 1000.0) def shutdown(self, current=None): current.adapter.getCommunicator().shutdown() diff --git a/python/test/Ice/binding/AllTests.py b/python/test/Ice/binding/AllTests.py index b877fc78ac5..5bf5a02a801 100644 --- a/python/test/Ice/binding/AllTests.py +++ b/python/test/Ice/binding/AllTests.py @@ -110,7 +110,7 @@ def allTests(communicator): name = test1.getAdapterName() if names.count(name) > 0: names.remove(name) - test1.ice_getConnection().close(False) + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) # # Ensure that the proxy correctly caches the connection (we @@ -128,7 +128,7 @@ def allTests(communicator): test(i == nRetry) for a in adapters: - a.getTestIntf().ice_getConnection().close(False) + a.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) # # Deactivate an adapter and ensure that we can still @@ -152,7 +152,7 @@ def allTests(communicator): name = test1.getAdapterName() if names.count(name) > 0: names.remove(name) - test1.ice_getConnection().close(False) + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) # # Deactivate an adapter and ensure that we can still @@ -194,7 +194,7 @@ def allTests(communicator): name = getAdapterNameWithAMI(test1) if names.count(name) > 0: names.remove(name) - test1.ice_getConnection().close(False) + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) # # Ensure that the proxy correctly caches the connection (we @@ -212,7 +212,7 @@ def allTests(communicator): test(i == nRetry) for a in adapters: - a.getTestIntf().ice_getConnection().close(False) + a.getTestIntf().ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) # # Deactivate an adapter and ensure that we can still @@ -236,7 +236,7 @@ def allTests(communicator): name = getAdapterNameWithAMI(test1) if names.count(name) > 0: names.remove(name) - test1.ice_getConnection().close(False) + test1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) # # Deactivate an adapter and ensure that we can still @@ -266,7 +266,7 @@ def allTests(communicator): name = t.getAdapterName() if names.count(name) > 0: names.remove(name) - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) t = Test.TestIntfPrx.uncheckedCast(t.ice_endpointSelection(Ice.EndpointSelectionType.Random)) test(t.ice_getEndpointSelection() == Ice.EndpointSelectionType.Random) @@ -278,7 +278,7 @@ def allTests(communicator): name = t.getAdapterName() if names.count(name) > 0: names.remove(name) - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) deactivate(com, adapters) @@ -337,13 +337,13 @@ def allTests(communicator): while i < nRetry and t.getAdapterName() == "Adapter36": i = i + 1 test(i == nRetry) - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) adapters.append(com.createObjectAdapter("Adapter35", endpoints[1].toString())) i = 0 while i < nRetry and t.getAdapterName() == "Adapter35": i = i + 1 test(i == nRetry) - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) adapters.append(com.createObjectAdapter("Adapter34", endpoints[0].toString())) i = 0 while i < nRetry and t.getAdapterName() == "Adapter34": @@ -618,7 +618,7 @@ def allTests(communicator): t = createTestIntfPrx(adapters) for i in range(0, 5): test(t.getAdapterName() == "Adapter82") - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) testSecure = Test.TestIntfPrx.uncheckedCast(t.ice_secure(True)) test(testSecure.ice_isSecure()) @@ -632,13 +632,13 @@ def allTests(communicator): for i in range(0, 5): test(t.getAdapterName() == "Adapter81") - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) com.createObjectAdapter("Adapter83", (t.ice_getEndpoints()[1]).toString()) # Reactive tcp OA. for i in range(0, 5): test(t.getAdapterName() == "Adapter83") - t.ice_getConnection().close(False) + t.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) com.deactivateObjectAdapter(adapters[0]) try: diff --git a/python/test/Ice/location/AllTests.py b/python/test/Ice/location/AllTests.py index dc0fb5afe5d..205ae91329d 100644 --- a/python/test/Ice/location/AllTests.py +++ b/python/test/Ice/location/AllTests.py @@ -22,8 +22,8 @@ def allTests(communicator, ref): locator = communicator.getDefaultLocator() test(manager) - registry = Test.TestLocatorRegistryPrx.checkedCast(locator.getRegistry()); - test(registry); + registry = Test.TestLocatorRegistryPrx.checkedCast(locator.getRegistry()) + test(registry) sys.stdout.write("testing stringToProxy... ") sys.stdout.flush() @@ -36,34 +36,34 @@ def allTests(communicator, ref): sys.stdout.write("testing ice_locator and ice_getLocator... ") sys.stdout.flush() - test(Ice.proxyIdentityEqual(base.ice_getLocator(), communicator.getDefaultLocator())); - anotherLocator = Ice.LocatorPrx.uncheckedCast(communicator.stringToProxy("anotherLocator")); - base = base.ice_locator(anotherLocator); - test(Ice.proxyIdentityEqual(base.ice_getLocator(), anotherLocator)); - communicator.setDefaultLocator(None); - base = communicator.stringToProxy("test @ TestAdapter"); - test(not base.ice_getLocator()); - base = base.ice_locator(anotherLocator); - test(Ice.proxyIdentityEqual(base.ice_getLocator(), anotherLocator)); - communicator.setDefaultLocator(locator); - base = communicator.stringToProxy("test @ TestAdapter"); - test(Ice.proxyIdentityEqual(base.ice_getLocator(), communicator.getDefaultLocator())); + test(Ice.proxyIdentityEqual(base.ice_getLocator(), communicator.getDefaultLocator())) + anotherLocator = Ice.LocatorPrx.uncheckedCast(communicator.stringToProxy("anotherLocator")) + base = base.ice_locator(anotherLocator) + test(Ice.proxyIdentityEqual(base.ice_getLocator(), anotherLocator)) + communicator.setDefaultLocator(None) + base = communicator.stringToProxy("test @ TestAdapter") + test(not base.ice_getLocator()) + base = base.ice_locator(anotherLocator) + test(Ice.proxyIdentityEqual(base.ice_getLocator(), anotherLocator)) + communicator.setDefaultLocator(locator) + base = communicator.stringToProxy("test @ TestAdapter") + test(Ice.proxyIdentityEqual(base.ice_getLocator(), communicator.getDefaultLocator())) # # We also test ice_router/ice_getRouter (perhaps we should add a # test/Ice/router test?) # - test(not base.ice_getRouter()); - anotherRouter = Ice.RouterPrx.uncheckedCast(communicator.stringToProxy("anotherRouter")); - base = base.ice_router(anotherRouter); - test(Ice.proxyIdentityEqual(base.ice_getRouter(), anotherRouter)); - router = Ice.RouterPrx.uncheckedCast(communicator.stringToProxy("dummyrouter")); - communicator.setDefaultRouter(router); - base = communicator.stringToProxy("test @ TestAdapter"); - test(Ice.proxyIdentityEqual(base.ice_getRouter(), communicator.getDefaultRouter())); - communicator.setDefaultRouter(None); - base = communicator.stringToProxy("test @ TestAdapter"); - test(not base.ice_getRouter()); + test(not base.ice_getRouter()) + anotherRouter = Ice.RouterPrx.uncheckedCast(communicator.stringToProxy("anotherRouter")) + base = base.ice_router(anotherRouter) + test(Ice.proxyIdentityEqual(base.ice_getRouter(), anotherRouter)) + router = Ice.RouterPrx.uncheckedCast(communicator.stringToProxy("dummyrouter")) + communicator.setDefaultRouter(router) + base = communicator.stringToProxy("test @ TestAdapter") + test(Ice.proxyIdentityEqual(base.ice_getRouter(), communicator.getDefaultRouter())) + communicator.setDefaultRouter(None) + base = communicator.stringToProxy("test @ TestAdapter") + test(not base.ice_getRouter()) print("ok") sys.stdout.write("starting server... ") @@ -200,7 +200,7 @@ def allTests(communicator, ref): sys.stdout.flush() hello = Test.HelloPrx.checkedCast(communicator.stringToProxy("hello")) obj.migrateHello() - hello.ice_getConnection().close(False); + hello.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) hello.sayHello() obj.migrateHello() hello.sayHello() @@ -237,21 +237,21 @@ def allTests(communicator, ref): # sys.stdout.write("testing indirect references to collocated objects... ") sys.stdout.flush() - properties = communicator.getProperties(); - properties.setProperty("Ice.PrintAdapterReady", "0"); - adapter = communicator.createObjectAdapterWithEndpoints("Hello", "default"); - adapter.setLocator(locator); + properties = communicator.getProperties() + properties.setProperty("Ice.PrintAdapterReady", "0") + adapter = communicator.createObjectAdapterWithEndpoints("Hello", "default") + adapter.setLocator(locator) assert(adapter.getLocator() == locator) - id = Ice.Identity(); - id.name = Ice.generateUUID(); - registry.addObject(adapter.add(HelloI(), id)); - adapter.activate(); + id = Ice.Identity() + id.name = Ice.generateUUID() + registry.addObject(adapter.add(HelloI(), id)) + adapter.activate() - helloPrx = Test.HelloPrx.checkedCast(communicator.stringToProxy(communicator.identityToString(id))); - test(not helloPrx.ice_getConnection()); + helloPrx = Test.HelloPrx.checkedCast(communicator.stringToProxy(Ice.identityToString(id))) + test(not helloPrx.ice_getConnection()) - adapter.deactivate(); + adapter.deactivate() print("ok") sys.stdout.write("shutdown server manager... ") diff --git a/python/test/Ice/operations/BatchOneways.py b/python/test/Ice/operations/BatchOneways.py index 5878ff7f7af..ea0fc26c29c 100644 --- a/python/test/Ice/operations/BatchOneways.py +++ b/python/test/Ice/operations/BatchOneways.py @@ -82,7 +82,7 @@ def batchOneways(p): batch1.ice_ping() batch2.ice_ping() batch1.ice_flushBatchRequests() - batch1.ice_getConnection().close(False) + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) batch1.ice_ping() batch2.ice_ping() @@ -90,7 +90,7 @@ def batchOneways(p): batch2.ice_getConnection() batch1.ice_ping() - batch1.ice_getConnection().close(False) + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) batch1.ice_ping() batch2.ice_ping() diff --git a/python/test/Ice/operations/BatchOnewaysAMI.py b/python/test/Ice/operations/BatchOnewaysAMI.py index cd53ee94470..f750b3eb02d 100644 --- a/python/test/Ice/operations/BatchOnewaysAMI.py +++ b/python/test/Ice/operations/BatchOnewaysAMI.py @@ -61,14 +61,14 @@ def batchOneways(p): batch1.end_ice_ping(batch1.begin_ice_ping()) batch2.end_ice_ping(batch2.begin_ice_ping()) batch1.end_ice_flushBatchRequests(batch1.begin_ice_flushBatchRequests()) - batch1.ice_getConnection().close(False) + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) batch1.end_ice_ping(batch1.begin_ice_ping()) batch2.end_ice_ping(batch2.begin_ice_ping()) batch1.ice_getConnection() batch2.ice_getConnection() - batch1.ice_getConnection().close(False) + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) batch1.end_ice_ping(batch1.begin_ice_ping()) batch2.end_ice_ping(batch2.begin_ice_ping()) diff --git a/python/test/Ice/operations/BatchOnewaysFuture.py b/python/test/Ice/operations/BatchOnewaysFuture.py index ccf84220aa1..ea1c70bcf94 100644 --- a/python/test/Ice/operations/BatchOnewaysFuture.py +++ b/python/test/Ice/operations/BatchOnewaysFuture.py @@ -63,14 +63,14 @@ def batchOneways(p): batch1.ice_pingAsync() batch2.ice_pingAsync() batch1.ice_flushBatchRequestsAsync().result() - batch1.ice_getConnection().close(False) + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) batch1.ice_pingAsync() batch2.ice_pingAsync() batch1.ice_getConnection() batch2.ice_getConnection() - batch1.ice_getConnection().close(False) + batch1.ice_getConnection().close(Ice.ConnectionClose.CloseGracefullyAndWait) test(not batch1.ice_pingAsync().done()) test(not batch2.ice_pingAsync().done()) diff --git a/python/test/Ice/timeout/AllTests.py b/python/test/Ice/timeout/AllTests.py index 16d3d0484f8..391c6b23ca5 100644 --- a/python/test/Ice/timeout/AllTests.py +++ b/python/test/Ice/timeout/AllTests.py @@ -116,58 +116,58 @@ def allTests(communicator): sys.stdout.write("testing invocation timeout... ") sys.stdout.flush() - connection = obj.ice_getConnection(); - to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(100)); - test(connection == to.ice_getConnection()); + connection = obj.ice_getConnection() + to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(100)) + test(connection == to.ice_getConnection()) try: - to.sleep(750); - test(False); + to.sleep(750) + test(False) except Ice.InvocationTimeoutException: pass - obj.ice_ping(); - to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(500)); - test(connection == to.ice_getConnection()); + obj.ice_ping() + to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(500)) + test(connection == to.ice_getConnection()) try: - to.sleep(250); + to.sleep(250) except Ice.InvocationTimeoutException: - test(False); - test(connection == to.ice_getConnection()); + test(False) + test(connection == to.ice_getConnection()) # # # # Expect InvocationTimeoutException. # # - # to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(250)); - # cb = new Callback(); - # to.begin_sleep(750, newCallback_Timeout_sleep(cb, &Callback.responseEx, &Callback.exceptionEx)); - # cb.check(); + # to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(250)) + # cb = new Callback() + # to.begin_sleep(750, newCallback_Timeout_sleep(cb, &Callback.responseEx, &Callback.exceptionEx)) + # cb.check() # # # # Expect success. # # - # to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(500)); - # cb = new Callback(); - # to.begin_sleep(250, newCallback_Timeout_sleep(cb, &Callback.response, &Callback.exception)); - # cb.check(); + # to = Test.TimeoutPrx.uncheckedCast(obj.ice_invocationTimeout(500)) + # cb = new Callback() + # to.begin_sleep(250, newCallback_Timeout_sleep(cb, &Callback.response, &Callback.exception)) + # cb.check() print("ok") sys.stdout.write("testing close timeout... ") sys.stdout.flush() - to = Test.TimeoutPrx.checkedCast(obj.ice_timeout(100)); - connection = to.ice_getConnection(); - timeout.holdAdapter(500); - connection.close(False); + to = Test.TimeoutPrx.checkedCast(obj.ice_timeout(100)) + connection = to.ice_getConnection() + timeout.holdAdapter(500) + connection.close(Ice.ConnectionClose.CloseGracefullyAndWait) try: connection.getInfo(); # getInfo() doesn't throw in the closing state. except Ice.LocalException: - test(False); - time.sleep(0.5); + test(False) + time.sleep(0.5) try: - connection.getInfo(); - test(False); - except Ice.CloseConnectionException: + connection.getInfo() + test(False) + except Ice.ConnectionManuallyClosedException, ex: # Expected. - pass - timeout.op(); # Ensure adapter is active. + test(ex.graceful) + timeout.op() # Ensure adapter is active. print("ok") sys.stdout.write("testing timeout overrides... ") @@ -193,7 +193,7 @@ def allTests(communicator): # timeout.op() # Ensure adapter is active. to = Test.TimeoutPrx.checkedCast(to.ice_timeout(1000)) - timeout.holdAdapter(500); + timeout.holdAdapter(500) try: to.sendData(seq) test(False) @@ -229,9 +229,9 @@ def allTests(communicator): # Verify that timeout set via ice_timeout() is still used for requests. # timeout.op() # Ensure adapter is active. - to = Test.TimeoutPrx.uncheckedCast(to.ice_timeout(250)); + to = Test.TimeoutPrx.uncheckedCast(to.ice_timeout(250)) to.ice_getConnection(); # Establish connection - timeout.holdAdapter(750); + timeout.holdAdapter(750) try: to.sendData(seq) test(False) @@ -246,11 +246,11 @@ def allTests(communicator): initData.properties = communicator.getProperties().clone() initData.properties.setProperty("Ice.Override.CloseTimeout", "100") comm = Ice.initialize(initData) - connection = comm.stringToProxy(sref).ice_getConnection(); - timeout.holdAdapter(800); - now = time.clock(); - comm.destroy(); - test((time.clock() - now) < 0.7); + connection = comm.stringToProxy(sref).ice_getConnection() + timeout.holdAdapter(800) + now = time.clock() + comm.destroy() + test((time.clock() - now) < 0.7) print("ok") diff --git a/ruby/src/IceRuby/Connection.cpp b/ruby/src/IceRuby/Connection.cpp index 6b4b57efdc7..2c5f65a8434 100644 --- a/ruby/src/IceRuby/Connection.cpp +++ b/ruby/src/IceRuby/Connection.cpp @@ -46,14 +46,23 @@ IceRuby::createConnection(const Ice::ConnectionPtr& p) extern "C" VALUE -IceRuby_Connection_close(VALUE self, VALUE b) +IceRuby_Connection_close(VALUE self, VALUE mode) { ICE_RUBY_TRY { Ice::ConnectionPtr* p = reinterpret_cast<Ice::ConnectionPtr*>(DATA_PTR(self)); assert(p); - (*p)->close(RTEST(b)); + volatile VALUE type = callRuby(rb_path2class, "Ice::ConnectionClose"); + if(callRuby(rb_obj_is_instance_of, mode, type) != Qtrue) + { + throw RubyException(rb_eTypeError, + "value for 'mode' argument must be an enumerator of Ice.ConnectionClose"); + } + volatile VALUE modeValue = callRuby(rb_funcall, mode, rb_intern("to_i"), 0); + assert(TYPE(modeValue) == T_FIXNUM); + Ice::ConnectionClose cc = static_cast<Ice::ConnectionClose>(FIX2LONG(modeValue)); + (*p)->close(cc); } ICE_RUBY_CATCH return Qnil; @@ -76,6 +85,21 @@ IceRuby_Connection_flushBatchRequests(VALUE self) extern "C" VALUE +IceRuby_Connection_heartbeat(VALUE self) +{ + ICE_RUBY_TRY + { + Ice::ConnectionPtr* p = reinterpret_cast<Ice::ConnectionPtr*>(DATA_PTR(self)); + assert(p); + + (*p)->heartbeat(); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE IceRuby_Connection_setACM(VALUE self, VALUE t, VALUE c, VALUE h) { ICE_RUBY_TRY @@ -221,6 +245,7 @@ IceRuby_Connection_getEndpoint(VALUE self) ICE_RUBY_CATCH return Qnil; } + extern "C" VALUE IceRuby_Connection_setBufferSize(VALUE self, VALUE r, VALUE s) @@ -241,6 +266,21 @@ IceRuby_Connection_setBufferSize(VALUE self, VALUE r, VALUE s) extern "C" VALUE +IceRuby_Connection_throwException(VALUE self) +{ + ICE_RUBY_TRY + { + Ice::ConnectionPtr* p = reinterpret_cast<Ice::ConnectionPtr*>(DATA_PTR(self)); + assert(p); + + (*p)->throwException(); + } + ICE_RUBY_CATCH + return Qnil; +} + +extern "C" +VALUE IceRuby_Connection_toString(VALUE self) { ICE_RUBY_TRY @@ -377,6 +417,7 @@ IceRuby::initConnection(VALUE iceModule) // rb_define_method(_connectionClass, "close", CAST_METHOD(IceRuby_Connection_close), 1); rb_define_method(_connectionClass, "flushBatchRequests", CAST_METHOD(IceRuby_Connection_flushBatchRequests), 0); + rb_define_method(_connectionClass, "heartbeat", CAST_METHOD(IceRuby_Connection_heartbeat), 0); rb_define_method(_connectionClass, "setACM", CAST_METHOD(IceRuby_Connection_setACM), 3); rb_define_method(_connectionClass, "getACM", CAST_METHOD(IceRuby_Connection_getACM), 0); rb_define_method(_connectionClass, "type", CAST_METHOD(IceRuby_Connection_type), 0); @@ -384,6 +425,7 @@ IceRuby::initConnection(VALUE iceModule) rb_define_method(_connectionClass, "getInfo", CAST_METHOD(IceRuby_Connection_getInfo), 0); rb_define_method(_connectionClass, "getEndpoint", CAST_METHOD(IceRuby_Connection_getEndpoint), 0); rb_define_method(_connectionClass, "setBufferSize", CAST_METHOD(IceRuby_Connection_setBufferSize), 2); + rb_define_method(_connectionClass, "throwException", CAST_METHOD(IceRuby_Connection_throwException), 0); rb_define_method(_connectionClass, "toString", CAST_METHOD(IceRuby_Connection_toString), 0); rb_define_method(_connectionClass, "to_s", CAST_METHOD(IceRuby_Connection_toString), 0); rb_define_method(_connectionClass, "inspect", CAST_METHOD(IceRuby_Connection_toString), 0); diff --git a/ruby/src/IceRuby/Util.cpp b/ruby/src/IceRuby/Util.cpp index 7fa1222fb06..84708f2dfb8 100644 --- a/ruby/src/IceRuby/Util.cpp +++ b/ruby/src/IceRuby/Util.cpp @@ -732,6 +732,10 @@ setExceptionMembers(const Ice::LocalException& ex, VALUE p) volatile VALUE v = createString(e.reason); callRuby(rb_iv_set, p, "@reason", v); } + catch(const Ice::ConnectionManuallyClosedException& e) + { + callRuby(rb_iv_set, p, "@graceful", e.graceful ? Qtrue : Qfalse); + } catch(const Ice::LocalException&) { // diff --git a/ruby/test/Ice/acm/AllTests.rb b/ruby/test/Ice/acm/AllTests.rb index 5c6b5e64f66..a147c616693 100644 --- a/ruby/test/Ice/acm/AllTests.rb +++ b/ruby/test/Ice/acm/AllTests.rb @@ -7,13 +7,10 @@ # # ********************************************************************** -def allTests(communicator) +def testSetACM(communicator, com) print "testing setACM/getACM... " STDOUT.flush - ref = "communicator:default -p 12010" - com = Test::RemoteCommunicatorPrx::uncheckedCast(communicator.stringToProxy(ref)) - adapter = com.createObjectAdapter(-1, -1, -1) initData = Ice::InitializationData.new @@ -44,14 +41,49 @@ def allTests(communicator) test(acm.close == Ice::ACMClose::CloseOnInvocationAndIdle) test(acm.heartbeat == Ice::ACMHeartbeat::HeartbeatAlways) - proxy.waitForHeartbeat(2) + proxy.startHeartbeatCount() + proxy.waitForHeartbeatCount(2) adapter.deactivate() testCommunicator.destroy() puts "ok" +end - print "shutting down... " +def testHeartbeatManual(communicator, com) + print "testing manual heartbeats... " STDOUT.flush - com.shutdown() + + adapter = com.createObjectAdapter(10, -1, 0) + + initData = Ice::InitializationData.new + initData.properties = communicator.getProperties().clone() + initData.properties.setProperty("Ice.ACM.Timeout", "10") + initData.properties.setProperty("Ice.ACM.Client.Timeout", "10") + initData.properties.setProperty("Ice.ACM.Client.Close", "0") + initData.properties.setProperty("Ice.ACM.Client.Heartbeat", "0") + testCommunicator = Ice::initialize(initData) + proxy = Test::TestIntfPrx::uncheckedCast(testCommunicator.stringToProxy(adapter.getTestIntf().ice_toString())) + con = proxy.ice_getConnection() + + proxy.startHeartbeatCount() + con.heartbeat() + con.heartbeat() + con.heartbeat() + con.heartbeat() + con.heartbeat() + proxy.waitForHeartbeatCount(5) + + adapter.deactivate() + testCommunicator.destroy() puts "ok" end + +def allTests(communicator) + ref = "communicator:default -p 12010" + com = Test::RemoteCommunicatorPrx::uncheckedCast(communicator.stringToProxy(ref)) + + testSetACM(communicator, com) + testHeartbeatManual(communicator, com) + + com.shutdown() +end diff --git a/ruby/test/Ice/acm/Test.ice b/ruby/test/Ice/acm/Test.ice index 5ab98180dd3..d78abd6eb0f 100644 --- a/ruby/test/Ice/acm/Test.ice +++ b/ruby/test/Ice/acm/Test.ice @@ -17,7 +17,8 @@ interface TestIntf void sleep(int seconds); void sleepAndHold(int seconds); void interruptSleep(); - void waitForHeartbeat(int count); + void startHeartbeatCount(); + void waitForHeartbeatCount(int count); }; interface RemoteObjectAdapter diff --git a/ruby/test/Ice/binding/AllTests.rb b/ruby/test/Ice/binding/AllTests.rb index f4c32a3a999..605063c96e4 100644 --- a/ruby/test/Ice/binding/AllTests.rb +++ b/ruby/test/Ice/binding/AllTests.rb @@ -92,7 +92,7 @@ def allTests(communicator) if names.include?(name) names.delete(name) end - test1.ice_getConnection().close(false) + test1.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end # @@ -113,7 +113,7 @@ def allTests(communicator) test(i == nRetry) for a in adapters - a.getTestIntf().ice_getConnection().close(false) + a.getTestIntf().ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end # @@ -139,7 +139,7 @@ def allTests(communicator) if names.include?(name) names.delete(name) end - test1.ice_getConnection().close(false) + test1.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end # @@ -171,7 +171,7 @@ def allTests(communicator) if names.include?(name) names.delete(name) end - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end t = Test::TestIntfPrx::uncheckedCast(t.ice_endpointSelection(Ice::EndpointSelectionType::Random)) @@ -185,7 +185,7 @@ def allTests(communicator) if names.include?(name) names.delete(name) end - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end deactivate(com, adapters) @@ -250,14 +250,14 @@ def allTests(communicator) i = i + 1 end test(i == nRetry) - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) adapters.push(com.createObjectAdapter("Adapter35", endpoints[1].toString())) i = 0 while i < nRetry and t.getAdapterName() == "Adapter35" i = i + 1 end test(i == nRetry) - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) adapters.push(com.createObjectAdapter("Adapter34", endpoints[0].toString())) i = 0 while i < nRetry and t.getAdapterName() == "Adapter34" @@ -442,7 +442,7 @@ def allTests(communicator) t = createTestIntfPrx(adapters) for i in 0...5 test(t.getAdapterName() == "Adapter82") - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end testSecure = Test::TestIntfPrx::uncheckedCast(t.ice_secure(true)) @@ -457,14 +457,14 @@ def allTests(communicator) for i in 0...5 test(t.getAdapterName() == "Adapter81") - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end com.createObjectAdapter("Adapter83", (t.ice_getEndpoints()[1]).toString()) # Reactive tcp OA. for i in 0...5 test(t.getAdapterName() == "Adapter83") - t.ice_getConnection().close(false) + t.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) end com.deactivateObjectAdapter(adapters[0]) diff --git a/ruby/test/Ice/operations/BatchOneways.rb b/ruby/test/Ice/operations/BatchOneways.rb index 042899d3d6b..99b2225f61b 100644 --- a/ruby/test/Ice/operations/BatchOneways.rb +++ b/ruby/test/Ice/operations/BatchOneways.rb @@ -33,7 +33,7 @@ def batchOneways(p) batch.ice_ping() batch2.ice_ping() batch.ice_flushBatchRequests() - batch.ice_getConnection().close(false) + batch.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) batch.ice_ping() batch2.ice_ping() @@ -41,7 +41,7 @@ def batchOneways(p) batch2.ice_getConnection() batch.ice_ping() - batch.ice_getConnection().close(false) + batch.ice_getConnection().close(Ice::ConnectionClose::CloseGracefullyAndWait) batch.ice_ping() batch2.ice_ping() diff --git a/ruby/test/Ice/timeout/AllTests.rb b/ruby/test/Ice/timeout/AllTests.rb index 05746528fc6..6c9ccc7ea55 100644 --- a/ruby/test/Ice/timeout/AllTests.rb +++ b/ruby/test/Ice/timeout/AllTests.rb @@ -98,7 +98,7 @@ def allTests(communicator) to = Test::TimeoutPrx.checkedCast(obj.ice_timeout(100)) connection = to.ice_getConnection() timeout.holdAdapter(500) - connection.close(false) + connection.close(Ice::ConnectionClose::CloseGracefullyAndWait) begin connection.getInfo() # getInfo() doesn't throw in the closing state. rescue Ice::LocalException @@ -108,8 +108,9 @@ def allTests(communicator) begin connection.getInfo() test(false) - rescue Ice::CloseConnectionException + rescue Ice::ConnectionManuallyClosedException => ex # Expected. + test(ex.graceful) end timeout.op() # Ensure adapter is active. puts "ok" diff --git a/scripts/tests/Ice/ami.py b/scripts/tests/Ice/ami.py index 6e5abe02f7f..7ecc8fd7c5f 100644 --- a/scripts/tests/Ice/ami.py +++ b/scripts/tests/Ice/ami.py @@ -8,4 +8,4 @@ # # ********************************************************************** -TestSuite(__name__, options = { "compress" : [False] })
\ No newline at end of file +TestSuite(__name__, options = { "compress" : [False], "serialize" : [False] }) diff --git a/slice/Ice/Connection.ice b/slice/Ice/Connection.ice index e87f8527958..af7b03a5a38 100644 --- a/slice/Ice/Connection.ice +++ b/slice/Ice/Connection.ice @@ -77,7 +77,8 @@ local interface CloseCallback /** * * This method is called by the the connection when the connection - * is closed. + * is closed. If the callback needs more information about the closure, + * it can call {@link Connection#throwException}. * * @param con The connection that closed. **/ @@ -132,6 +133,27 @@ local struct ACM }; /** + * Determines the behavior when manually closing a connection. + **/ +["cpp:unscoped"] +local enum ConnectionClose +{ + /** + * Close the connection immediately without sending a close connection protocol message to the peer + * and waiting for the peer to acknowledge it. + **/ + CloseForcefully, + /** + * Close the connection by notifying the peer but do not wait for pending invocations to complete. + **/ + CloseGracefully, + /** + * Wait for all pending invocations to complete before closing the connection. + **/ + CloseGracefullyAndWait +}; + +/** * * The user-level interface to a connection. * @@ -141,17 +163,13 @@ local interface Connection { /** * - * Close a connection, either gracefully or forcefully. If a - * connection is closed forcefully, it closes immediately, without - * sending the relevant close connection protocol messages to the - * peer and waiting for the peer to acknowledge these protocol - * messages. + * Manually close the connection using the specified closure mode. * - * @param force If true, close forcefully. Otherwise the - * connection is closed gracefully. + * @param mode Determines how the connection will be closed. * + * @see ConnectionClose **/ - void close(bool force); + void close(ConnectionClose mode); /** * @@ -225,18 +243,19 @@ local interface Connection /** * - * Set callback on the connection. The callback is called by the + * Set a close callback on the connection. The callback is called by the * connection when it's closed. The callback is called from the - * Ice thread pool associated with the connection. + * Ice thread pool associated with the connection. If the callback needs + * more information about the closure, it can call {@link Connection#throwException}. * - * @param callback The closed callback object. + * @param callback The close callback object. * **/ void setCloseCallback(CloseCallback callback); /** * - * Set callback on the connection. The callback is called by the + * Set a heartbeat callback on the connection. The callback is called by the * connection when a heartbeat is received. The callback is called * from the Ice thread pool associated with the connection. * @@ -247,6 +266,13 @@ local interface Connection /** * + * Send a heartbeat message. + * + **/ + ["async-oneway"] void heartbeat(); + + /** + * * Set the active connection management parameters. * * @param timeout The timeout value in milliseconds. @@ -307,7 +333,6 @@ local interface Connection **/ ["cpp:const"] ConnectionInfo getInfo(); - /** * * Set the connection buffer receive/send size. @@ -317,6 +342,17 @@ local interface Connection * **/ void setBufferSize(int rcvSize, int sndSize); + + /** + * + * Throw an exception indicating the reason for connection closure. For example, + * {@link CloseConnectionException} is raised if the connection was closed gracefully, + * whereas {@link ConnectionManuallyClosedException} is raised if the connection was + * manually closed by the application. This operation does nothing if the connection is + * not yet closed. + * + **/ + ["cpp:const"] void throwException(); }; /** diff --git a/slice/Ice/LocalException.ice b/slice/Ice/LocalException.ice index 374a84d9e4c..e0a77945bf5 100644 --- a/slice/Ice/LocalException.ice +++ b/slice/Ice/LocalException.ice @@ -814,14 +814,16 @@ local exception CloseConnectionException extends ProtocolException /** * * This exception is raised by an operation call if the application - * forcefully closes the connection {@link Connection#close}. + * closes the connection locally using {@link Connection#close}. * * @see Connection#close * **/ ["cpp:ice_print"] -local exception ForcedCloseConnectionException extends ProtocolException +local exception ConnectionManuallyClosedException { + /** True if the connection was closed gracefully, false otherwise. **/ + bool graceful; }; /** @@ -1032,5 +1034,4 @@ local exception ResponseSentException { }; - }; |