2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 #pragma ident "%Z%%M% %I% %E% SMI"
10 * The contents of this file are subject to the Netscape Public
11 * License Version 1.1 (the "License"); you may not use this file
12 * except in compliance with the License. You may obtain a copy of
13 * the License at http://www.mozilla.org/NPL/
15 * Software distributed under the License is distributed on an "AS
16 * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
17 * implied. See the License for the specific language governing
18 * rights and limitations under the License.
20 * The Original Code is Mozilla Communicator client code, released
23 * The Initial Developer of the Original Code is Netscape
24 * Communications Corporation. Portions created by Netscape are
25 * Copyright (C) 1998-1999 Netscape Communications Corporation. All
32 * Thread callback functions for libldap that use the NSPR (Netscape
33 * Portable Runtime) thread API.
44 #include <sys/types.h>
48 #endif /* _SOLARIS_SDK */
50 #include "ldappr-int.h"
57 * Grow thread private data arrays 10 elements at a time.
59 #define PRLDAP_TPD_ARRAY_INCREMENT 10
62 * Structures and types:
65 * Structure used by libldap thread callbacks to maintain error information.
67 typedef struct prldap_errorinfo
{
74 * Structure used to maintain thread-private data. At the present time,
75 * only error info. is thread-private. One of these structures is allocated
78 typedef struct prldap_tpd_header
{
79 int ptpdh_tpd_count
; /* # of data items allocated */
80 void **ptpdh_dataitems
; /* array of data items */
84 * Structure used by associate a PRLDAP thread-private data index with an
85 * LDAP session handle. One of these exists for each active LDAP session
88 typedef struct prldap_tpd_map
{
89 LDAP
*prtm_ld
; /* non-NULL if in use */
90 PRUintn prtm_index
; /* index into TPD array */
91 struct prldap_tpd_map
*prtm_next
;
95 extern mutex_t inited_mutex
;
96 #endif /* _SOLARIS_SDK */
102 * prldap_map_list points to all of the PRLDAP_TPDMap structures
103 * we have ever allocated. We recycle them as we open and close LDAP
106 static PRLDAP_TPDMap
*prldap_map_list
= NULL
;
110 * The prldap_map_mutex is used to protect access to the prldap_map_list.
112 static PRLock
*prldap_map_mutex
= NULL
;
115 * The prldap_tpd_maxindex value is used to track the largest TPD array
116 * index we have used.
118 static PRInt32 prldap_tpd_maxindex
= -1;
121 * prldap_tpdindex is an NSPR thread private data index we use to
122 * maintain our own thread-private data. It is initialized inside
125 static PRUintn prldap_tpdindex
= 0;
128 * The prldap_callonce_init_tpd structure is used by NSPR to ensure
129 * that prldap_init_tpd() is called at most once.
131 static PRCallOnceType prldap_callonce_init_tpd
= { 0, 0, 0 };
135 * Private function prototypes:
137 static void prldap_set_ld_error( int err
, char *matched
, char *errmsg
,
139 static int prldap_get_ld_error( char **matchedp
, char **errmsgp
,
142 static void *prldap_mutex_alloc( void );
143 static void prldap_mutex_free( void *mutex
);
144 static int prldap_mutex_lock( void *mutex
);
145 static int prldap_mutex_unlock( void *mutex
);
146 static void *prldap_get_thread_id( void );
148 static PRStatus
prldap_init_tpd( void );
149 static PRLDAP_TPDMap
*prldap_allocate_map( LDAP
*ld
);
150 static void prldap_return_map( PRLDAP_TPDMap
*map
);
151 static PRUintn
prldap_new_tpdindex( void );
152 static int prldap_set_thread_private( PRInt32 tpdindex
, void *priv
);
153 static void *prldap_get_thread_private( PRInt32 tpdindex
);
154 static PRLDAP_TPDHeader
*prldap_tsd_realloc( PRLDAP_TPDHeader
*tsdhdr
,
156 static void prldap_tsd_destroy( void *priv
);
161 * Install NSPR thread functions into ld (if ld is NULL, they are installed
162 * as the default functions for new LDAP * handles).
164 * Returns 0 if all goes well and -1 if not.
167 prldap_install_thread_functions( LDAP
*ld
, int shared
)
169 struct ldap_thread_fns tfns
;
170 struct ldap_extra_thread_fns xtfns
;
173 if ( PR_CallOnce( &prldap_callonce_init_tpd
, prldap_init_tpd
)
175 ldap_set_lderrno( ld
, LDAP_LOCAL_ERROR
, NULL
, NULL
);
178 #endif /* _SOLARIS_SDK */
180 /* set thread function pointers */
181 memset( &tfns
, '\0', sizeof(struct ldap_thread_fns
) );
182 tfns
.ltf_get_errno
= prldap_get_system_errno
;
183 tfns
.ltf_set_errno
= prldap_set_system_errno
;
185 tfns
.ltf_mutex_alloc
= prldap_mutex_alloc
;
186 tfns
.ltf_mutex_free
= prldap_mutex_free
;
187 tfns
.ltf_mutex_lock
= prldap_mutex_lock
;
188 tfns
.ltf_mutex_unlock
= prldap_mutex_unlock
;
190 tfns
.ltf_get_lderrno
= NULL
;
191 tfns
.ltf_set_lderrno
= NULL
;
193 tfns
.ltf_get_lderrno
= prldap_get_ld_error
;
194 tfns
.ltf_set_lderrno
= prldap_set_ld_error
;
197 * If this is a real ld (i.e., we are not setting the global
198 * defaults) allocate thread private data for error information.
199 * If ld is NULL we do not do this here but it is done in
200 * prldap_thread_new_handle().
202 if (( tfns
.ltf_lderrno_arg
= (void *)prldap_allocate_map( ld
))
210 if ( ldap_set_option( ld
, LDAP_OPT_THREAD_FN_PTRS
,
211 (void *)&tfns
) != 0 ) {
213 prldap_return_map( (PRLDAP_TPDMap
*)tfns
.ltf_lderrno_arg
);
218 /* set extended thread function pointers */
219 memset( &xtfns
, '\0', sizeof(struct ldap_extra_thread_fns
) );
220 xtfns
.ltf_threadid_fn
= prldap_get_thread_id
;
221 if ( ldap_set_option( ld
, LDAP_OPT_EXTRA_THREAD_FN_PTRS
,
222 (void *)&xtfns
) != 0 ) {
231 prldap_mutex_alloc( void )
233 return( (void *)PR_NewLock());
238 prldap_mutex_free( void *mutex
)
240 PR_DestroyLock( (PRLock
*)mutex
);
245 prldap_mutex_lock( void *mutex
)
247 PR_Lock( (PRLock
*)mutex
);
253 prldap_mutex_unlock( void *mutex
)
255 if ( PR_Unlock( (PRLock
*)mutex
) == PR_FAILURE
) {
264 prldap_get_thread_id( void )
267 return ((void *)thr_self());
269 return( (void *)PR_GetCurrentThread());
275 prldap_get_ld_error( char **matchedp
, char **errmsgp
, void *errorarg
)
278 PRLDAP_ErrorInfo
*eip
;
280 if (( map
= (PRLDAP_TPDMap
*)errorarg
) != NULL
&& ( eip
=
281 (PRLDAP_ErrorInfo
*)prldap_get_thread_private(
282 map
->prtm_index
)) != NULL
) {
283 if ( matchedp
!= NULL
) {
284 *matchedp
= eip
->plei_matched
;
286 if ( errmsgp
!= NULL
) {
287 *errmsgp
= eip
->plei_errmsg
;
289 return( eip
->plei_lderrno
);
291 if ( matchedp
!= NULL
) {
294 if ( errmsgp
!= NULL
) {
297 return( LDAP_LOCAL_ERROR
); /* punt */
303 prldap_set_ld_error( int err
, char *matched
, char *errmsg
, void *errorarg
)
306 PRLDAP_ErrorInfo
*eip
;
308 if (( map
= (PRLDAP_TPDMap
*)errorarg
) != NULL
) {
309 if (( eip
= (PRLDAP_ErrorInfo
*)prldap_get_thread_private(
310 map
->prtm_index
)) == NULL
) {
312 * Error info. has not yet been allocated for this thread.
313 * Do so now. Note that we free this memory only for the
314 * thread that calls prldap_thread_dispose_handle(), which
315 * should be the one that called ldap_unbind() -- see
316 * prldap_return_map(). Not freeing the memory used by
317 * other threads is deemed acceptable since it will be
318 * recycled and used by other LDAP sessions. All of the
319 * thread-private memory is freed when a thread exits
320 * (inside the prldap_tsd_destroy() function).
322 eip
= (PRLDAP_ErrorInfo
*)PR_Calloc( 1,
323 sizeof( PRLDAP_ErrorInfo
));
327 (void)prldap_set_thread_private( map
->prtm_index
, eip
);
330 eip
->plei_lderrno
= err
;
331 if ( eip
->plei_matched
!= NULL
) {
332 ldap_memfree( eip
->plei_matched
);
334 eip
->plei_matched
= matched
;
335 if ( eip
->plei_errmsg
!= NULL
) {
336 ldap_memfree( eip
->plei_errmsg
);
338 eip
->plei_errmsg
= errmsg
;
345 * Called when a new LDAP * session handle is allocated.
346 * Allocate thread-private data for error information, but only if
347 * it has not already been allocated and the get_ld_error callback has
348 * been installed. If ld is not NULL when prldap_install_thread_functions()
349 * is called, we will have already allocated the thread-private data there.
352 prldap_thread_new_handle( LDAP
*ld
, void *sessionarg
)
354 struct ldap_thread_fns tfns
;
357 if ( ldap_get_option( ld
, LDAP_OPT_THREAD_FN_PTRS
, (void *)&tfns
) != 0 ) {
358 return( LDAP_LOCAL_ERROR
);
361 if ( tfns
.ltf_lderrno_arg
== NULL
&& tfns
.ltf_get_lderrno
!= NULL
) {
362 if (( tfns
.ltf_lderrno_arg
= (void *)prldap_allocate_map( ld
)) == NULL
363 || ldap_set_option( ld
, LDAP_OPT_THREAD_FN_PTRS
,
364 (void *)&tfns
) != 0 ) {
365 return( LDAP_LOCAL_ERROR
);
370 return( LDAP_SUCCESS
);
375 * Called when an LDAP * session handle is being destroyed.
376 * Clean up our thread private data map.
379 prldap_thread_dispose_handle( LDAP
*ld
, void *sessionarg
)
382 struct ldap_thread_fns tfns
;
384 if ( ldap_get_option( ld
, LDAP_OPT_THREAD_FN_PTRS
,
385 (void *)&tfns
) == 0 &&
386 tfns
.ltf_lderrno_arg
!= NULL
) {
387 prldap_return_map( (PRLDAP_TPDMap
*)tfns
.ltf_lderrno_arg
);
395 prldap_init_tpd( void )
397 if (( prldap_map_mutex
= PR_NewLock()) == NULL
|| PR_NewThreadPrivateIndex(
398 &prldap_tpdindex
, prldap_tsd_destroy
) != PR_SUCCESS
) {
399 return( PR_FAILURE
);
402 prldap_map_list
= NULL
;
404 return( PR_SUCCESS
);
409 * Function: prldap_allocate_map()
410 * Description: allocate a thread-private data map to use for a new
411 * LDAP session handle.
412 * Returns: a pointer to the TPD map or NULL if none available.
414 static PRLDAP_TPDMap
*
415 prldap_allocate_map( LDAP
*ld
)
417 PRLDAP_TPDMap
*map
, *prevmap
;
419 PR_Lock( prldap_map_mutex
);
422 * first look for a map that is already allocated but free to be re-used
425 for ( map
= prldap_map_list
; map
!= NULL
; map
= map
->prtm_next
) {
426 if ( map
->prtm_ld
== NULL
) {
433 * if none we found (map == NULL), try to allocate a new one and add it
434 * to the end of our global list.
439 tpdindex
= prldap_new_tpdindex();
440 map
= (PRLDAP_TPDMap
*)PR_Malloc( sizeof( PRLDAP_TPDMap
));
442 map
->prtm_index
= tpdindex
;
443 map
->prtm_next
= NULL
;
444 if ( prevmap
== NULL
) {
445 prldap_map_list
= map
;
447 prevmap
->prtm_next
= map
;
453 map
->prtm_ld
= ld
; /* now marked as "in use" */
454 /* since we are reusing...reset */
455 /* to initial state */
456 (void)prldap_set_thread_private( map
->prtm_index
, NULL
);
459 PR_Unlock( prldap_map_mutex
);
466 * Function: prldap_return_map()
467 * Description: return a thread-private data map to the pool of ones
468 * available for re-use.
471 prldap_return_map( PRLDAP_TPDMap
*map
)
473 PRLDAP_ErrorInfo
*eip
;
475 PR_Lock( prldap_map_mutex
);
478 * Dispose of thread-private LDAP error information. Note that this
479 * only disposes of the memory consumed on THIS thread, but that is
480 * okay. See the comment in prldap_set_ld_error() for the reason why.
482 if (( eip
= (PRLDAP_ErrorInfo
*)prldap_get_thread_private(
483 map
->prtm_index
)) != NULL
&&
484 prldap_set_thread_private( map
->prtm_index
, NULL
) == 0 ) {
485 if ( eip
->plei_matched
!= NULL
) {
486 ldap_memfree( eip
->plei_matched
);
488 if ( eip
->plei_errmsg
!= NULL
) {
489 ldap_memfree( eip
->plei_errmsg
);
495 /* mark map as available for re-use */
498 PR_Unlock( prldap_map_mutex
);
503 * Function: prldap_new_tpdindex()
504 * Description: allocate a thread-private data index.
505 * Returns: the new index.
508 prldap_new_tpdindex( void )
512 tpdindex
= (PRUintn
)PR_AtomicIncrement( &prldap_tpd_maxindex
);
518 * Function: prldap_set_thread_private()
519 * Description: store a piece of thread-private data.
520 * Returns: 0 if successful and -1 if not.
523 prldap_set_thread_private( PRInt32 tpdindex
, void *priv
)
525 PRLDAP_TPDHeader
*tsdhdr
;
527 if ( tpdindex
> prldap_tpd_maxindex
) {
528 return( -1 ); /* bad index */
531 tsdhdr
= (PRLDAP_TPDHeader
*)PR_GetThreadPrivate( prldap_tpdindex
);
532 if ( tsdhdr
== NULL
|| tpdindex
>= tsdhdr
->ptpdh_tpd_count
) {
533 tsdhdr
= prldap_tsd_realloc( tsdhdr
, tpdindex
);
534 if ( tsdhdr
== NULL
) {
535 return( -1 ); /* realloc failed */
539 tsdhdr
->ptpdh_dataitems
[ tpdindex
] = priv
;
545 * Function: prldap_get_thread_private()
546 * Description: retrieve a piece of thread-private data. If not set,
548 * Returns: 0 if successful and -1 if not.
551 prldap_get_thread_private( PRInt32 tpdindex
)
553 PRLDAP_TPDHeader
*tsdhdr
;
555 tsdhdr
= (PRLDAP_TPDHeader
*)PR_GetThreadPrivate( prldap_tpdindex
);
556 if ( tsdhdr
== NULL
) {
557 return( NULL
); /* no thread private data */
560 if ( tpdindex
>= tsdhdr
->ptpdh_tpd_count
561 || tsdhdr
->ptpdh_dataitems
== NULL
) {
562 return( NULL
); /* fewer data items than requested index */
565 return( tsdhdr
->ptpdh_dataitems
[ tpdindex
] );
570 * Function: prldap_tsd_realloc()
571 * Description: enlarge the thread-private data array.
572 * Returns: the new PRLDAP_TPDHeader value (non-NULL if successful).
573 * Note: tsdhdr can be NULL (allocates a new PRLDAP_TPDHeader).
575 static PRLDAP_TPDHeader
*
576 prldap_tsd_realloc( PRLDAP_TPDHeader
*tsdhdr
, int maxindex
)
578 void *newdataitems
= NULL
;
581 if ( tsdhdr
== NULL
) {
582 /* allocate a new thread private data header */
583 if (( tsdhdr
= PR_Calloc( 1, sizeof( PRLDAP_TPDHeader
))) == NULL
) {
586 (void)PR_SetThreadPrivate( prldap_tpdindex
, tsdhdr
);
590 * Make the size of the new array the next highest multiple of
591 * the array increment value that is greater than maxindex.
593 count
= PRLDAP_TPD_ARRAY_INCREMENT
*
594 ( 1 + ( maxindex
/ PRLDAP_TPD_ARRAY_INCREMENT
));
596 /* increase the size of the data item array if necessary */
597 if ( count
> tsdhdr
->ptpdh_tpd_count
) {
598 newdataitems
= (PRLDAP_ErrorInfo
*)PR_Calloc( count
, sizeof( void * ));
599 if ( newdataitems
== NULL
) {
602 if ( tsdhdr
->ptpdh_dataitems
!= NULL
) { /* preserve old data */
603 memcpy( newdataitems
, tsdhdr
->ptpdh_dataitems
,
604 tsdhdr
->ptpdh_tpd_count
* sizeof( void * ));
605 PR_Free( tsdhdr
->ptpdh_dataitems
);
608 tsdhdr
->ptpdh_tpd_count
= count
;
609 tsdhdr
->ptpdh_dataitems
= newdataitems
;
617 * Function: prldap_tsd_destroy()
618 * Description: Free a thread-private data array. Installed as an NSPR TPD
619 * destructor function
621 * Note: this function assumes that each TPD item installed at the PRLDAP
622 * level can be freed with a call to PR_Free().
625 prldap_tsd_destroy( void *priv
)
627 PRLDAP_TPDHeader
*tsdhdr
;
630 tsdhdr
= (PRLDAP_TPDHeader
*)priv
;
631 if ( tsdhdr
!= NULL
) {
632 if ( tsdhdr
->ptpdh_dataitems
!= NULL
) {
633 for ( i
= 0; i
< tsdhdr
->ptpdh_tpd_count
; ++i
) {
634 if ( tsdhdr
->ptpdh_dataitems
[ i
] != NULL
) {
635 PR_Free( tsdhdr
->ptpdh_dataitems
[ i
] );
636 tsdhdr
->ptpdh_dataitems
[ i
] = NULL
;
639 PR_Free( tsdhdr
->ptpdh_dataitems
);
640 tsdhdr
->ptpdh_dataitems
= NULL
;
648 #pragma init(prldap_nspr_init)
649 static mutex_t nspr_init_lock
= DEFAULTMUTEX
;
650 static int nspr_initialized
= 0;
653 * Initialize NSPR once
657 prldap_nspr_init(void) {
658 struct sigaction action
;
661 * For performance reason, test it here first
663 if (nspr_initialized
!= 0)
666 (void) mutex_lock(&nspr_init_lock
);
667 /* Make sure PR_Init() is executed only once */
668 if (nspr_initialized
== 0) {
670 * PR_Init changes the signal handler of SIGPIPE to SIG_IGN.
671 * Save the original and restore it after PR_Init.
673 (void) sigaction(SIGPIPE
, NULL
, &action
);
675 if (PR_Initialized() == PR_FALSE
) {
677 * PR_Init() changes the current thread's
678 * priority. Save and restore the priority.
681 (void) thr_getprio(thr_self(), &priority
);
682 PR_Init(PR_USER_THREAD
, PR_PRIORITY_NORMAL
, 0);
683 (void) thr_setprio(thr_self(), priority
);
685 nspr_initialized
= 1;
687 * Restore signal handling attributes of SIGPIPE
689 (void) sigaction(SIGPIPE
, &action
, NULL
);
691 (void) mutex_unlock(&nspr_init_lock
);