#include <dg.h>
#include <dgcall.h>
#include <dgccall.h>
#include <dgcct.h>
#include <dgpkt.h>
#include <dgrq.h>
#include <dgxq.h>
#include <dghnd.h>
#include <dgscall.h>
#include <dce/ep.h>
typedef struct {
unsigned32 max_ping_time;
} com_timeout_params_t;
INTERNAL void ccall_common_init (
rpc_dg_binding_client_p_t ,
rpc_dg_ccall_p_t ,
unsigned32 ,
rpc_if_rep_p_t ,
unsigned32 ,
unsigned32 *
);
INTERNAL rpc_dg_ccall_p_t ccall_alloc (
rpc_dg_binding_client_p_t ,
unsigned32 ,
rpc_if_rep_p_t ,
unsigned32 ,
unsigned32 *
);
INTERNAL rpc_dg_ccall_p_t ccall_reinit (
rpc_dg_binding_client_p_t ,
unsigned32 ,
rpc_if_rep_p_t ,
unsigned32 ,
boolean * ,
unsigned32 *
);
INTERNAL void xmit_ping ( rpc_dg_ccall_p_t );
INTERNAL void xmit_orphan_quit ( rpc_dg_ccall_p_t );
INTERNAL void recv_state_timer (rpc_dg_ccall_p_t );
INTERNAL void ccall_orphan_timer (rpc_dg_ccall_p_t );
INTERNAL void ccall_cancel_timer (rpc_dg_ccall_p_t );
INTERNAL void ccall_uncache (rpc_dg_ccall_p_t );
INTERNAL void ccall_binding_serialize (
rpc_dg_binding_client_p_t ,
boolean32 ,
unsigned32 * ,
rpc_clock_p_t ,
unsigned32 *
);
INTERNAL void ccall_initial_cancel_setup (
rpc_dg_ccall_p_t ,
unsigned32 ,
rpc_clock_t
);
INTERNAL void ccall_timeout_timer (
rpc_dg_ccall_p_t
);
PRIVATE void rpc__dg_ccall_lsct_inq_scall
(
rpc_dg_ccall_p_t ccall,
rpc_dg_scall_p_t *scallp
)
{
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
if (ccall->c.is_cbk)
*scallp = NULL;
else
{
*scallp = ccall->cbk_scall;
if (*scallp != NULL)
{
RPC_DG_CALL_LOCK(&(*scallp)->c);
if ((*scallp)->c.call_seq != ccall->c.high_seq)
{
RPC_DG_CALL_UNLOCK(&(*scallp)->c);
*scallp = NULL;
}
}
}
}
PRIVATE void rpc__dg_ccall_lsct_new_call
(
rpc_dg_ccall_p_t ccall,
rpc_dg_sock_pool_elt_p_t si,
rpc_dg_recvq_elt_p_t rqe,
rpc_dg_scall_p_t *scallp
)
{
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
assert(ccall->c.is_cbk == false);
*scallp = ccall->cbk_scall;
if (*scallp != NULL)
rpc__dg_scall_reinit(*scallp, si, rqe);
else
*scallp = rpc__dg_scall_cbk_alloc(ccall, si, rqe);
if (*scallp != NULL)
{
if (RPC_DG_SEQ_IS_LT(rqe->hdrp->seq, ccall->c.high_seq))
{
rpc_dce_svc_printf (
__FILE__, __LINE__,
"%s",
rpc_svc_general,
svc_c_sev_fatal | svc_c_action_abort,
rpc_m_invalid_seqnum,
"rpc__dg_ccall_lsct_new_call" );
}
ccall->c.high_seq = rqe->hdrp->seq;
}
}
INTERNAL void ccall_common_init
(
rpc_dg_binding_client_p_t h,
rpc_dg_ccall_p_t ccall,
unsigned32 options,
rpc_if_rep_p_t ifspec,
unsigned32 opnum,
unsigned32 *st
)
{
boolean doing_callback = ccall->c.is_cbk;
RPC_LOCK_ASSERT(0);
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
if (doing_callback)
RPC_DG_CALL_LOCK_ASSERT(&ccall->cbk_scall->c);
ccall->c.call_opnum = opnum;
ccall->c.xq.base_flags = 0;
ccall->c.xq.base_flags2 = 0;
ccall->response_info_updated = false;
ccall->cbk_start = false;
ccall->reject_status = 0;
ccall->cancel.next_time = 0;
ccall->cancel.server_is_accepting = true;
ccall->cancel.server_had_pending = false;
ccall->cancel.local_count = 0;
ccall->cancel.server_count = 0;
ccall->cancel.timeout_time = 0;
ccall->quit.quack_rcvd = false;
ccall->auth_way_info = NULL;
ccall->auth_way_info_len = 0;
assert(h->c.c.timeout > rpc_c_binding_min_timeout);
assert(h->c.c.timeout <= rpc_c_binding_infinite_timeout);
ccall->c.com_timeout_knob = h->c.c.timeout;
if (h->c.c.call_timeout_time == 0)
{
ccall->timeout_stamp = 0;
}
else
{
ccall->timeout_stamp = rpc__clock_stamp() + h->c.c.call_timeout_time;
}
if ((options & rpc_c_call_brdcst) != 0)
{
ccall->c.xq.base_flags |= RPC_C_DG_PF_BROADCAST | RPC_C_DG_PF_IDEMPOTENT;
h->c.c.bound_server_instance = false;
}
if ((options & rpc_c_call_idempotent) != 0)
ccall->c.xq.base_flags |= RPC_C_DG_PF_IDEMPOTENT;
if ((options & rpc_c_call_maybe) != 0)
ccall->c.xq.base_flags |= RPC_C_DG_PF_MAYBE | RPC_C_DG_PF_IDEMPOTENT;
if (ccall->c.sock_ref->is_private)
ccall->c.thread_id = dcethread_self();
ccall->server_bound = h->c.c.bound_server_instance;
if (ccall->server_bound == false && h->c.c.addr_has_endpoint == false)
{
rpc__if_set_wk_endpoint(ifspec, &ccall->c.addr, st);
if (*st != rpc_s_ok)
{
rpc__if_set_wk_endpoint
((rpc_if_rep_p_t) ept_v3_0_c_ifspec, &ccall->c.addr, st);
if (*st != rpc_s_ok)
return;
}
}
if (doing_callback)
{
ccall->c.high_seq = ccall->c.call_seq = ++ccall->cbk_scall->c.high_seq;
ccall->c.rq.high_rcv_frag_size =
ccall->cbk_scall->c.rq.high_rcv_frag_size;
if (ccall->c.rq.high_rcv_frag_size > ccall->cbk_scall->c.xq.snd_frag_size)
{
ccall->c.xq.snd_frag_size = MIN(ccall->c.rq.high_rcv_frag_size,
ccall->c.xq.max_snd_tsdu);
ccall->c.xq.snd_frag_size &= ~ 0x7;
ccall->c.xq.first_fack_seen = true;
}
else
ccall->c.xq.snd_frag_size = ccall->cbk_scall->c.xq.snd_frag_size;
ccall->c.n_resvs = ccall->cbk_scall->c.n_resvs;
}
else
{
ccall->c.high_seq = ccall->c.call_seq = ccall->ccte_ref.ccte->seq++;
if ((options & rpc_c_call_brdcst) != 0)
{
ccall->c.rq.high_rcv_frag_size = RPC_C_DG_INITIAL_MAX_PKT_SIZE;
ccall->c.xq.snd_frag_size = MIN(RPC_C_DG_MUST_RECV_FRAG_SIZE,
ccall->c.xq.snd_frag_size);
}
else if (ccall->c.rq.high_rcv_frag_size > ccall->c.xq.snd_frag_size)
{
ccall->c.xq.snd_frag_size = MIN(ccall->c.rq.high_rcv_frag_size,
ccall->c.xq.max_snd_tsdu);
ccall->c.xq.snd_frag_size &= ~ 0x7;
ccall->c.xq.first_fack_seen = true;
}
}
RPC_DBG_PRINTF(rpc_e_dbg_xmit, 6,
("(ccall_common_init) Set snd fs %lu, high rcv fs %lu\n",
ccall->c.xq.snd_frag_size, ccall->c.rq.high_rcv_frag_size));
*st = rpc_s_ok;
}
INTERNAL rpc_dg_ccall_p_t ccall_alloc
(
rpc_dg_binding_client_p_t h,
unsigned32 options,
rpc_if_rep_p_t ifspec,
unsigned32 opnum,
unsigned32 *st
)
{
rpc_dg_pkt_hdr_p_t hdrp;
rpc_dg_sock_pool_elt_p_t sp;
rpc_dg_ccall_p_t ccall;
boolean doing_callback = (h->shand != NULL);
RPC_LOCK_ASSERT(0);
if (doing_callback)
RPC_DG_CALL_LOCK_ASSERT(&h->shand->scall->c);
if (! doing_callback)
{
rpc__dg_network_use_protseq_cl(h->c.c.rpc_addr->rpc_protseq_id,
&sp);
if (sp == NULL) {
*st = rpc_s_cant_create_sock;
return (NULL);
}
}
else
{
sp = ((rpc_dg_binding_server_p_t) (h->shand))->scall->c.sock_ref;
rpc__dg_network_sock_reference(sp);
}
RPC_MEM_ALLOC(ccall, rpc_dg_ccall_p_t, sizeof *ccall,
RPC_C_MEM_DG_CCALL, RPC_C_MEM_NOWAIT);
rpc__dg_call_init(&ccall->c);
RPC_DG_CALL_LOCK(&ccall->c);
RPC_DG_CALL_REFERENCE(&ccall->c);
RPC_DG_CCTE_REF_INIT(&ccall->ccte_ref);
ccall->c.sock_ref = sp;
if (sp->is_private)
sp->ccall = ccall;
if (! doing_callback)
{
rpc__dg_cct_get(h->c.c.auth_info, ccall);
ccall->c.is_cbk = false;
ccall->cbk_scall = NULL;
}
else
{
rpc_dg_scall_p_t scall = ((rpc_dg_binding_server_p_t) (h->shand))->scall;
ccall->c.key_info = 0;
ccall->c.auth_epv = 0;
ccall->c.call_actid = scall->c.call_actid;
ccall->c.actid_hash = scall->c.actid_hash;
ccall->c.rq.window_size = scall->c.rq.window_size;
ccall->c.is_cbk = true;
RPC_DG_CALL_REFERENCE(&scall->c);
ccall->cbk_scall = scall;
RPC_DG_CALL_REFERENCE(&ccall->c);
scall->cbk_ccall = ccall;
ccall->c.xq.max_rcv_tsdu = scall->c.xq.max_rcv_tsdu;
ccall->c.xq.max_snd_tsdu = scall->c.xq.max_snd_tsdu;
ccall->c.xq.max_frag_size = scall->c.xq.max_frag_size;
ccall->c.max_resvs = scall->c.max_resvs;
}
ccall->c.c.is_server = false;
rpc__naf_addr_copy(h->c.c.rpc_addr, &ccall->c.addr, st);
ccall->c.call_object = h->c.c.obj;
ccall->c.call_if_id = ifspec->id;
ccall->c.call_if_vers = ifspec->vers;
ccall->c.call_ihint = RPC_C_DG_NO_HINT;
hdrp = &ccall->c.xq.hdr;
RPC_DG_HDR_SET_PTYPE(hdrp, RPC_C_DG_PT_REQUEST);
hdrp->flags2 = 0;
hdrp->server_boot = h->server_boot;
hdrp->ahint = RPC_C_DG_NO_HINT;
ccall->h = (struct rpc_dg_binding_client_t *) h;
ccall->server_bound = false;
ccall->fault_rqe = NULL;
if (! doing_callback)
{
rpc__naf_inq_max_tsdu(ccall->c.addr->rpc_protseq_id,
&ccall->c.xq.max_rcv_tsdu, st);
ccall->c.xq.max_snd_tsdu = ccall->c.xq.max_rcv_tsdu;
ccall->c.xq.max_rcv_tsdu = MIN(ccall->c.xq.max_rcv_tsdu,
ccall->c.sock_ref->rcvbuf);
ccall->c.xq.max_snd_tsdu = MIN(ccall->c.xq.max_snd_tsdu,
ccall->c.sock_ref->sndbuf);
RPC_DBG_PRINTF(rpc_e_dbg_xmit, 6,
("(ccall_alloc) Set rcv tsdu %u, snd tsdu %u\n",
ccall->c.xq.max_rcv_tsdu, ccall->c.xq.max_snd_tsdu));
}
if ((options & rpc_c_call_brdcst) == 0)
{
if (! doing_callback)
{
RPC_DG_CALL_SET_MAX_FRAG_SIZE(&ccall->c, st);
RPC_DBG_PRINTF(rpc_e_dbg_xmit, 6,
("(ccall_alloc) Set max fs %d\n",
ccall->c.xq.max_frag_size));
}
}
if (! doing_callback)
{
RPC_DG_RBUF_SIZE_TO_WINDOW_SIZE(sp->rcvbuf,
sp->is_private,
ccall->c.xq.max_frag_size,
ccall->c.rq.window_size);
RPC_DBG_PRINTF(rpc_e_dbg_xmit, 6,
("(ccall_alloc) Set ws %d, rcvbuf %u, max fs %u\n",
ccall->c.rq.window_size, sp->rcvbuf, ccall->c.xq.max_frag_size));
}
ccall_common_init(h, ccall, options, ifspec, opnum, st);
if (*st != rpc_s_ok)
{
RPC_DG_CCALL_RELEASE(&ccall);
return (NULL);
}
RPC_DG_CALL_SET_TIMER(&ccall->c, rpc__dg_ccall_timer, RPC_CLOCK_SEC(1));
return (ccall);
}
INTERNAL rpc_dg_ccall_p_t ccall_reinit
(
rpc_dg_binding_client_p_t h,
unsigned32 options,
rpc_if_rep_p_t ifspec,
unsigned32 opnum,
boolean *insert_in_ccallt,
unsigned32 *st
)
{
rpc_dg_call_state_t prev_state;
rpc_dg_ccall_p_t ccall;
boolean doing_callback = (h->shand != NULL);
RPC_LOCK_ASSERT(0);
if (doing_callback)
RPC_DG_CALL_LOCK_ASSERT(&h->shand->scall->c);
ccall = h->ccall;
h->ccall = NULL;
RPC_DG_CALL_LOCK(&ccall->c);
RPC_DG_CALL_REINIT(&ccall->c);
if (ccall->c.state != rpc_e_dg_cs_final)
{
assert(ccall->c.state == rpc_e_dg_cs_idle);
if (! doing_callback)
{
rpc__dg_cct_get(h->c.c.auth_info, ccall);
}
}
prev_state = ccall->c.state;
RPC_DG_CALL_SET_STATE(&ccall->c, rpc_e_dg_cs_init);
if (ifspec->vers != ccall->c.call_if_vers
|| ! UUID_EQ(ifspec->id, ccall->c.call_if_id, st))
{
ccall->c.call_ihint = RPC_C_DG_NO_HINT;
ccall->c.call_if_id = ifspec->id;
ccall->c.call_if_vers = ifspec->vers;
}
if (ccall->auth_way_info != NULL)
{
RPC_MEM_FREE(ccall->auth_way_info, RPC_C_MEM_DG_EPAC);
}
ccall_common_init(h, ccall, options, ifspec, opnum, st);
if (*st != rpc_s_ok)
{
RPC_DG_CALL_UNLOCK(&ccall->c);
return (NULL);
}
*insert_in_ccallt = (prev_state != rpc_e_dg_cs_final);
return (ccall);
}
INTERNAL void ccall_binding_serialize
(
rpc_dg_binding_client_p_t h,
boolean32 is_brdcst,
unsigned32 *cancel_cnt,
rpc_clock_p_t cancel_timeout_time,
unsigned32 *st
)
{
volatile boolean is_awaiting_timeout = false;
volatile boolean has_timed_out = false;
struct timespec zero_delta, delta, abstime, curtime;
RPC_LOCK_ASSERT(0);
*st = rpc_s_ok;
*cancel_cnt = 0;
*cancel_timeout_time = RPC_CLOCK_SEC(0);
zero_delta.tv_sec = 0;
zero_delta.tv_nsec = 0;
delta.tv_sec = 0;
delta.tv_nsec = 0;
RPC_GET_CANCEL_TIMEOUT(delta.tv_sec, st);
if (*st != rpc_s_ok)
return;
while (has_timed_out == false
&& ((h->c.c.bound_server_instance == false || is_brdcst)
&& h->c.c.calls_in_progress != 0))
{
#ifdef _PTHREAD_NO_CANCEL_SUPPORT
RPC_BINDING_COND_WAIT(0);
#else
DCETHREAD_TRY
if (! is_awaiting_timeout)
{
RPC_BINDING_COND_WAIT(0);
}
else
{
RPC_BINDING_COND_TIMED_WAIT(&abstime);
dcethread_get_expiration(&zero_delta, &curtime);
if (curtime.tv_sec == abstime.tv_sec)
has_timed_out = true;
}
DCETHREAD_CATCH(dcethread_interrupt_e)
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 5,
("(ccall_binding_serialize) cancel detected\n"));
if (delta.tv_sec == 0)
has_timed_out = true;
else if ((signed32)delta.tv_sec == rpc_c_cancel_infinite_timeout)
;
else
{
if (is_awaiting_timeout == false)
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 5,
("(ccall_binding_serialize) %d sec cancel timeout setup\n",
(int)delta.tv_sec));
dcethread_get_expiration(&delta, &abstime);
*cancel_timeout_time = rpc__clock_stamp() +
RPC_CLOCK_SEC(delta.tv_sec);
}
is_awaiting_timeout = true;
}
*cancel_cnt += 1;
DCETHREAD_ENDTRY
#endif
}
if (has_timed_out)
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 5,
("(ccall_binding_serialize) cancel timeout\n"));
*st = rpc_s_cancel_timeout;
}
}
INTERNAL void ccall_initial_cancel_setup
(
rpc_dg_ccall_p_t ccall,
unsigned32 cancel_cnt,
rpc_clock_t cancel_timeout_time
)
{
if (cancel_cnt == 0)
return;
if (ccall == NULL)
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 5,
("(ccall_initial_cancel_setup) reposting cancels\n"));
for (; cancel_cnt--; )
dcethread_interrupt_throw(dcethread_self());
return;
}
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
ccall->cancel.timeout_time = cancel_timeout_time;
ccall->cancel.local_count = cancel_cnt;
}
PRIVATE void rpc__dg_ccall_setup_cancel_tmo(ccall)
rpc_dg_ccall_p_t ccall;
{
unsigned32 ctmo = 0;
unsigned32 tst;
if (ccall->cancel.timeout_time == 0)
{
RPC_GET_CANCEL_TIMEOUT(ctmo, &tst);
if ((signed32) ctmo != rpc_c_cancel_infinite_timeout)
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 10,
("(rpc__dg_ccall_setup_cancel_tmo) %d sec cancel timeout setup\n",
ctmo));
ccall->cancel.timeout_time = rpc__clock_stamp() +
RPC_CLOCK_SEC(ctmo);
}
}
}
PRIVATE rpc_call_rep_p_t rpc__dg_call_start
(
rpc_binding_rep_p_t h_,
unsigned32 options,
rpc_if_rep_p_t ifspec,
unsigned32 opnum,
rpc_transfer_syntax_t *transfer_syntax,
unsigned32 *st
)
{
rpc_dg_binding_client_p_t h = (rpc_dg_binding_client_p_t) h_;
rpc_dg_ccall_p_t ccall;
boolean doing_callback = RPC_BINDING_IS_SERVER(&h->c.c);
unsigned32 cancel_cnt = 0;
rpc_clock_t cancel_timeout_time;
rpc_dg_scall_p_t scall = NULL;
boolean32 is_brdcst = (options & rpc_c_call_brdcst) != 0;
RPC_DG_STATS_INCR(calls_sent);
*transfer_syntax = ndr_g_transfer_syntax;
if (doing_callback)
{
h = rpc__dg_binding_srvr_to_client( (rpc_dg_binding_server_p_t) h_, st);
if (*st != rpc_s_ok)
return(NULL);
scall = ((rpc_dg_binding_server_p_t) (h->shand))->scall;
}
RPC_LOCK(0);
if ((h->c.c.bound_server_instance == false || is_brdcst)
&& h->c.c.calls_in_progress != 0)
{
RPC_DBG_PRINTF(rpc_e_dbg_general, 5,
("(rpc__dg_call_start) serializing call...\n"));
ccall_binding_serialize(h, is_brdcst,
&cancel_cnt, &cancel_timeout_time, st);
if (*st != rpc_s_ok)
{
RPC_UNLOCK(0);
ccall_initial_cancel_setup(NULL, cancel_cnt, 0);
return (NULL);
}
}
ccall = h->ccall;
if (ccall != NULL)
{
rpc_key_info_p_t key_info = ccall->c.key_info;
rpc_auth_info_p_t auth_info = h->c.c.auth_info;
if (RPC_DG_SOCK_IS_DISABLED(ccall->c.sock_ref) ||
((auth_info != NULL) ?
((key_info == NULL) || (key_info->auth_info != auth_info))
: (key_info != NULL)))
{
RPC_DG_CALL_LOCK(&ccall->c);
RPC_DG_CCALL_RELEASE(&h->ccall)
}
}
if (doing_callback)
RPC_DG_CALL_LOCK(&scall->c);
if (h->ccall == NULL)
{
ccall = ccall_alloc(h, options, ifspec, opnum, st);
if (*st != rpc_s_ok)
{
if (doing_callback)
RPC_DG_CALL_UNLOCK(&scall->c);
RPC_UNLOCK(0);
ccall_initial_cancel_setup(NULL, cancel_cnt, 0);
return (NULL);
}
rpc__dg_ccallt_insert(ccall);
}
else
{
boolean insert_in_ccallt = 0;
ccall = ccall_reinit(h, options, ifspec, opnum, &insert_in_ccallt, st);
if (*st != rpc_s_ok)
{
if (doing_callback)
RPC_DG_CALL_UNLOCK(&scall->c);
RPC_UNLOCK(0);
ccall_initial_cancel_setup(NULL, cancel_cnt, 0);
return (NULL);
}
if (insert_in_ccallt)
{
rpc__dg_ccallt_insert(ccall);
}
}
RPC_BINDING_CALL_START(&h->c.c);
if (cancel_cnt)
ccall_initial_cancel_setup(ccall, cancel_cnt, cancel_timeout_time);
if (is_brdcst)
{
rpc__dg_pkt_adjust_reservation(&ccall->c,
RPC_C_DG_MAX_NUM_PKTS_IN_FRAG, true);
}
else
{
rpc__dg_pkt_adjust_reservation(&ccall->c, ccall->c.max_resvs, true);
}
if (doing_callback)
{
RPC_DG_CALL_UNLOCK(&scall->c);
}
RPC_DG_CALL_UNLOCK(&ccall->c);
RPC_UNLOCK(0);
if (!doing_callback &&
ccall->c.key_info != NULL)
{
rpc_dg_auth_epv_p_t epv = ccall->c.auth_epv;
error_status_t xst;
(*epv->pre_call) (ccall->c.key_info, (handle_t)h, &xst);
if (xst != rpc_s_ok)
{
RPC_DG_CALL_LOCK(&ccall->c);
rpc__dg_call_signal_failure(&ccall->c, xst);
RPC_DG_CALL_UNLOCK(&ccall->c);
}
}
*st = rpc_s_ok;
return ((rpc_call_rep_p_t) ccall);
}
PRIVATE void rpc__dg_call_end
(
rpc_call_rep_p_t *call_,
unsigned32 *st
)
{
rpc_dg_ccall_p_t ccall;
rpc_dg_binding_client_p_t h;
*st = rpc_s_ok;
ccall = (rpc_dg_ccall_p_t) *call_;
*call_ = NULL;
assert(RPC_DG_CALL_IS_CLIENT(&ccall->c));
RPC_LOCK(0);
RPC_DG_CALL_LOCK(&ccall->c);
h = (rpc_dg_binding_client_p_t) ccall->h;
rpc__dg_pkt_cancel_reservation(&ccall->c);
if (ccall->c.is_cbk)
{
rpc_dg_scall_p_t scall = ccall->cbk_scall;
RPC_DG_CALL_UNLOCK(&ccall->c);
RPC_DG_CALL_LOCK(&scall->c);
RPC_DG_CALL_LOCK(&ccall->c);
if (ccall->response_info_updated && ccall->cbk_scall != NULL)
ccall->cbk_scall->c.high_seq = ccall->c.high_seq;
if (ccall->cbk_scall != NULL)
{
ccall->cbk_scall->c.rq.high_rcv_frag_size =
ccall->c.rq.high_rcv_frag_size;
ccall->cbk_scall->c.xq.snd_frag_size = ccall->c.xq.snd_frag_size;
}
RPC_DG_CALL_UNLOCK(&scall->c);
}
else
{
if (RPC_DG_SEQ_IS_LT(ccall->c.call_seq, ccall->c.high_seq))
ccall->ccte_ref.ccte->seq = ccall->c.high_seq + 1;
if (ccall->cbk_scall != NULL)
{
rpc_dg_scall_p_t scall = ccall->cbk_scall;
unsigned32 tst;
assert(scall->cbk_ccall == ccall);
ccall->cbk_start = false;
RPC_DG_CALL_LOCK(&scall->c);
RPC_DG_SCALL_RELEASE_NO_UNLOCK(&scall->h->scall);
RPC_BINDING_RELEASE((rpc_binding_rep_p_t *) &scall->h, &tst);
RPC_DG_CALL_STOP_TIMER(&scall->c);
if (RPC_DG_SEQ_IS_LT(ccall->c.high_seq, scall->c.call_seq))
ccall->c.high_seq = scall->c.call_seq;
RPC_DG_CCALL_RELEASE_NO_UNLOCK(&scall->cbk_ccall);
RPC_DG_SCALL_RELEASE(&ccall->cbk_scall);
}
}
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
RPC_BINDING_CALL_END(&h->c.c);
if (ccall->fault_rqe != NULL)
{
rpc__dg_pkt_free_rqe(ccall->fault_rqe, &ccall->c);
ccall->fault_rqe = NULL;
}
if (ccall->cancel.server_had_pending
|| (ccall->cancel.local_count > ccall->cancel.server_count))
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 5,
("(rpc__dg_call_end) reposting cancel\n"));
dcethread_interrupt_throw(dcethread_self());
}
if ((ccall->c.rq.all_pkts_recvd == true)
&& (ccall->c.status == rpc_s_ok)
&& ((ccall->c.xq.base_flags & RPC_C_DG_PF_IDEMPOTENT) == 0
|| ccall->c.rq.recving_frags))
{
RPC_DG_CALL_SET_STATE(&ccall->c, rpc_e_dg_cs_final);
}
else
{
if (ccall->c.rq.all_pkts_recvd == false)
{
ccall->quit.next_time = rpc__clock_stamp() + RPC_CLOCK_SEC(1);
RPC_DG_CALL_SET_STATE(&ccall->c, rpc_e_dg_cs_orphan);
RPC_DBG_GPRINTF(("(rpc__dg_call_end) Sending orphan quit\n"));
xmit_orphan_quit(ccall);
RPC_UNLOCK(0);
while (ccall->quit.quack_rcvd != true)
{
int oc;
oc = dcethread_enableinterrupt_throw(0);
RPC_DG_CALL_COND_WAIT(&ccall->c);
dcethread_enableinterrupt_throw(oc);
}
RPC_DG_CALL_UNLOCK(&ccall->c);
RPC_LOCK(0);
RPC_DG_CALL_LOCK(&ccall->c);
if (ccall->cancel.local_count > 0)
dcethread_interrupt_throw(dcethread_self());
}
RPC_DG_CCALL_SET_STATE_IDLE(ccall);
}
if (h->ccall != NULL)
{
rpc__dg_ccall_free_prep(ccall);
}
else
{
RPC_DG_CALL_REFERENCE(&ccall->c);
h->ccall = ccall;
}
RPC_DG_CCALL_RELEASE(&ccall);
RPC_UNLOCK(0);
}
INTERNAL void xmit_ping
(
rpc_dg_ccall_p_t ccall
)
{
rpc_dg_xmitq_p_t xq = &ccall->c.xq;
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
xq->next_serial_num++;
xq->hdr.serial_hi = (xq->next_serial_num & 0xFF00) >> 8;
xq->hdr.serial_lo = xq->next_serial_num & 0xFF;
rpc__dg_xmit_hdr_only_pkt(ccall->c.sock_ref->sock, ccall->c.addr,
&xq->hdr, RPC_C_DG_PT_PING);
}
PRIVATE void rpc__dg_ccall_ack
(
rpc_dg_ccall_p_t ccall
)
{
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
assert(ccall->c.state == rpc_e_dg_cs_final);
RPC_DBG_PRINTF(rpc_e_dbg_xmit, 5, ("sending ack\n"));
rpc__dg_xmit_hdr_only_pkt(ccall->c.sock_ref->sock, ccall->c.addr,
&ccall->c.xq.hdr,
RPC_C_DG_PT_ACK);
}
INTERNAL void xmit_orphan_quit
(
rpc_dg_ccall_p_t ccall
)
{
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
rpc__dg_xmit_hdr_only_pkt(ccall->c.sock_ref->sock, ccall->c.addr,
&ccall->c.xq.hdr,
RPC_C_DG_PT_QUIT);
}
PRIVATE void rpc__dg_ccall_xmit_cancel_quit
(
rpc_dg_ccall_p_t ccall,
unsigned32 cancel_id
)
{
rpc_socket_iovec_t iov[2];
rpc_dg_pkt_hdr_t hdr;
#ifndef MISPACKED_HDR
rpc_dg_quitpkt_body_t body;
#else
rpc_dg_raw_quitpkt_body_t body;
#endif
boolean b;
hdr = ccall->c.xq.hdr;
RPC_DG_HDR_SET_PTYPE(&hdr, RPC_C_DG_PT_QUIT);
hdr.flags = 0;
hdr.len = RPC_C_DG_RAW_QUITPKT_BODY_SIZE;
RPC_DG_HDR_SET_DREP(&hdr);
#ifndef MISPACKED_HDR
body.vers = RPC_C_DG_QUITPKT_BODY_VERS;
body.cancel_id = cancel_id;
#else
!!!
#endif
iov[0].iov_base = (byte_p_t) &hdr;
iov[0].iov_len = RPC_C_DG_RAW_PKT_HDR_SIZE;
iov[1].iov_base = (byte_p_t) &body;
iov[1].iov_len = hdr.len;
rpc__dg_xmit_pkt(ccall->c.sock_ref->sock, ccall->c.addr, iov, 2, &b);
}
INTERNAL void ccall_cancel_timer
(
rpc_dg_ccall_p_t ccall
)
{
rpc_clock_t now;
static rpc_clock_t inter_cancel_time = RPC_CLOCK_SEC(2);
if (ccall->cancel.local_count == 0)
return;
if (ccall->c.status != rpc_s_ok)
return;
now = rpc__clock_stamp();
if (ccall->cancel.timeout_time != 0
&& now >= ccall->cancel.timeout_time)
{
RPC_DBG_GPRINTF(("(ccall_cancel_timer) cancel timeout [%s]\n",
rpc__dg_act_seq_string(&ccall->c.xq.hdr)));
ccall->cancel.server_is_accepting = false;
ccall->cancel.server_had_pending = true;
rpc__dg_call_signal_failure(&ccall->c, rpc_s_cancel_timeout);
return;
}
if (ccall->cancel.local_count > ccall->cancel.server_count
&& ccall->cancel.server_is_accepting
&& now >= ccall->cancel.next_time)
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 10,
("(ccall_cancel_timer) Sending cancel id: %d [%s]\n",
ccall->cancel.local_count,
rpc__dg_act_seq_string(&ccall->c.xq.hdr)));
ccall->cancel.next_time = now + inter_cancel_time;
rpc__dg_ccall_xmit_cancel_quit(ccall, ccall->cancel.local_count);
}
}
INTERNAL void recv_state_timer
(
rpc_dg_ccall_p_t ccall
)
{
rpc_dg_ping_info_t *ping = &ccall->ping;
rpc_clock_t now;
rpc_dg_xmitq_p_t xq = &ccall->c.xq;
rpc_dg_recvq_p_t rq = &ccall->c.rq;
static rpc_clock_t broadcast_wait_time = RPC_CLOCK_SEC(3);
static com_timeout_params_t ccall_com_timeout_params[] = {
{RPC_CLOCK_SEC(1)},
{RPC_CLOCK_SEC(2)},
{RPC_CLOCK_SEC(4)},
{RPC_CLOCK_SEC(8)},
{RPC_CLOCK_SEC(15)},
{RPC_CLOCK_SEC(30)},
{RPC_CLOCK_SEC(2*30)},
{RPC_CLOCK_SEC(4*30)},
{RPC_CLOCK_SEC(8*30)},
{RPC_CLOCK_SEC(16*30)},
{RPC_CLOCK_SEC(0)}
};
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
if (ccall->c.status != rpc_s_ok || rq->all_pkts_recvd)
{
return;
}
ccall_cancel_timer(ccall);
if ((xq->base_flags & RPC_C_DG_PF_BROADCAST) != 0)
{
if (rpc__clock_aged(ccall->c.start_time, broadcast_wait_time))
{
RPC_DBG_GPRINTF(("(recv_state_timer) Call expiration time reached [%s]\n",
rpc__dg_act_seq_string(&xq->hdr)));
rpc__dg_call_signal_failure(&ccall->c, rpc_s_comm_failure);
}
return;
}
if (! ping->pinging)
{
now = rpc__clock_stamp();
if (now >= ping->next_time)
{
RPC_DBG_PRINTF(rpc_e_dbg_general, 3,
("(recv_state_timer) Starting to ping (rq->next_fragnum=%u) [%s]\n",
ccall->c.rq.next_fragnum,
rpc__dg_act_seq_string(&xq->hdr)));
if (rpc__dg_xmitq_awaiting_ack_tmo(xq, ccall->c.com_timeout_knob))
{
rpc__dg_call_signal_failure(&ccall->c, rpc_s_comm_failure);
}
else
{
ping->pinging = true;
ping->count = 0;
ping->start_time = now;
RPC_PING_INFO_NEXT(ping, now);
xmit_ping(ccall);
}
}
}
else
{
if (rpc__clock_aged(ping->start_time,
ccall_com_timeout_params[ccall->c.com_timeout_knob].max_ping_time)
&& ccall->c.com_timeout_knob != rpc_c_binding_infinite_timeout)
{
RPC_DBG_GPRINTF(("(recv_state_timer) Ping timeout [%s]\n",
rpc__dg_act_seq_string(&xq->hdr)));
rpc__dg_call_signal_failure(&ccall->c, rpc_s_comm_failure);
}
else
{
now = rpc__clock_stamp();
if (now >= ping->next_time)
{
RPC_DBG_PRINTF(rpc_e_dbg_general, 2,
("(recv_state_timer) Re-pinging (rq->next_fragnum=%u) [%s]\n",
ccall->c.rq.next_fragnum,
rpc__dg_act_seq_string(&xq->hdr)));
RPC_PING_INFO_NEXT(ping, now);
xmit_ping(ccall);
}
}
}
}
INTERNAL void ccall_orphan_timer
(
rpc_dg_ccall_p_t ccall
)
{
struct rpc_dg_quit_info_t *quit = &ccall->quit;
rpc_clock_t now;
static rpc_clock_t inter_quit_time = RPC_CLOCK_SEC(1);
now = rpc__clock_stamp();
if (now >= quit->next_time)
{
RPC_DBG_PRINTF(rpc_e_dbg_cancel, 10,
("(ccall_orphan_timer) Resending orphan quit [%s]\n",
rpc__dg_act_seq_string(&ccall->c.xq.hdr)));
quit->next_time = now + inter_quit_time;
xmit_orphan_quit(ccall);
}
}
INTERNAL void ccall_timeout_timer
(
rpc_dg_ccall_p_t ccall
)
{
rpc_clock_t now;
if (ccall->timeout_stamp == 0)
return;
now = rpc__clock_stamp();
if (now >= ccall->timeout_stamp)
{
RPC_DBG_GPRINTF(("(ccall_timeout_timer) call timeout\n"));
rpc__dg_call_signal_failure(&ccall->c, rpc_s_call_timeout);
}
}
PRIVATE void rpc__dg_ccall_free
(
rpc_dg_ccall_p_t ccall
)
{
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
assert(ccall->c.refcnt == 0);
if (ccall->c.state == rpc_e_dg_cs_final)
{
rpc__dg_ccall_ack(ccall);
}
assert(ccall->cbk_scall == NULL);
if (ccall->auth_way_info != NULL)
{
RPC_MEM_FREE(ccall->auth_way_info, RPC_C_MEM_DG_EPAC);
}
rpc__dg_call_free(&ccall->c);
RPC_MEM_FREE(ccall, RPC_C_MEM_DG_CCALL);
}
PRIVATE void rpc__dg_ccall_free_prep
(
rpc_dg_ccall_p_t ccall
)
{
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
RPC_DG_CALL_STOP_TIMER(&ccall->c);
if (ccall->c.state != rpc_e_dg_cs_idle)
RPC_DG_CCALL_SET_STATE_IDLE(ccall);
}
INTERNAL void ccall_uncache
(
rpc_dg_ccall_p_t ccall
)
{
RPC_LOCK_ASSERT(0);
RPC_DG_CALL_LOCK_ASSERT(&ccall->c);
assert(ccall->c.state == rpc_e_dg_cs_idle);
if (ccall->h != NULL && ccall->h->ccall == ccall)
{
RPC_DG_CCALL_RELEASE_NO_UNLOCK(&ccall->h->ccall);
}
rpc__timer_clear(&ccall->c.timer);
RPC_DG_CCALL_RELEASE(&ccall);
}
PRIVATE void rpc__dg_ccall_timer
(
dce_pointer_t p
)
{
rpc_dg_ccall_p_t ccall = (rpc_dg_ccall_p_t) p;
static rpc_clock_t max_idle_time = RPC_CLOCK_SEC(30);
static rpc_clock_t max_final_time = RPC_CLOCK_SEC(3);
static rpc_clock_t max_orphan_time = RPC_CLOCK_SEC(3);
RPC_LOCK(0);
RPC_DG_CALL_LOCK(&ccall->c);
if (ccall->c.stop_timer)
{
rpc__timer_clear(&ccall->c.timer);
RPC_DG_CCALL_RELEASE(&ccall);
RPC_UNLOCK(0);
return;
}
switch (ccall->c.state)
{
case rpc_e_dg_cs_init:
ccall_cancel_timer(ccall);
ccall_timeout_timer(ccall);
break;
case rpc_e_dg_cs_idle:
if (! ccall->c.is_cbk)
{
if (rpc__clock_aged(ccall->c.state_timestamp,
max_idle_time))
{
ccall_uncache(ccall);
RPC_UNLOCK(0);
return;
}
}
break;
case rpc_e_dg_cs_xmit:
rpc__dg_call_xmitq_timer(&ccall->c);
ccall_cancel_timer(ccall);
ccall_timeout_timer(ccall);
break;
case rpc_e_dg_cs_recv:
recv_state_timer(ccall);
ccall_timeout_timer(ccall);
break;
case rpc_e_dg_cs_final:
if (rpc__clock_aged(ccall->c.state_timestamp, max_final_time))
{
RPC_DG_CCALL_SET_STATE_IDLE(ccall);
}
break;
case rpc_e_dg_cs_orphan:
if (rpc__clock_aged(ccall->c.state_timestamp, max_orphan_time))
{
RPC_DBG_GPRINTF(("rpc__dg_ccall_timer) Orphan timeout\n"));
ccall->quit.quack_rcvd = true;
rpc__dg_call_signal_failure(&ccall->c, rpc_s_comm_failure);
}
else
{
ccall_orphan_timer(ccall);
}
break;
}
RPC_DG_CALL_UNLOCK(&ccall->c);
RPC_UNLOCK(0);
}