#if HAVE_CONFIG_H
#include <config.h>
#endif
#include <dce/rpc.h>
#include <dce/stubbase.h>
#include <lsysdep.h>
#include <ctxeertl.h>
# include <stdio.h>
#ifdef PERFMON
#include <dce/idl_log.h>
#endif
#ifndef DEBUG_VERBOSE
# define NDEBUG
#endif
#include <assert.h>
#ifdef CTXEETEST
# define DPRINT(ARGS) printf ARGS
#else
# define DPRINT(ARGS)
#endif
#include <rpcsvc.h>
typedef struct rpc_ss_rundown_list_elt {
ctx_rundown_fn_p_t rundown;
rpc_ss_context_t user_context;
struct rpc_ss_rundown_list_elt *next;
} rpc_ss_rundown_list_elt;
#define CALLEE_CLIENT_TABLE_SIZE 256
#define HASH_CLIENT_ID(ID) (((unsigned long)(ID) >> 4) % CALLEE_CLIENT_TABLE_SIZE)
static callee_client_entry_t *client_table = NULL;
void rpc_ss_init_callee_client_table(
void
)
{
#ifdef PERFMON
RPC_SS_INIT_CALLEE_CLNT_TABLE_N;
#endif
assert(!client_table);
client_table = (callee_client_entry_t *)malloc(
CALLEE_CLIENT_TABLE_SIZE * sizeof(callee_client_entry_t)
);
if (!client_table)
DCETHREAD_RAISE(rpc_x_no_memory);
memset(client_table, 0, CALLEE_CLIENT_TABLE_SIZE * sizeof(callee_client_entry_t));
#ifdef PERFMON
RPC_SS_INIT_CALLEE_CLNT_TABLE_X;
#endif
}
void rpc_ss_add_to_callee_client
(
rpc_client_handle_t ctx_client,
callee_context_entry_t *p_context,
ndr_boolean *p_is_new_client,
error_status_t *result
)
{
int slot;
ndr_boolean creating_at_home;
callee_client_entry_t *this_client, *next_client, *new_client;
#ifdef PERFMON
RPC_SS_ADD_TO_CALLEE_CLIENT_N;
#endif
slot = HASH_CLIENT_ID(ctx_client);
this_client = &client_table[slot];
while (ndr_true)
{
if ( ctx_client == this_client->client )
{
++this_client->count;
*p_is_new_client = (this_client->count == 1);
p_context->p_client_entry = this_client;
p_context->prev_in_client = this_client->last_context;
p_context->next_in_client = NULL;
if (this_client->first_context == NULL)
{
this_client->first_context = p_context;
}
else
{
(this_client->last_context)->next_in_client = p_context;
}
this_client->last_context = p_context;
*result = error_status_ok;
#ifdef PERFMON
RPC_SS_ADD_TO_CALLEE_CLIENT_X;
#endif
return;
}
next_client = this_client->next_h_client;
if (next_client == NULL) break;
this_client = next_client;
}
creating_at_home = (client_table[slot].client == NULL);
if (creating_at_home)
{
new_client = &client_table[slot];
}
else
{
new_client = (callee_client_entry_t *)
malloc(sizeof(callee_client_entry_t));
if (new_client == NULL)
{
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
DPRINT(("Released context tables\n"));
DCETHREAD_RAISE( rpc_x_no_memory );
}
this_client->next_h_client = new_client;
new_client->prev_h_client = this_client;
new_client->next_h_client = NULL;
}
new_client->client = ctx_client;
new_client->rundown_pending = idl_false;
RPC_SS_THREADS_CONDITION_CREATE(&(new_client->cond_var));
if (p_context == NULL)
{
new_client->count = 0;
new_client->first_context = NULL;
new_client->last_context = NULL;
new_client->ref_count = 1;
*p_is_new_client = ndr_false;
}
else
{
new_client->count = 1;
new_client->first_context = p_context;
new_client->last_context = p_context;
new_client->ref_count = 0;
p_context->p_client_entry = new_client;
p_context->prev_in_client = NULL;
p_context->next_in_client = NULL;
*p_is_new_client = ndr_true;
}
*result = error_status_ok;
#ifdef PERFMON
RPC_SS_ADD_TO_CALLEE_CLIENT_X;
#endif
}
static void rpc_ss_ctx_remove_client_entry
(
callee_client_entry_t *this_client
)
{
callee_client_entry_t *next_client, *prev_client;
RPC_SS_THREADS_CONDITION_DELETE(&(this_client->cond_var));
prev_client = this_client->prev_h_client;
next_client = this_client->next_h_client;
if (prev_client != NULL)
{
prev_client->next_h_client = next_client;
if (next_client != NULL)
{
next_client->prev_h_client = prev_client;
}
free((char_p_t)this_client);
}
else
{
this_client->client = NULL;
}
}
void rpc_ss_take_from_callee_client
(
callee_context_entry_t *p_context,
rpc_client_handle_t *p_close_client,
error_status_t *result
)
{
callee_client_entry_t *this_client;
#ifdef PERFMON
RPC_SS_TAKE_FROM_CALLEE_CLNT_N;
#endif
*result = error_status_ok;
*p_close_client = NULL;
this_client = p_context->p_client_entry;
--this_client->count;
if ( (this_client->count != 0) || (this_client->rundown_pending) )
{
if (this_client->first_context == p_context)
{
this_client->first_context = p_context->next_in_client;
}
else
{
(p_context->prev_in_client)->next_in_client
= p_context->next_in_client;
}
if (this_client->last_context == p_context)
{
this_client->last_context = p_context->prev_in_client;
}
else
{
(p_context->next_in_client)->prev_in_client
= p_context->prev_in_client;
}
if (this_client->count != 0)
{
#ifdef PERFMON
RPC_SS_TAKE_FROM_CALLEE_CLNT_X;
#endif
return;
}
}
*p_close_client = this_client->client;
if ( ! this_client->rundown_pending )
{
rpc_ss_ctx_remove_client_entry(this_client);
}
#ifdef PERFMON
RPC_SS_TAKE_FROM_CALLEE_CLNT_X;
#endif
}
void rpc_ss_rundown_client
(
rpc_client_handle_t failed_client
)
{
error_status_t result;
callee_client_entry_t *this_client;
callee_context_entry_t *this_context;
rpc_client_handle_t close_client = NULL;
rpc_ss_rundown_list_elt * volatile rundown_list;
rpc_ss_rundown_list_elt * volatile rundown_elt;
rundown_list = NULL;
#ifdef PERFMON
RPC_SS_RUNDOWN_CLIENT_N;
#endif
RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
DPRINT(("Seized context tables\n"));
for( this_client = &client_table[HASH_CLIENT_ID(failed_client)];
(this_client != NULL) && (close_client == NULL);
this_client = this_client->next_h_client )
{
if (this_client->client == failed_client)
{
while (this_client->ref_count != 0)
{
this_client->rundown_pending = idl_true;
RPC_SS_THREADS_CONDITION_WAIT(&this_client->cond_var,
&rpc_ss_context_table_mutex);
RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
}
if (this_client->count == 0)
{
rpc_ss_ctx_remove_client_entry(this_client);
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
DPRINT(("Released context tables\n"));
#ifdef PERFMON
RPC_SS_RUNDOWN_CLIENT_X;
#endif
return;
}
this_client->rundown_pending = idl_false;
while (close_client == NULL)
{
this_context = this_client->first_context;
rundown_elt = (rpc_ss_rundown_list_elt *)
malloc(sizeof(rpc_ss_rundown_list_elt));
if (rundown_elt == NULL)
{
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
DPRINT(("Released context tables\n"));
#if 0
rpc_dce_svc_printf (
__FILE__, __LINE__,
"%x",
rpc_svc_libidl,
svc_c_sev_error,
rpc_m_ctxrundown_nomem,
this_client );
#endif
return;
}
rundown_elt->rundown = this_context->rundown;
rundown_elt->user_context = this_context->user_context;
rundown_elt->next = rundown_list;
rundown_list = rundown_elt;
rpc_ss_lkddest_callee_context
(&this_context->uuid,&close_client,&result);
}
}
}
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
DPRINT(("Released context tables\n"));
while (rundown_list != NULL)
{
if (rundown_list->rundown != NULL)
{
DCETHREAD_TRY
(*(rundown_list->rundown))(rundown_list->user_context);
DCETHREAD_CATCH_ALL(caught)
#if 0
rpc_dce_svc_printf (
__FILE__, __LINE__,
%x %x %x",
rpc_svc_libidl,
svc_c_sev_error,
rpc_m_ctxrundown_exc,
rundown_list->rundown,
rundown_list->user_context,
this_client );
#endif
DCETHREAD_ENDTRY
}
rundown_elt = rundown_list;
rundown_list = rundown_list->next;
free(rundown_elt);
}
#ifdef PERFMON
RPC_SS_RUNDOWN_CLIENT_X;
#endif
return;
}
/******************************************************************************/
/* */
/* Increment the count of managers currently using contexts of a client */
/* */
/* ENTRY POINT INTO LIBIDL FROM STUB */
/* */
/******************************************************************************/
void rpc_ss_ctx_client_ref_count_inc
(
handle_t h,
error_status_t *p_st
)
{
rpc_client_handle_t client_id; /* ID of client */
callee_client_entry_t *this_client;
ndr_boolean is_new_client; /* Dummy procedure parameter */
/* This could be the first access to the context tables */
RPC_SS_INIT_CONTEXT
rpc_binding_inq_client(h, &client_id, p_st);
if (*p_st != error_status_ok)
return;
RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
this_client = &client_table[HASH_CLIENT_ID(client_id)];
while (this_client != NULL)
{
if (this_client->client == client_id)
{
(this_client->ref_count)++;
break;
}
this_client = this_client->next_h_client;
}
if (this_client == NULL)
{
/* Client does not yet have any active contexts.
Current manager is expected to create one */
rpc_ss_add_to_callee_client(client_id, NULL, &is_new_client, p_st);
}
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
}
/******************************************************************************/
/* */
/* Decrement the count of managers currently using contexts of a client */
/* */
/* ENTRY POINT INTO LIBIDL FROM STUB */
/* */
/******************************************************************************/
void rpc_ss_ctx_client_ref_count_dec
(
handle_t h,
error_status_t *p_st
)
{
rpc_client_handle_t client_id; /* ID of client */
callee_client_entry_t *this_client;
rpc_binding_inq_client(h, &client_id, p_st);
if (*p_st != error_status_ok)
return;
RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
this_client = &client_table[HASH_CLIENT_ID(client_id)];
while (this_client != NULL)
{
if (this_client->client == client_id)
{
(this_client->ref_count)--;
if (this_client->rundown_pending)
{
RPC_SS_THREADS_CONDITION_SIGNAL(&this_client->cond_var);
}
else if ((this_client->count == 0) && (this_client->ref_count == 0))
{
/* This thread expected to create a context for the client,
but didn't */
rpc_ss_ctx_remove_client_entry(this_client);
}
break;
}
this_client = this_client->next_h_client;
}
/* Can reach here with this_client == NULL if last context of client
was destroyed by manager */
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
}
/*
* OT_8069 - context reference counts incorrectly maintained
* Duplicate routines so existing stubs still work
*/
/******************************************************************************/
/* */
/* Increment the count of managers currently using contexts of a client */
/* */
/* ENTRY POINT INTO LIBIDL FROM STUB */
/* */
/******************************************************************************/
void rpc_ss_ctx_client_ref_count_i_2
(
handle_t h,
rpc_client_handle_t *p_client_id, /* [out] ID of client, or NULL */
error_status_t *p_st
)
{
rpc_client_handle_t client_id; /* ID of client */
callee_client_entry_t *this_client;
ndr_boolean is_new_client; /* Dummy procedure parameter */
/* This could be the first access to the context tables */
RPC_SS_INIT_CONTEXT
rpc_binding_inq_client(h, p_client_id, p_st);
if (*p_st != error_status_ok)
{
*p_client_id = NULL;
return;
}
client_id = *p_client_id;
RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
this_client = &client_table[HASH_CLIENT_ID(client_id)];
while (this_client != NULL)
{
if (this_client->client == client_id)
{
(this_client->ref_count)++;
break;
}
this_client = this_client->next_h_client;
}
if (this_client == NULL)
{
/* Client does not yet have any active contexts.
Current manager is expected to create one */
rpc_ss_add_to_callee_client(client_id, NULL, &is_new_client, p_st);
}
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
}
/******************************************************************************/
/* */
/* Decrement the count of managers currently using contexts of a client */
/* */
/* ENTRY POINT INTO LIBIDL FROM STUB */
/* */
/******************************************************************************/
void rpc_ss_ctx_client_ref_count_d_2
(
handle_t h ATTRIBUTE_UNUSED,
rpc_client_handle_t client_id /* [in] ID of client, or NULL */
)
{
callee_client_entry_t *this_client;
if (client_id == NULL)
return;
RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
this_client = &client_table[HASH_CLIENT_ID(client_id)];
while (this_client != NULL)
{
if (this_client->client == client_id)
{
(this_client->ref_count)--;
if (this_client->rundown_pending)
{
RPC_SS_THREADS_CONDITION_SIGNAL(&this_client->cond_var);
}
else if ((this_client->count == 0) && (this_client->ref_count == 0))
{
/* This thread expected to create a context for the client,
but didn't */
rpc_ss_ctx_remove_client_entry(this_client);
}
break;
}
this_client = this_client->next_h_client;
}
/* Can reach here with this_client == NULL if last context of client
was destroyed by manager */
RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
}
#ifdef CTXEETEST
void show_client_context_chain
(
callee_client_entry_t *p_client_entry
)
{
callee_context_entry_t *this_context;
this_context = p_client_entry->first_context;
printf("\t\tForward context chain\n");
while (this_context != NULL)
{
printf("\t\t\t %s %lx\n",
&this_context->uuid,
this_context->user_context);
this_context = this_context->next_in_client;
}
this_context = p_client_entry->last_context;
printf("\t\tBackward context chain\n");
while (this_context != NULL)
{
printf("\t\t\t %s %lx\n",
&this_context->uuid,
this_context->user_context);
this_context = this_context->prev_in_client;
}
}
void dump_client_table(
void
)
{
int i;
callee_client_entry_t *this_client;
for (i=0; i<CALLEE_CLIENT_TABLE_SIZE; i++)
{
if ( (client_table[i].client != NULL)
|| (client_table[i].next_h_client != NULL) )
{
printf("Forward client chain for client slot %d\n",i);
this_client = &client_table[i];
while (ndr_true)
{
if (this_client->client != NULL)
{
printf("\t %lx %d\n",this_client->client,this_client->count);
show_client_context_chain(this_client);
}
if (this_client->next_h_client == NULL) break;
this_client = this_client->next_h_client;
}
printf("Backward chain to same slot\n");
while (ndr_true)
{
if (this_client->client != NULL)
{
printf("\t %lx %d\n",this_client->client,this_client->count);
show_client_context_chain(this_client);
}
if (this_client->prev_h_client == NULL) break;
this_client = this_client->prev_h_client;
}
}
}
}
#endif