diff options
Diffstat (limited to 'ACE/TAO/tao/Synch_Invocation.cpp')
-rw-r--r-- | ACE/TAO/tao/Synch_Invocation.cpp | 762 |
1 files changed, 762 insertions, 0 deletions
diff --git a/ACE/TAO/tao/Synch_Invocation.cpp b/ACE/TAO/tao/Synch_Invocation.cpp new file mode 100644 index 00000000000..7e3009faa5e --- /dev/null +++ b/ACE/TAO/tao/Synch_Invocation.cpp @@ -0,0 +1,762 @@ +// $Id$ + +#include "tao/Synch_Invocation.h" +#include "tao/Profile_Transport_Resolver.h" +#include "tao/Profile.h" +#include "tao/Synch_Reply_Dispatcher.h" +#include "tao/Transport.h" +#include "tao/Stub.h" +#include "tao/Bind_Dispatcher_Guard.h" +#include "tao/operation_details.h" +#include "tao/Wait_Strategy.h" +#include "tao/debug.h" +#include "tao/ORB_Constants.h" +#include "tao/Messaging_SyncScopeC.h" +#include "tao/ORB_Core.h" +#include "tao/Service_Context.h" +#include "tao/SystemException.h" +#include "ace/Intrusive_Auto_Ptr.h" + +#if TAO_HAS_INTERCEPTORS == 1 +# include "tao/PortableInterceptorC.h" +#endif /*TAO_HAS_INTERCEPTORS */ + +#include "ace/Auto_Ptr.h" +#include "ace/OS_NS_string.h" +#include "ace/Countdown_Time.h" + +#if !defined (__ACE_INLINE__) +# include "tao/Synch_Invocation.inl" +#endif /* __ACE_INLINE__ */ + + +ACE_RCSID (tao, + Synch_Invocation, + "$Id$") + + +TAO_BEGIN_VERSIONED_NAMESPACE_DECL + +namespace TAO +{ + Synch_Twoway_Invocation::Synch_Twoway_Invocation ( + CORBA::Object_ptr otarget, + Profile_Transport_Resolver &resolver, + TAO_Operation_Details &detail, + bool response_expected) + : Remote_Invocation (otarget, + resolver, + detail, + response_expected) + { + } + + Invocation_Status + Synch_Twoway_Invocation::remote_twoway (ACE_Time_Value *max_wait_time) + { + ACE_Countdown_Time countdown (max_wait_time); + + TAO_Synch_Reply_Dispatcher *rd_p = 0; + ACE_NEW_NORETURN (rd_p, TAO_Synch_Reply_Dispatcher (this->resolver_.stub ()->orb_core (), + this->details_.reply_service_info ())); + if (!rd_p) + { + throw ::CORBA::NO_MEMORY (); + } + + ACE_Intrusive_Auto_Ptr<TAO_Synch_Reply_Dispatcher> rd(rd_p, false); + + Invocation_Status s = TAO_INVOKE_FAILURE; + +#if TAO_HAS_INTERCEPTORS == 1 + // Start the interception point here.. + s = this->send_request_interception (); + + if (s != TAO_INVOKE_SUCCESS) + return s; + + // We have started the interception flow. We need to call the + // ending interception flow if things go wrong. The purpose of the + // try block is to do just this. + try + { +#endif /*TAO_HAS_INTERCEPTORS */ + TAO_Transport* const transport = this->resolver_.transport (); + + if (!transport) + { + // Way back, we failed to find a profile we could connect to. + // We've come this far only so we reach the interception points + // in case they can fix things. Time to bail.... + throw CORBA::TRANSIENT (CORBA::OMGVMCID | 2, CORBA::COMPLETED_NO); + } + + ACE_GUARD_RETURN (TAO_SYNCH_MUTEX, ace_mon, + transport->output_cdr_lock (), TAO_INVOKE_FAILURE); + + TAO_OutputCDR &cdr = transport->out_stream (); + + cdr.message_attributes (this->details_.request_id (), + this->resolver_.stub (), + TAO_TWOWAY_REQUEST, + max_wait_time); + + this->write_header (cdr); + + this->marshal_data (cdr); + + // Register a reply dispatcher for this invocation. Use the + // preallocated reply dispatcher. + TAO_Bind_Dispatcher_Guard dispatch_guard ( + this->details_.request_id (), + rd.get (), + transport->tms ()); + + if (dispatch_guard.status () != 0) + { + // @@ What is the right way to handle this error? Why should + // we close the connection? + transport->close_connection (); + + throw ::CORBA::INTERNAL (0, CORBA::COMPLETED_NO); + } + + countdown.update (); + + s = this->send_message (cdr, + TAO_TWOWAY_REQUEST, + max_wait_time); + + ace_mon.release(); + +#if TAO_HAS_INTERCEPTORS == 1 + // @@NOTE: Too much code repetition. + // If the above call returns a restart due to connection + // failure then call the receive_other interception point + // before we leave. + if (s == TAO_INVOKE_RESTART) + { + Invocation_Status const tmp = this->receive_other_interception (); + + if (tmp != TAO_INVOKE_SUCCESS) + s = tmp; + } +#endif /* TAO_HAS_INTERCEPTORS */ + + if (s != TAO_INVOKE_SUCCESS) + return s; + + countdown.update (); + + // For some strategies one may want to release the transport + // back to cache. If the idling is successfull let the + // resolver about that. + if (transport->idle_after_send ()) + this->resolver_.transport_released (); + + // @@ In all MT environments, there's a cancellation point lurking + // here; need to investigate. Client threads would frequently be + // canceled sometime during recv_request ... the correct action to + // take on being canceled is to issue a CancelRequest message to the + // server and then imediately let other client-side cancellation + // handlers do their jobs. + // + // In C++, that basically means to unwind the stack using almost + // normal procedures: all destructors should fire, and some "catch" + // blocks should probably be able to handle things like releasing + // pointers. (Without unwinding the C++ stack, resources that must + // be freed by thread cancellation won't be freed, and the process + // won't continue to function correctly.) The tricky part is that + // according to POSIX, all C stack frames must also have their + // (explicitly coded) handlers called. We assume a POSIX.1c/C/C++ + // environment. + + s = this->wait_for_reply (max_wait_time, *rd.get (), dispatch_guard); + +#if TAO_HAS_INTERCEPTORS == 1 + if (s == TAO_INVOKE_RESTART) + { + Invocation_Status const tmp = this->receive_other_interception (); + + // Push the latest values for the return.. + if (tmp != TAO_INVOKE_SUCCESS) + s = tmp; + } +#endif /* TAO_HAS_INTERCEPTORS */ + + if (s != TAO_INVOKE_SUCCESS) + return s; + + // What happens when the above call returns an error through + // the return value? That would be bogus as per the contract + // in the interface. The call violated the contract + s = this->check_reply_status (*rd.get ()); + + // For some strategies one may want to release the transport + // back to cache after receiving the reply. + if (transport->idle_after_reply ()) + this->resolver_.transport_released (); + +#if TAO_HAS_INTERCEPTORS == 1 + Invocation_Status tmp = TAO_INVOKE_FAILURE; + if (s == TAO_INVOKE_RESTART) + { + tmp = this->receive_other_interception (); + } + else if (s == TAO_INVOKE_SUCCESS) + { + tmp = this->receive_reply_interception (); + } + if (tmp != TAO_INVOKE_SUCCESS) + s = tmp; + } + catch ( ::CORBA::Exception& ex) + { + PortableInterceptor::ReplyStatus const status = + this->handle_any_exception (&ex); + + if (status == PortableInterceptor::LOCATION_FORWARD || + status == PortableInterceptor::TRANSPORT_RETRY) + s = TAO_INVOKE_RESTART; + else if (status == PortableInterceptor::SYSTEM_EXCEPTION + || status == PortableInterceptor::USER_EXCEPTION) + throw; + } + catch (...) + { + // Notify interceptors of non-CORBA exception, and propagate + // that exception to the caller. + + PortableInterceptor::ReplyStatus const st = + this->handle_all_exception (); + + if (st == PortableInterceptor::LOCATION_FORWARD || + st == PortableInterceptor::TRANSPORT_RETRY) + s = TAO_INVOKE_RESTART; + else + throw; + } +#endif /* TAO_HAS_INTERCEPTORS */ + + return s; + } + + Invocation_Status + Synch_Twoway_Invocation::wait_for_reply (ACE_Time_Value *max_wait_time, + TAO_Synch_Reply_Dispatcher &rd, + TAO_Bind_Dispatcher_Guard &bd) + { + /* + * Precondition: The call went to the remote + * peer. <ACE_Thread::self> is waiting for the reply. + * + * Postcondition: Any error during a wait is marked by raising an + * exception. Success alone is returned through the return value. + */ + + bool const + expired= (max_wait_time && ACE_Time_Value::zero == *max_wait_time); + if (expired) + errno= ETIME; + int const + reply_error = expired ? -1 : + this->resolver_.transport ()->wait_strategy ()->wait (max_wait_time, rd); + + if (TAO_debug_level > 0 && max_wait_time) + { + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Twoway_Invocation::wait_for_reply, " + "timeout after recv is <%u> status <%d>\n", + max_wait_time->msec (), + reply_error)); + } + + // Check the reply error. + if (reply_error == -1) + { + // Unbind the dispatcher, since its of no use at this point of + // time + if (TAO_debug_level > 3) + { + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Twoway_Invocation::wait_for_reply, " + "recovering after an error\n")); + } + + // You the smarty, don't try to moving the unbind_dispatcher + // () call since it looks like it is repeated twice. That + // could land you in trouble. If you don't believe this + // warning go ahead and try. Try running tests to see what is + // going on ;) + if (errno == ETIME) + { + // If the unbind succeeds then thrown an exception to the + // application, else just collect the reply and dispatch + // that to the application. + // + // NOTE: A fragile synchronization is provided when using + // the Muxed Transport strategy. We could infact be a + // follower thread getting timedout in the LF whereas the + // dispatching thread could be on the reply_dispatcher + // that we created. This would lead bad crashes. To get + // around that, the call to unbind_dispatcher () will wait + // on the lock on the Muxed_Transport_Strategy if + // dispatching has started. This is fragile. + if (bd.unbind_dispatcher () == 0) + { + // Just a timeout with completed_maybe, don't close + // the connection or anything + throw ::CORBA::TIMEOUT ( + CORBA::SystemException::_tao_minor_code ( + TAO_TIMEOUT_RECV_MINOR_CODE, + errno), + CORBA::COMPLETED_MAYBE); + } + } + else + { + (void) bd.unbind_dispatcher (); + this->resolver_.transport ()->close_connection (); + + try + { + return + this->stub()->orb_core ()->service_raise_comm_failure ( + this->details_.request_service_context ().service_info (), + this->resolver_.profile ()); + + } + catch (const ::CORBA::Exception&) + { + this->resolver_.stub ()->reset_profiles (); + throw; + } + } + } + + return TAO_INVOKE_SUCCESS; + } + + Invocation_Status + Synch_Twoway_Invocation::check_reply_status (TAO_Synch_Reply_Dispatcher &rd) + { + /* + * Precondition: We probably got a reply. <ACE_Thread::self> is + * checking the status of the reply + * + * Postcondition: Any error while reading the reply is marked by + * raising an exception. LOCATION_FORWARDED replies are marked by + * returning a restart since that is what needed to be done by the + * callee. + */ + TAO_InputCDR &cdr = rd.reply_cdr (); + + // Set the translators + this->resolver_.transport ()->assign_translators (&cdr, 0); + + // At this point it can be assumed that the GIOP/whatever protocol + // header and the reply header are already handled. Further it + // can be assumed that the reply body contains the details + // required for further processing. All the other details should + // have been handled in the reply dispatcher/protocol framework. + switch (rd.reply_status ()) + { + case GIOP::NO_EXCEPTION: + { + Reply_Guard mon (this, TAO_INVOKE_FAILURE); + if (this->details_.demarshal_args (cdr) == false) + { + throw ::CORBA::MARSHAL (); + } + + mon.set_status (TAO_INVOKE_SUCCESS); + } + break; + case GIOP::LOCATION_FORWARD: + return this->location_forward (cdr); + case GIOP::LOCATION_FORWARD_PERM: + { + // Unmarshal the location forward object and set the + // variable this->forward_to_. + Invocation_Status const s = this->location_forward (cdr); + if (s != TAO_INVOKE_FAILURE) + { + // de-marshalling of permanent object reference was successfull + CORBA::Boolean const permanent_forward_condition = + this->stub ()->orb_core ()->is_permanent_forward_condition + (this->forwarded_to_.in (), + this->request_service_context ()); + + if (!permanent_forward_condition) + { + // permanent condition not given + if (TAO_debug_level > 3) + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Twoway_Invocation::" + "check_reply_status: unexpected LOCATION_FORWARD_PERM reply\n")); + + throw ::CORBA::INTERNAL (0, CORBA::COMPLETED_NO); + } + + // This is the only place where we set in permanent forward state. + this->reply_status (GIOP::LOCATION_FORWARD_PERM); + } + + return s; + } + case GIOP::USER_EXCEPTION: + return this->handle_user_exception (cdr); + case GIOP::SYSTEM_EXCEPTION: + return this->handle_system_exception (cdr); + + case GIOP::NEEDS_ADDRESSING_MODE: + { + Reply_Guard mon (this, TAO_INVOKE_FAILURE); + // We have received a message with a request to change the + // addressing mode. First let us read the mode that the + // server/agent asks for. + CORBA::Short addr_mode = 0; + + if (cdr.read_short (addr_mode) == 0) + { + // Could not demarshal the addressing disposition, raise an local + // CORBA::MARSHAL + throw ::CORBA::MARSHAL (0, CORBA::COMPLETED_MAYBE); + } + + // Now set this addressing mode in the profile, so that + // the next invocation need not go through this. + this->resolver_.profile ()->addressing_mode (addr_mode); + + mon.set_status (TAO_INVOKE_RESTART); + + // Now restart the invocation + return TAO_INVOKE_RESTART; + } + } + return TAO_INVOKE_SUCCESS; + } + + Invocation_Status + Synch_Twoway_Invocation::location_forward (TAO_InputCDR &inp_stream) + { + Reply_Guard mon (this, TAO_INVOKE_FAILURE); + + if (TAO_debug_level > 3) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("TAO (%P|%t) - Synch_Twoway_Invocation::location_forward ") + ACE_TEXT ("being handled\n"))); + } + + CORBA::Object_var fwd; + + if (!(inp_stream >> fwd)) + { + throw ::CORBA::MARSHAL ( + CORBA::SystemException::_tao_minor_code ( + TAO_INVOCATION_LOCATION_FORWARD_MINOR_CODE, + errno), + CORBA::COMPLETED_NO); + } + + this->forwarded_reference (fwd.in ()); + + mon.set_status (TAO_INVOKE_RESTART); + + return TAO_INVOKE_RESTART; + } + + Invocation_Status + Synch_Twoway_Invocation::handle_user_exception (TAO_InputCDR &cdr) + { + Reply_Guard mon (this, + TAO_INVOKE_FAILURE); + + if (TAO_debug_level > 3) + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Twoway_Invocation::" + "handle_user_exception\n")); + + // Pull the exception from the stream. + CORBA::String_var buf; + + if (!(cdr >> buf.inout ())) + { + // Could not demarshal the exception id, raise an local + // CORBA::MARSHAL + throw ::CORBA::MARSHAL (0, CORBA::COMPLETED_MAYBE); + } + + CORBA::Exception *exception = this->details_.corba_exception (buf.in ()); + + exception->_tao_decode (cdr); + + if (TAO_debug_level > 5) + { + ACE_DEBUG ((LM_DEBUG, + ACE_TEXT ("TAO (%P|%t) - Synch_Twoway_Invocation::") + ACE_TEXT ("handle_user_exception - ") + ACE_TEXT ("raising exception %C\n"), + buf.in ())); + } + + mon.set_status (TAO_INVOKE_USER_EXCEPTION); + + // We must manage the memory allocated + // by the call above to alloc(). + auto_ptr<CORBA::Exception> safety (exception); + + exception->_raise (); + + return TAO_INVOKE_USER_EXCEPTION; + } + + Invocation_Status + Synch_Twoway_Invocation::handle_system_exception (TAO_InputCDR &cdr) + { + Reply_Guard mon (this, TAO_INVOKE_FAILURE); + + if (TAO_debug_level > 3) + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Twoway_Invocation::" + "handle_system_exception\n")); + + CORBA::String_var type_id; + + if (!(cdr >> type_id.inout ())) + { + // Could not demarshal the exception id, raise an local + // CORBA::MARSHAL + throw ::CORBA::MARSHAL (0, CORBA::COMPLETED_MAYBE); + } + + CORBA::ULong minor = 0; + CORBA::ULong completion = 0; + + if (!(cdr >> minor) || !(cdr >> completion)) + { + throw ::CORBA::MARSHAL (0, CORBA::COMPLETED_MAYBE); + } + + bool do_forward = false; + int foe_kind = this->stub ()->orb_core ()->orb_params ()->forward_once_exception(); + + if ((CORBA::CompletionStatus) completion != CORBA::COMPLETED_YES + && (((foe_kind & TAO::FOE_TRANSIENT) == 0 + && ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/TRANSIENT:1.0") == 0) || + ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/OBJ_ADAPTER:1.0") == 0 || + ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/NO_RESPONSE:1.0") == 0 || + ((foe_kind & TAO::FOE_COMM_FAILURE) == 0 + && ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/COMM_FAILURE:1.0") == 0) || + (this->stub ()->orb_core ()->orb_params ()->forward_invocation_on_object_not_exist () + && ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/OBJECT_NOT_EXIST:1.0") == 0) || + (do_forward = ! this->stub ()->forwarded_on_exception () + && ((((foe_kind & TAO::FOE_OBJECT_NOT_EXIST) == TAO::FOE_OBJECT_NOT_EXIST) + && (ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/OBJECT_NOT_EXIST:1.0") == 0)) || + (((foe_kind & TAO::FOE_COMM_FAILURE) == TAO::FOE_COMM_FAILURE) + && (ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/COMM_FAILURE:1.0") == 0)) || + (((foe_kind & TAO::FOE_TRANSIENT) == TAO::FOE_TRANSIENT) + && (ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/TRANSIENT:1.0") == 0)) || + (((foe_kind & TAO::FOE_INV_OBJREF) == TAO::FOE_INV_OBJREF) + && (ACE_OS_String::strcmp (type_id.in (), + "IDL:omg.org/CORBA/INV_OBJREF:1.0") == 0)))))) + { + if (do_forward) + this->stub ()->forwarded_on_exception (true); + + // Start the special case for FTCORBA. + /** + * There has been a unanimous view that this is not the + * right way to do things. But a need to be compliant is + * forcing us into this. + */ + Invocation_Status const s = + this->stub ()->orb_core ()->service_raise_transient_failure ( + this->details_.request_service_context ().service_info (), + this->resolver_.profile ()); + + if (s == TAO_INVOKE_RESTART) + return s; + + // Attempt profile retry. + /** + * @note A location forwarding loop may occur where a client + * is bounced from the original target to the forwarded + * target and back if the application is not equipped to + * handle retries of previously called targets. TAO may + * be able to help in this case but it ultimately ends + * up being an application issue. + */ + if (completion != CORBA::COMPLETED_MAYBE && + this->resolver_.stub ()->next_profile_retry ()) + { + return TAO_INVOKE_RESTART; + } + + // Fall through and raise an exception. + } + + CORBA::SystemException *ex = TAO::create_system_exception (type_id.in ()); + + if (ex == 0) + { + // @@ We should raise a CORBA::NO_MEMORY, but we ran out + // of memory already. We need a pre-allocated, TSS, + // CORBA::NO_MEMORY instance + ACE_NEW_RETURN (ex, + CORBA::UNKNOWN, + TAO_INVOKE_FAILURE); + } + + // Without this, the call to create_system_exception() above + // causes a memory leak. On platforms without native exceptions, + // the CORBA::Environment class manages the memory. + auto_ptr<CORBA::SystemException> safety (ex); + + ex->minor (minor); + ex->completed (CORBA::CompletionStatus (completion)); + + if (TAO_debug_level > 4) + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Twoway_Invocation::" + "handle_system_exception, about to raise\n")); + + mon.set_status (TAO_INVOKE_SYSTEM_EXCEPTION); + + // Raise the exception. + ex->_raise (); + + return TAO_INVOKE_SYSTEM_EXCEPTION; + } + + // ========================================================================= + + Synch_Oneway_Invocation::Synch_Oneway_Invocation ( + CORBA::Object_ptr otarget, + Profile_Transport_Resolver &r, + TAO_Operation_Details &d) + : Synch_Twoway_Invocation (otarget, r, d, false) + { + } + + Invocation_Status + Synch_Oneway_Invocation::remote_oneway (ACE_Time_Value *max_wait_time) + { + ACE_Countdown_Time countdown (max_wait_time); + + CORBA::Octet const response_flags = this->details_.response_flags (); + + Invocation_Status s = TAO_INVOKE_FAILURE; + + if (response_flags == CORBA::Octet (Messaging::SYNC_WITH_SERVER) || + response_flags == CORBA::Octet (Messaging::SYNC_WITH_TARGET)) + { + s = Synch_Twoway_Invocation::remote_twoway (max_wait_time); + + return s; + } + +#if TAO_HAS_INTERCEPTORS == 1 + s = this->send_request_interception (); + + if (s != TAO_INVOKE_SUCCESS) + return s; + + try + { +#endif /*TAO_HAS_INTERCEPTORS */ + TAO_Transport* const transport = this->resolver_.transport (); + + if (!transport) + { + // Way back, we failed to find a profile we could connect to. + // We've come this far only so we reach the interception points + // in case they can fix things. Time to bail.... + throw CORBA::TRANSIENT (CORBA::OMGVMCID | 2, CORBA::COMPLETED_NO); + } + + { + ACE_GUARD_RETURN (TAO_SYNCH_MUTEX, ace_mon, transport->output_cdr_lock (), + TAO_INVOKE_FAILURE); + + TAO_OutputCDR &cdr = transport->out_stream (); + + cdr.message_attributes (this->details_.request_id (), + this->resolver_.stub (), + TAO_ONEWAY_REQUEST, + max_wait_time); + + this->write_header (cdr); + + this->marshal_data (cdr); + + countdown.update (); + + if (transport->is_connected ()) + { + // We have a connected transport so we can send the message + s = this->send_message (cdr, + TAO_ONEWAY_REQUEST, + max_wait_time); + } + else + { + if (TAO_debug_level > 4) + ACE_DEBUG ((LM_DEBUG, + "TAO (%P|%t) - Synch_Oneway_Invocation::" + "remote_oneway, queueing message\n")); + + if (transport->format_queue_message (cdr, + max_wait_time, + this->resolver_.stub()) != 0) + { + s = TAO_INVOKE_FAILURE; + } + } + } + +#if TAO_HAS_INTERCEPTORS == 1 + s = this->receive_other_interception (); + } + catch ( ::CORBA::Exception& ex) + { + PortableInterceptor::ReplyStatus const status = + this->handle_any_exception (&ex); + + if (status == PortableInterceptor::LOCATION_FORWARD || + status == PortableInterceptor::TRANSPORT_RETRY) + s = TAO_INVOKE_RESTART; + else if (status == PortableInterceptor::SYSTEM_EXCEPTION + || status == PortableInterceptor::USER_EXCEPTION) + throw; + } + catch (...) + { + // Notify interceptors of non-CORBA exception, and propagate + // that exception to the caller. + + PortableInterceptor::ReplyStatus const st = + this->handle_all_exception (); + + if (st == PortableInterceptor::LOCATION_FORWARD || + st == PortableInterceptor::TRANSPORT_RETRY) + s = TAO_INVOKE_RESTART; + else + throw; + } +#endif /* TAO_HAS_INTERCEPTORS */ + + return s; + } +} + +TAO_END_VERSIONED_NAMESPACE_DECL |