1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 *
10 * 1.  Redistributions of source code must retain the above copyright
11 *     notice, this list of conditions and the following disclaimer.
12 * 2.  Redistributions in binary form must reproduce the above copyright
13 *     notice, this list of conditions and the following disclaimer in the
14 *     documentation and/or other materials provided with the distribution.
15 * 3.  Neither the name of Apple Inc. ("Apple") nor the names of its
16 *     contributors may be used to endorse or promote products derived from
17 *     this software without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
20 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
23 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
28 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 *
30 * Portions of this software have been released under the following terms:
31 *
32 * (c) Copyright 1989-1993 OPEN SOFTWARE FOUNDATION, INC.
33 * (c) Copyright 1989-1993 HEWLETT-PACKARD COMPANY
34 * (c) Copyright 1989-1993 DIGITAL EQUIPMENT CORPORATION
35 *
36 * To anyone who acknowledges that this file is provided "AS IS"
37 * without any express or implied warranty:
38 * permission to use, copy, modify, and distribute this file for any
39 * purpose is hereby granted without fee, provided that the above
40 * copyright notices and this notice appears in all source code copies,
41 * and that none of the names of Open Software Foundation, Inc., Hewlett-
42 * Packard Company or Digital Equipment Corporation be used
43 * in advertising or publicity pertaining to distribution of the software
44 * without specific, written prior permission.  Neither Open Software
45 * Foundation, Inc., Hewlett-Packard Company nor Digital
46 * Equipment Corporation makes any representations about the suitability
47 * of this software for any purpose.
48 *
49 * Copyright (c) 2007, Novell, Inc. All rights reserved.
50 * Redistribution and use in source and binary forms, with or without
51 * modification, are permitted provided that the following conditions
52 * are met:
53 *
54 * 1.  Redistributions of source code must retain the above copyright
55 *     notice, this list of conditions and the following disclaimer.
56 * 2.  Redistributions in binary form must reproduce the above copyright
57 *     notice, this list of conditions and the following disclaimer in the
58 *     documentation and/or other materials provided with the distribution.
59 * 3.  Neither the name of Novell Inc. nor the names of its contributors
60 *     may be used to endorse or promote products derived from this
61 *     this software without specific prior written permission.
62 *
63 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
64 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
65 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
66 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY
67 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
68 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
69 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
70 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
71 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
72 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
73 *
74 * @APPLE_LICENSE_HEADER_END@
75 */
76
77/*
78**
79**  NAME:
80**
81**      ctxeecli.c
82**
83**  FACILITY:
84**
85**      IDL Stub Runtime Support
86**
87**  ABSTRACT:
88**
89**      Maintain callee stub's table of clients
90**
91**
92*/
93#if HAVE_CONFIG_H
94#include <config.h>
95#endif
96
97/* The ordering of the following 3 includes should NOT be changed! */
98#include <dce/rpc.h>
99#include <dce/stubbase.h>
100#include <lsysdep.h>
101
102#include <ctxeertl.h>
103
104#   include <stdio.h>
105
106#ifdef PERFMON
107#include <dce/idl_log.h>
108#endif
109
110#ifndef DEBUG_VERBOSE
111#   define NDEBUG
112#endif
113#include <assert.h>
114
115#ifdef CTXEETEST
116#   define DPRINT(ARGS) printf ARGS
117#else
118#   define DPRINT(ARGS) /* ARGS */
119#endif
120
121#include <rpcsvc.h>
122
123/*
124 *  Data structure for element of list of rundown actions
125 */
126typedef struct rpc_ss_rundown_list_elt {
127    ctx_rundown_fn_p_t rundown; /* Pointer to rundown routine for context */
128    rpc_ss_context_t user_context;
129    struct rpc_ss_rundown_list_elt *next;
130} rpc_ss_rundown_list_elt;
131
132/*  Number of client slots in hash table.
133*/
134#define CALLEE_CLIENT_TABLE_SIZE 256
135
136/*  Macro to hash client IDs.  ID is actually a pointer to memory,
137**  probably allocated by malloc and 16-byte aligned.
138*/
139#define HASH_CLIENT_ID(ID) (((unsigned long)(ID) >> 4) % CALLEE_CLIENT_TABLE_SIZE)
140
141static callee_client_entry_t *client_table = NULL;
142
143/*  Allocate callee client lookup table and initialize pointers to NULL.
144*/
145void rpc_ss_init_callee_client_table(
146    void
147)
148{
149#ifdef PERFMON
150    RPC_SS_INIT_CALLEE_CLNT_TABLE_N;
151#endif
152
153    assert(!client_table);      /* Must not be called more than once. */
154
155    client_table = (callee_client_entry_t *)malloc(
156        CALLEE_CLIENT_TABLE_SIZE * sizeof(callee_client_entry_t)
157    );
158
159    if (!client_table)
160        DCETHREAD_RAISE(rpc_x_no_memory);
161
162    memset(client_table, 0, CALLEE_CLIENT_TABLE_SIZE * sizeof(callee_client_entry_t));
163
164#ifdef PERFMON
165    RPC_SS_INIT_CALLEE_CLNT_TABLE_X;
166#endif
167
168}
169
170/******************************************************************************/
171/*                                                                            */
172/*    Add a context to the list being maintained for a client                 */
173/*      or create a client table entry for a client which it is expected      */
174/*      a context will be created                                             */
175/*                                                                            */
176/*  Assumes that the context table mutex is locked                            */
177/*                                                                            */
178/******************************************************************************/
179void rpc_ss_add_to_callee_client
180(
181    /* [in] */ rpc_client_handle_t ctx_client,
182                             /* Client for whom there is or will be a context */
183    /* [in] */ callee_context_entry_t *p_context,  /* Pointer to the context,
184                     NULL if we are creating a client table entry to which
185                     we expect a context to be attached later */
186    /* [out] */ndr_boolean *p_is_new_client,
187                                          /* TRUE => first context for client */
188    error_status_t *result         /* Function result */
189)
190{
191    int slot;    /* Index of the slot in the lookup table the client
192                    should be in */
193    ndr_boolean creating_at_home;    /* TRUE if new client being created in slot
194                                    in lookup table */
195    callee_client_entry_t *this_client, *next_client, *new_client;
196
197#ifdef PERFMON
198    RPC_SS_ADD_TO_CALLEE_CLIENT_N;
199#endif
200    /* Now find which chain */
201    slot = HASH_CLIENT_ID(ctx_client);
202
203    /* Look for the client in a chain based on the slot.
204        Note the following possibilities exist for the slot:
205        1) Unoccupied. No chain.
206        2) Occupied. No chain.
207        3) Occupied. Chain exists.
208        4) Unoccupied. Chain exists.
209        This is because once a client record is
210            created it is constrained by the presence of threads machinery
211            not to be moved */
212    this_client = &client_table[slot];
213    while (ndr_true)
214    {
215        if ( ctx_client == this_client->client )
216        {
217            ++this_client->count;
218            *p_is_new_client = (this_client->count == 1);
219            /* Linkage of contexts within client */
220            p_context->p_client_entry = this_client;
221            p_context->prev_in_client = this_client->last_context;
222            p_context->next_in_client = NULL;
223            if (this_client->first_context == NULL)
224            {
225                /* Client table entry created before manager was entered */
226                this_client->first_context = p_context;
227            }
228            else
229            {
230                (this_client->last_context)->next_in_client = p_context;
231            }
232            this_client->last_context = p_context;
233            *result = error_status_ok;
234#ifdef PERFMON
235            RPC_SS_ADD_TO_CALLEE_CLIENT_X;
236#endif
237            return;
238        }
239        next_client = this_client->next_h_client;
240        if (next_client == NULL) break;
241        this_client = next_client;
242    }
243
244    /* Get here only if this is the first context for the client */
245    creating_at_home = (client_table[slot].client == NULL);
246    if (creating_at_home)
247    {
248        /* The slot in the table is unoccupied */
249        new_client = &client_table[slot];
250    }
251    else
252    {
253        new_client = (callee_client_entry_t *)
254                            malloc(sizeof(callee_client_entry_t));
255        if (new_client == NULL)
256        {
257            RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
258            DPRINT(("Released context tables\n"));
259            DCETHREAD_RAISE( rpc_x_no_memory );
260        }
261        this_client->next_h_client = new_client;
262        new_client->prev_h_client = this_client;
263        new_client->next_h_client = NULL;
264    }
265    new_client->client = ctx_client;
266    new_client->rundown_pending = idl_false;
267    RPC_SS_THREADS_CONDITION_CREATE(&(new_client->cond_var));
268    if (p_context == NULL)
269    {
270        /* New (locking) code. Client table entry created before manager
271            entered */
272        new_client->count = 0;
273        new_client->first_context = NULL;
274        new_client->last_context = NULL;
275        new_client->ref_count = 1;
276        *p_is_new_client = ndr_false;
277    }
278    else
279    {
280        /* Old (non-locking) code. Client table entry created when non-null
281            context marshalled after return from manager */
282        new_client->count = 1;
283        new_client->first_context = p_context;
284        new_client->last_context = p_context;
285        new_client->ref_count = 0;
286        p_context->p_client_entry = new_client;
287        p_context->prev_in_client = NULL;
288        p_context->next_in_client = NULL;
289        *p_is_new_client = ndr_true;
290    }
291    *result = error_status_ok;
292
293#ifdef PERFMON
294    RPC_SS_ADD_TO_CALLEE_CLIENT_X;
295#endif
296}
297
298/******************************************************************************/
299/*                                                                            */
300/*    Remove a client entry from the client hash table                        */
301/*                                                                            */
302/*  Assumes that the context table mutex is locked                            */
303/*                                                                            */
304/******************************************************************************/
305static void rpc_ss_ctx_remove_client_entry
306(
307    /* [in] */callee_client_entry_t *this_client
308)
309{
310    callee_client_entry_t *next_client, *prev_client;
311
312    RPC_SS_THREADS_CONDITION_DELETE(&(this_client->cond_var));
313    prev_client = this_client->prev_h_client;
314    next_client = this_client->next_h_client;
315
316    if (prev_client != NULL)
317    {
318        /* Not at head of chain, cut this client out */
319        prev_client->next_h_client = next_client;
320        if (next_client != NULL)
321        {
322            next_client->prev_h_client = prev_client;
323        }
324        free((char_p_t)this_client);
325    }
326    else    /* Head of chain */
327    {
328        this_client->client = NULL;
329        /* Hash link fields left intact. Other fields will be initialized
330            next time a context is created in this slot */
331    }
332}
333
334/******************************************************************************/
335/*                                                                            */
336/*    Remove a context from the list being maintained for a client            */
337/*                                                                            */
338/*  Assumes that the context table mutex is locked                            */
339/*                                                                            */
340/******************************************************************************/
341void rpc_ss_take_from_callee_client
342(
343    /* [in] */ callee_context_entry_t *p_context,  /* Pointer to the context */
344    /* [out] */ rpc_client_handle_t *p_close_client,
345                                /* Ptr to NULL or client to stop monitoring */
346    /* [out] */ error_status_t *result /* Function result */
347)
348{
349    callee_client_entry_t *this_client;
350
351#ifdef PERFMON
352    RPC_SS_TAKE_FROM_CALLEE_CLNT_N;
353#endif
354
355    *result = error_status_ok;
356    *p_close_client = NULL;
357    this_client = p_context->p_client_entry;
358    --this_client->count;
359    if ( (this_client->count != 0) || (this_client->rundown_pending) )
360    {
361        /* We will not be destroying the client entry */
362        /* Remove this context from the chain */
363        if (this_client->first_context == p_context)
364        {
365            /* Context to be removed is first in client chain */
366            this_client->first_context = p_context->next_in_client;
367        }
368        else
369        {
370            (p_context->prev_in_client)->next_in_client
371                = p_context->next_in_client;
372        }
373        if (this_client->last_context == p_context)
374        {
375            /* Context to be removed is last in client chain */
376            this_client->last_context = p_context->prev_in_client;
377        }
378        else
379        {
380            (p_context->next_in_client)->prev_in_client
381                = p_context->prev_in_client;
382        }
383        if (this_client->count != 0)
384        {
385#ifdef PERFMON
386            RPC_SS_TAKE_FROM_CALLEE_CLNT_X;
387#endif
388            return;
389        }
390    }
391
392    /* Was last context for client */
393    *p_close_client = this_client->client;
394    if ( ! this_client->rundown_pending )
395    {
396        rpc_ss_ctx_remove_client_entry(this_client);
397    }
398
399#ifdef PERFMON
400    RPC_SS_TAKE_FROM_CALLEE_CLNT_X;
401#endif
402
403}
404
405/******************************************************************************/
406/*                                                                            */
407/*    Run down all the contexts a client owns                                 */
408/*                                                                            */
409/******************************************************************************/
410void rpc_ss_rundown_client
411(
412    /* [in] */ rpc_client_handle_t failed_client
413)
414{
415    error_status_t result;
416    callee_client_entry_t *this_client;
417    callee_context_entry_t *this_context;
418    rpc_client_handle_t close_client = NULL;
419                                       /* NULL or client to stop monitoring */
420    /* FIXME: is the volatility set correctly here? */
421    rpc_ss_rundown_list_elt * volatile rundown_list;
422    rpc_ss_rundown_list_elt * volatile rundown_elt;
423
424    rundown_list = NULL;
425
426#ifdef PERFMON
427    RPC_SS_RUNDOWN_CLIENT_N;
428#endif
429
430    RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
431    DPRINT(("Seized context tables\n"));
432    for( this_client = &client_table[HASH_CLIENT_ID(failed_client)];
433         (this_client != NULL) && (close_client == NULL);
434         this_client = this_client->next_h_client )
435    {
436        if (this_client->client == failed_client)
437        {
438            while (this_client->ref_count != 0)
439            {
440                this_client->rundown_pending = idl_true;
441                RPC_SS_THREADS_CONDITION_WAIT(&this_client->cond_var,
442                                              &rpc_ss_context_table_mutex);
443                                        /* Mutex has been released */
444                RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
445            }
446            if (this_client->count == 0)
447            {
448                /* The manager closed the contexts while a rundown was
449                        pending */
450                rpc_ss_ctx_remove_client_entry(this_client);
451                RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
452                DPRINT(("Released context tables\n"));
453#ifdef PERFMON
454                RPC_SS_RUNDOWN_CLIENT_X;
455#endif
456                return;
457            }
458            /* Need to clear the rundown pending flag so that the client
459                entry can be deleted */
460            this_client->rundown_pending = idl_false;
461            while (close_client == NULL)
462            {
463                /* Loop until all contexts for this client have been removed
464                    from the context table. Note that each iteration brings
465                    a different context to first_context position */
466                this_context = this_client->first_context;
467                rundown_elt = (rpc_ss_rundown_list_elt *)
468                                malloc(sizeof(rpc_ss_rundown_list_elt));
469                if (rundown_elt == NULL)
470                {
471                    RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
472                    DPRINT(("Released context tables\n"));
473		    /*
474		     * rpc_m_ctxrundown_nomem
475		     * "Out of memory while trying to run down contexts of client %x"
476		     */
477#if 0
478                    rpc_dce_svc_printf (
479                        __FILE__, __LINE__,
480                         "%x",
481                         rpc_svc_libidl,
482                         svc_c_sev_error,
483                         rpc_m_ctxrundown_nomem,
484                         this_client );
485#endif
486                    return;
487                }
488                rundown_elt->rundown = this_context->rundown;
489                rundown_elt->user_context = this_context->user_context;
490                rundown_elt->next = rundown_list;
491                rundown_list = rundown_elt;
492                rpc_ss_lkddest_callee_context
493                                    (&this_context->uuid,&close_client,&result);
494            }
495        }
496    }
497    RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
498    DPRINT(("Released context tables\n"));
499    while (rundown_list != NULL)
500    {
501        if (rundown_list->rundown != NULL)
502        {
503            DCETHREAD_TRY
504            (*(rundown_list->rundown))(rundown_list->user_context);
505            DCETHREAD_CATCH_ALL(caught)
506		/*
507		 * rpc_m_ctxrundown_exc
508		 * "Exception in routine at %x, running down context %x of client %x"
509		 */
510#if 0
511            rpc_dce_svc_printf (
512                __FILE__, __LINE__,
513                 %x %x %x",
514                 rpc_svc_libidl,
515                 svc_c_sev_error,
516                 rpc_m_ctxrundown_exc,
517                 rundown_list->rundown,
518                 rundown_list->user_context,
519                 this_client );
520#endif
521            DCETHREAD_ENDTRY
522        }
523        rundown_elt = rundown_list;
524        rundown_list = rundown_list->next;
525        free(rundown_elt);
526    }
527
528#ifdef PERFMON
529    RPC_SS_RUNDOWN_CLIENT_X;
530#endif
531
532    return;
533}
534
535/******************************************************************************/
536/*                                                                            */
537/*    Increment the count of managers currently using contexts of a client    */
538/*                                                                            */
539/*  ENTRY POINT INTO LIBIDL FROM STUB                                         */
540/*                                                                            */
541/******************************************************************************/
542void rpc_ss_ctx_client_ref_count_inc
543(
544    handle_t h,
545    error_status_t *p_st
546)
547{
548    rpc_client_handle_t client_id;     /* ID of client */
549    callee_client_entry_t *this_client;
550    ndr_boolean is_new_client;      /* Dummy procedure parameter */
551
552    /* This could be the first access to the context tables */
553    RPC_SS_INIT_CONTEXT
554
555    rpc_binding_inq_client(h, &client_id, p_st);
556    if (*p_st != error_status_ok)
557        return;
558    RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
559    this_client = &client_table[HASH_CLIENT_ID(client_id)];
560    while (this_client != NULL)
561    {
562        if (this_client->client == client_id)
563        {
564            (this_client->ref_count)++;
565            break;
566        }
567        this_client = this_client->next_h_client;
568    }
569    if (this_client == NULL)
570    {
571        /* Client does not yet have any active contexts.
572                                 Current manager is expected to create one */
573        rpc_ss_add_to_callee_client(client_id, NULL, &is_new_client, p_st);
574    }
575    RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
576}
577
578/******************************************************************************/
579/*                                                                            */
580/*    Decrement the count of managers currently using contexts of a client    */
581/*                                                                            */
582/*  ENTRY POINT INTO LIBIDL FROM STUB                                         */
583/*                                                                            */
584/******************************************************************************/
585void rpc_ss_ctx_client_ref_count_dec
586(
587    handle_t h,
588    error_status_t *p_st
589)
590{
591    rpc_client_handle_t client_id;     /* ID of client */
592    callee_client_entry_t *this_client;
593
594    rpc_binding_inq_client(h, &client_id, p_st);
595    if (*p_st != error_status_ok)
596        return;
597    RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
598    this_client = &client_table[HASH_CLIENT_ID(client_id)];
599    while (this_client != NULL)
600    {
601        if (this_client->client == client_id)
602        {
603            (this_client->ref_count)--;
604            if (this_client->rundown_pending)
605            {
606                RPC_SS_THREADS_CONDITION_SIGNAL(&this_client->cond_var);
607            }
608            else if ((this_client->count == 0) && (this_client->ref_count == 0))
609            {
610                /* This thread expected to create a context for the client,
611                    but didn't */
612                rpc_ss_ctx_remove_client_entry(this_client);
613            }
614            break;
615        }
616        this_client = this_client->next_h_client;
617    }
618    /* Can reach here with this_client == NULL if last context of client
619        was destroyed by manager */
620    RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
621}
622
623/*
624 *  OT_8069 - context reference counts incorrectly maintained
625 *  Duplicate routines so existing stubs still work
626 */
627
628/******************************************************************************/
629/*                                                                            */
630/*    Increment the count of managers currently using contexts of a client    */
631/*                                                                            */
632/*  ENTRY POINT INTO LIBIDL FROM STUB                                         */
633/*                                                                            */
634/******************************************************************************/
635void rpc_ss_ctx_client_ref_count_i_2
636(
637    handle_t h,
638    rpc_client_handle_t *p_client_id,     /* [out] ID of client, or NULL */
639    error_status_t *p_st
640)
641{
642    rpc_client_handle_t client_id;     /* ID of client */
643    callee_client_entry_t *this_client;
644    ndr_boolean is_new_client;      /* Dummy procedure parameter */
645
646    /* This could be the first access to the context tables */
647    RPC_SS_INIT_CONTEXT
648
649    rpc_binding_inq_client(h, p_client_id, p_st);
650    if (*p_st != error_status_ok)
651    {
652        *p_client_id = NULL;
653        return;
654    }
655    client_id = *p_client_id;
656    RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
657    this_client = &client_table[HASH_CLIENT_ID(client_id)];
658    while (this_client != NULL)
659    {
660        if (this_client->client == client_id)
661        {
662            (this_client->ref_count)++;
663            break;
664        }
665        this_client = this_client->next_h_client;
666    }
667    if (this_client == NULL)
668    {
669        /* Client does not yet have any active contexts.
670                                 Current manager is expected to create one */
671        rpc_ss_add_to_callee_client(client_id, NULL, &is_new_client, p_st);
672    }
673    RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
674}
675
676/******************************************************************************/
677/*                                                                            */
678/*    Decrement the count of managers currently using contexts of a client    */
679/*                                                                            */
680/*  ENTRY POINT INTO LIBIDL FROM STUB                                         */
681/*                                                                            */
682/******************************************************************************/
683void rpc_ss_ctx_client_ref_count_d_2
684(
685    handle_t h ATTRIBUTE_UNUSED,
686    rpc_client_handle_t client_id     /* [in] ID of client, or NULL */
687)
688{
689    callee_client_entry_t *this_client;
690
691    if (client_id == NULL)
692        return;
693    RPC_SS_THREADS_MUTEX_LOCK(&rpc_ss_context_table_mutex);
694    this_client = &client_table[HASH_CLIENT_ID(client_id)];
695    while (this_client != NULL)
696    {
697        if (this_client->client == client_id)
698        {
699            (this_client->ref_count)--;
700            if (this_client->rundown_pending)
701            {
702                RPC_SS_THREADS_CONDITION_SIGNAL(&this_client->cond_var);
703            }
704            else if ((this_client->count == 0) && (this_client->ref_count == 0))
705            {
706                /* This thread expected to create a context for the client,
707                    but didn't */
708                rpc_ss_ctx_remove_client_entry(this_client);
709            }
710            break;
711        }
712        this_client = this_client->next_h_client;
713    }
714    /* Can reach here with this_client == NULL if last context of client
715        was destroyed by manager */
716    RPC_SS_THREADS_MUTEX_UNLOCK(&rpc_ss_context_table_mutex);
717}
718
719#ifdef CTXEETEST
720void show_client_context_chain
721(
722    callee_client_entry_t *p_client_entry
723)
724{
725    callee_context_entry_t *this_context;
726
727    this_context = p_client_entry->first_context;
728    printf("\t\tForward context chain\n");
729    while (this_context != NULL)
730    {
731        printf("\t\t\t %s %lx\n",
732                &this_context->uuid,
733                this_context->user_context);
734        this_context = this_context->next_in_client;
735    }
736
737    this_context = p_client_entry->last_context;
738    printf("\t\tBackward context chain\n");
739    while (this_context != NULL)
740    {
741        printf("\t\t\t %s %lx\n",
742                &this_context->uuid,
743                this_context->user_context);
744        this_context = this_context->prev_in_client;
745    }
746
747}
748
749void dump_client_table(
750    void
751)
752{
753    int i;
754    callee_client_entry_t *this_client;
755
756    for (i=0; i<CALLEE_CLIENT_TABLE_SIZE; i++)
757    {
758        if  ( (client_table[i].client != NULL)
759             || (client_table[i].next_h_client != NULL) )
760        {
761            printf("Forward client chain for client slot %d\n",i);
762            this_client = &client_table[i];
763            while (ndr_true)
764            {
765                if (this_client->client != NULL)
766                {
767                    printf("\t %lx %d\n",this_client->client,this_client->count);
768                    show_client_context_chain(this_client);
769                }
770                if (this_client->next_h_client == NULL) break;
771                this_client = this_client->next_h_client;
772            }
773            printf("Backward chain to same slot\n");
774            while (ndr_true)
775            {
776                if (this_client->client != NULL)
777                {
778                    printf("\t %lx %d\n",this_client->client,this_client->count);
779                    show_client_context_chain(this_client);
780                }
781                if (this_client->prev_h_client == NULL) break;
782                this_client = this_client->prev_h_client;
783            }
784        }
785    }
786}
787#endif
788