Back to index

lightning-sunbird  0.9+nobinonly
ldappr-threads.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is Mozilla Communicator client code, released
00015  * March 31, 1998.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998-1999
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either the GNU General Public License Version 2 or later (the "GPL"), or
00026  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 
00038 /*
00039  * Thread callback functions for libldap that use the NSPR (Netscape
00040  * Portable Runtime) thread API.
00041  *
00042  */
00043 
00044 #include "ldappr-int.h"
00045 
00046 /*
00047  * Macros:
00048  */
00049 /*
00050  * Grow thread private data arrays 10 elements at a time.
00051  */
00052 #define PRLDAP_TPD_ARRAY_INCREMENT 10
00053 
00054 /*
00055  * Structures and types:
00056  */
00057 /*
00058  * Structure used by libldap thread callbacks to maintain error information.
00059  */
00060 typedef struct prldap_errorinfo {
00061     int              plei_lderrno;
00062     char      *plei_matched;
00063     char      *plei_errmsg;
00064 } PRLDAP_ErrorInfo;
00065 
00066 /*
00067  * Structure used to maintain thread-private data. At the present time,
00068  * only error info. is thread-private.  One of these structures is allocated
00069  * for each thread.
00070  */
00071 typedef struct prldap_tpd_header {
00072     int                     ptpdh_tpd_count;     /* # of data items allocated */
00073     void             **ptpdh_dataitems;   /* array of data items */
00074 } PRLDAP_TPDHeader;
00075 
00076 /*
00077  * Structure used by associate a PRLDAP thread-private data index with an
00078  * LDAP session handle. One of these exists for each active LDAP session
00079  * handle.
00080  */
00081 typedef struct prldap_tpd_map {
00082     LDAP                    *prtm_ld;     /* non-NULL if in use */
00083     PRUintn                 prtm_index;   /* index into TPD array */
00084     struct prldap_tpd_map   *prtm_next;
00085 } PRLDAP_TPDMap;
00086 
00087 
00088 
00089 /*
00090  * Static Variables:
00091  */
00092 /*
00093  * prldap_map_list points to all of the PRLDAP_TPDMap structures
00094  * we have ever allocated.  We recycle them as we open and close LDAP
00095  * sessions.
00096  */
00097 static PRLDAP_TPDMap *prldap_map_list = NULL;
00098 
00099 
00100 /*
00101  * The prldap_map_mutex is used to protect access to the prldap_map_list.
00102  */
00103 static PRLock *prldap_map_mutex = NULL;
00104 
00105 /*
00106  * The prldap_tpd_maxindex value is used to track the largest TPD array
00107  * index we have used.
00108  */
00109 static PRInt32       prldap_tpd_maxindex = -1;
00110 
00111 /*
00112  * prldap_tpdindex is an NSPR thread private data index we use to
00113  * maintain our own thread-private data. It is initialized inside
00114  * prldap_init_tpd().
00115  */
00116 static PRUintn       prldap_tpdindex = 0;
00117 
00118 /*
00119  * The prldap_callonce_init_tpd structure is used by NSPR to ensure
00120  * that prldap_init_tpd() is called at most once.
00121  */
00122 static PRCallOnceType prldap_callonce_init_tpd = { 0, 0, 0 };
00123 
00124 
00125 /*
00126  * Private function prototypes:
00127  */
00128 static void prldap_set_ld_error( int err, char *matched, char *errmsg,
00129        void *errorarg );
00130 static int prldap_get_ld_error( char **matchedp, char **errmsgp,
00131        void *errorarg );
00132 static void *prldap_mutex_alloc( void );
00133 static void prldap_mutex_free( void *mutex );
00134 static int prldap_mutex_lock( void *mutex );
00135 static int prldap_mutex_unlock( void *mutex );
00136 static void *prldap_get_thread_id( void );
00137 static PRStatus prldap_init_tpd( void );
00138 static PRLDAP_TPDMap *prldap_allocate_map( LDAP *ld );
00139 static void prldap_return_map( PRLDAP_TPDMap *map );
00140 static PRUintn prldap_new_tpdindex( void );
00141 static int prldap_set_thread_private( PRInt32 tpdindex, void *priv );
00142 static void *prldap_get_thread_private( PRInt32 tpdindex );
00143 static PRLDAP_TPDHeader *prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr,
00144        int maxindex );
00145 static void prldap_tsd_destroy( void *priv );
00146 
00147 
00148 /*
00149  * Install NSPR thread functions into ld (if ld is NULL, they are installed
00150  * as the default functions for new LDAP * handles).
00151  *
00152  * Returns 0 if all goes well and -1 if not.
00153  */
00154 int
00155 prldap_install_thread_functions( LDAP *ld, int shared )
00156 {
00157     struct ldap_thread_fns         tfns;
00158     struct ldap_extra_thread_fns   xtfns;
00159 
00160     if ( PR_CallOnce( &prldap_callonce_init_tpd, prldap_init_tpd )
00161               != PR_SUCCESS ) {
00162        ldap_set_lderrno( ld, LDAP_LOCAL_ERROR, NULL, NULL );
00163        return( -1 );
00164     }
00165 
00166     /* set thread function pointers */
00167     memset( &tfns, '\0', sizeof(struct ldap_thread_fns) );
00168     tfns.ltf_get_errno = prldap_get_system_errno;
00169     tfns.ltf_set_errno = prldap_set_system_errno;
00170     if ( shared ) {
00171        tfns.ltf_mutex_alloc = prldap_mutex_alloc;
00172        tfns.ltf_mutex_free = prldap_mutex_free;
00173        tfns.ltf_mutex_lock = prldap_mutex_lock;
00174        tfns.ltf_mutex_unlock = prldap_mutex_unlock;
00175        tfns.ltf_get_lderrno = prldap_get_ld_error;
00176        tfns.ltf_set_lderrno = prldap_set_ld_error;
00177        if ( ld != NULL ) {
00178            /*
00179             * If this is a real ld (i.e., we are not setting the global
00180             * defaults) allocate thread private data for error information.
00181             * If ld is NULL we do not do this here but it is done in
00182             * prldap_thread_new_handle().
00183             */
00184            if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld ))
00185                   == NULL ) {
00186               return( -1 );
00187            }
00188        }
00189     }
00190 
00191     if ( ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS,
00192            (void *)&tfns ) != 0 ) {
00193        prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg );
00194        return( -1 );
00195     }
00196 
00197     /* set extended thread function pointers */
00198     memset( &xtfns, '\0', sizeof(struct ldap_extra_thread_fns) );
00199     xtfns.ltf_threadid_fn = prldap_get_thread_id;
00200     if ( ldap_set_option( ld, LDAP_OPT_EXTRA_THREAD_FN_PTRS,
00201            (void *)&xtfns ) != 0 ) {
00202        return( -1 );
00203     }
00204 
00205     return( 0 );
00206 }
00207 
00208 
00209 static void *
00210 prldap_mutex_alloc( void )
00211 {
00212     return( (void *)PR_NewLock());
00213 }
00214 
00215 
00216 static void
00217 prldap_mutex_free( void *mutex )
00218 {
00219     PR_DestroyLock( (PRLock *)mutex );
00220 }
00221 
00222 
00223 static int
00224 prldap_mutex_lock( void *mutex )
00225 {
00226     PR_Lock( (PRLock *)mutex );
00227     return( 0 );
00228 }
00229 
00230 
00231 static int
00232 prldap_mutex_unlock( void *mutex )
00233 {
00234     if ( PR_Unlock( (PRLock *)mutex ) == PR_FAILURE ) {
00235        return( -1 );
00236     }
00237 
00238     return( 0 );
00239 }
00240 
00241 
00242 static void *
00243 prldap_get_thread_id( void )
00244 {
00245     return( (void *)PR_GetCurrentThread());
00246 }
00247 
00248 
00249 static int
00250 prldap_get_ld_error( char **matchedp, char **errmsgp, void *errorarg )
00251 {
00252     PRLDAP_TPDMap    *map;
00253     PRLDAP_ErrorInfo *eip;
00254 
00255     if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL && ( eip =
00256            (PRLDAP_ErrorInfo *)prldap_get_thread_private(
00257            map->prtm_index )) != NULL ) {
00258        if ( matchedp != NULL ) {
00259            *matchedp = eip->plei_matched;
00260        }
00261        if ( errmsgp != NULL ) {
00262            *errmsgp = eip->plei_errmsg;
00263        }
00264        return( eip->plei_lderrno );
00265     } else {
00266        if ( matchedp != NULL ) {
00267            *matchedp = NULL;
00268        }
00269        if ( errmsgp != NULL ) {
00270            *errmsgp = NULL;
00271        }
00272        return( LDAP_LOCAL_ERROR ); /* punt */
00273     }
00274 }
00275 
00276 
00277 static void
00278 prldap_set_ld_error( int err, char *matched, char *errmsg, void *errorarg )
00279 {
00280     PRLDAP_TPDMap    *map;
00281     PRLDAP_ErrorInfo *eip;
00282 
00283     if (( map = (PRLDAP_TPDMap *)errorarg ) != NULL ) {
00284        if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private(
00285               map->prtm_index )) == NULL ) {
00286            /*
00287             * Error info. has not yet been allocated for this thread.
00288             * Do so now.  Note that we free this memory only for the
00289             * thread that calls prldap_thread_dispose_handle(), which
00290             * should be the one that called ldap_unbind() -- see
00291             * prldap_return_map().  Not freeing the memory used by
00292             * other threads is deemed acceptable since it will be
00293             * recycled and used by other LDAP sessions.  All of the
00294             * thread-private memory is freed when a thread exits
00295             * (inside the prldap_tsd_destroy() function).
00296             */
00297            eip = (PRLDAP_ErrorInfo *)PR_Calloc( 1,
00298                   sizeof( PRLDAP_ErrorInfo ));
00299            if ( eip == NULL ) {
00300               return;       /* punt */
00301            }
00302            (void)prldap_set_thread_private( map->prtm_index, eip );
00303        }
00304 
00305        eip->plei_lderrno = err;
00306        if ( eip->plei_matched != NULL ) {
00307            ldap_memfree( eip->plei_matched );
00308        }
00309        eip->plei_matched = matched;
00310        if ( eip->plei_errmsg != NULL ) {
00311            ldap_memfree( eip->plei_errmsg );
00312        }
00313        eip->plei_errmsg = errmsg;
00314     }
00315 }
00316 
00317 
00318 /*
00319  * Called when a new LDAP * session handle is allocated.
00320  * Allocate thread-private data for error information, but only if
00321  * it has not already been allocated and the get_ld_error callback has
00322  * been installed.  If ld is not NULL when prldap_install_thread_functions()
00323  * is called, we will have already allocated the thread-private data there.
00324  */
00325 int
00326 prldap_thread_new_handle( LDAP *ld, void *sessionarg )
00327 {
00328     struct ldap_thread_fns  tfns;
00329 
00330     if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS, (void *)&tfns ) != 0 ) {
00331        return( LDAP_LOCAL_ERROR );
00332     }
00333 
00334     if ( tfns.ltf_lderrno_arg == NULL && tfns.ltf_get_lderrno != NULL ) {
00335        if (( tfns.ltf_lderrno_arg = (void *)prldap_allocate_map( ld )) == NULL
00336               || ldap_set_option( ld, LDAP_OPT_THREAD_FN_PTRS,
00337               (void *)&tfns ) != 0 ) {
00338            return( LDAP_LOCAL_ERROR );
00339        }
00340     }
00341 
00342     return( LDAP_SUCCESS );
00343 }
00344 
00345 
00346 /*
00347  * Called when an LDAP * session handle is being destroyed.
00348  * Clean up our thread private data map.
00349  */
00350 void
00351 prldap_thread_dispose_handle( LDAP *ld, void *sessionarg )
00352 {
00353     struct ldap_thread_fns  tfns;
00354 
00355     if ( ldap_get_option( ld, LDAP_OPT_THREAD_FN_PTRS,
00356            (void *)&tfns ) == 0 &&
00357            tfns.ltf_lderrno_arg != NULL ) {
00358        prldap_return_map( (PRLDAP_TPDMap *)tfns.ltf_lderrno_arg );
00359     }
00360 }
00361 
00362 
00363 static PRStatus
00364 prldap_init_tpd( void )
00365 {
00366     if (( prldap_map_mutex = PR_NewLock()) == NULL || PR_NewThreadPrivateIndex(
00367               &prldap_tpdindex, prldap_tsd_destroy ) != PR_SUCCESS ) {
00368        return( PR_FAILURE );
00369     }
00370 
00371     prldap_map_list = NULL;
00372 
00373     return( PR_SUCCESS );
00374 }
00375 
00376 
00377 /*
00378  * Function: prldap_allocate_map()
00379  * Description: allocate a thread-private data map to use for a new
00380  *     LDAP session handle.
00381  * Returns: a pointer to the TPD map or NULL if none available.
00382  */
00383 static PRLDAP_TPDMap *
00384 prldap_allocate_map( LDAP *ld )
00385 {
00386     PRLDAP_TPDMap    *map, *prevmap;
00387 
00388     PR_Lock( prldap_map_mutex );
00389 
00390     /*
00391      * first look for a map that is already allocated but free to be re-used
00392      */
00393     prevmap = NULL;
00394     for ( map = prldap_map_list; map != NULL; map = map->prtm_next ) {
00395        if ( map->prtm_ld == NULL ) {
00396            break;
00397        }
00398        prevmap = map;
00399     }
00400 
00401     /*
00402      * if none we found (map == NULL), try to allocate a new one and add it
00403      * to the end of our global list.
00404      */
00405     if ( map == NULL ) {
00406        PRUintn       tpdindex;
00407 
00408        tpdindex = prldap_new_tpdindex();
00409        map = (PRLDAP_TPDMap *)PR_Malloc( sizeof( PRLDAP_TPDMap ));
00410        if ( map != NULL ) {
00411            map->prtm_index = tpdindex;
00412            map->prtm_next = NULL;
00413            if ( prevmap == NULL ) {
00414               prldap_map_list = map;
00415            } else {
00416               prevmap->prtm_next = map;
00417            }
00418        }
00419     }
00420 
00421     if ( map != NULL ) {
00422        map->prtm_ld = ld;   /* now marked as "in use" */
00423                             /* since we are reusing...reset */
00424                             /* to initial state */
00425        (void)prldap_set_thread_private( map->prtm_index, NULL );
00426     }
00427 
00428     PR_Unlock( prldap_map_mutex );
00429 
00430     return( map );
00431 }
00432 
00433 
00434 /*
00435  * Function: prldap_return_map()
00436  * Description: return a thread-private data map to the pool of ones
00437  *     available for re-use.
00438  */
00439 static void
00440 prldap_return_map( PRLDAP_TPDMap *map )
00441 {
00442     PRLDAP_ErrorInfo *eip;
00443 
00444     PR_Lock( prldap_map_mutex );
00445 
00446     /*
00447      * Dispose of thread-private LDAP error information.  Note that this
00448      * only disposes of the memory consumed on THIS thread, but that is
00449      * okay.  See the comment in prldap_set_ld_error() for the reason why.
00450      */
00451     if (( eip = (PRLDAP_ErrorInfo *)prldap_get_thread_private(
00452               map->prtm_index )) != NULL &&
00453               prldap_set_thread_private( map->prtm_index, NULL ) == 0 ) {
00454        if ( eip->plei_matched != NULL ) {
00455            ldap_memfree( eip->plei_matched );
00456        }
00457        if ( eip->plei_errmsg != NULL ) {
00458            ldap_memfree( eip->plei_errmsg );
00459        }
00460 
00461        PR_Free( eip );
00462     }
00463 
00464     /* mark map as available for re-use */
00465     map->prtm_ld = NULL;
00466 
00467     PR_Unlock( prldap_map_mutex );
00468 }
00469 
00470 
00471 /*
00472  * Function: prldap_new_tpdindex()
00473  * Description: allocate a thread-private data index.
00474  * Returns: the new index.
00475  */
00476 static PRUintn
00477 prldap_new_tpdindex( void )
00478 {
00479     PRUintn   tpdindex;
00480 
00481     tpdindex = (PRUintn)PR_AtomicIncrement( &prldap_tpd_maxindex );
00482     return( tpdindex );
00483 }
00484 
00485 
00486 /*
00487  * Function: prldap_set_thread_private()
00488  * Description: store a piece of thread-private data.
00489  * Returns: 0 if successful and -1 if not.
00490  */
00491 static int
00492 prldap_set_thread_private( PRInt32 tpdindex, void *priv )
00493 {
00494     PRLDAP_TPDHeader *tsdhdr;
00495 
00496     if ( tpdindex > prldap_tpd_maxindex ) {
00497        return( -1 ); /* bad index */ 
00498     }
00499 
00500     tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex );
00501     if ( tsdhdr == NULL || tpdindex >= tsdhdr->ptpdh_tpd_count ) {
00502        tsdhdr = prldap_tsd_realloc( tsdhdr, tpdindex );
00503        if ( tsdhdr == NULL ) {
00504            return( -1 );    /* realloc failed */
00505        }
00506     }
00507 
00508     tsdhdr->ptpdh_dataitems[ tpdindex ] = priv;
00509     return( 0 );
00510 }
00511 
00512 
00513 /*
00514  * Function: prldap_get_thread_private()
00515  * Description: retrieve a piece of thread-private data.  If not set,
00516  *     NULL is returned.
00517  * Returns: 0 if successful and -1 if not.
00518  */
00519 static void *
00520 prldap_get_thread_private( PRInt32 tpdindex )
00521 {
00522     PRLDAP_TPDHeader *tsdhdr;
00523 
00524     tsdhdr = (PRLDAP_TPDHeader *)PR_GetThreadPrivate( prldap_tpdindex );
00525     if ( tsdhdr == NULL ) {
00526        return( NULL );      /* no thread private data */
00527     }
00528 
00529     if ( tpdindex >= tsdhdr->ptpdh_tpd_count
00530               || tsdhdr->ptpdh_dataitems == NULL ) {
00531        return( NULL );      /* fewer data items than requested index */
00532     }
00533 
00534     return( tsdhdr->ptpdh_dataitems[ tpdindex ] );
00535 }
00536 
00537 
00538 /*
00539  * Function: prldap_tsd_realloc()
00540  * Description: enlarge the thread-private data array.
00541  * Returns: the new PRLDAP_TPDHeader value (non-NULL if successful).
00542  * Note: tsdhdr can be NULL (allocates a new PRLDAP_TPDHeader).
00543  */
00544 static PRLDAP_TPDHeader *
00545 prldap_tsd_realloc( PRLDAP_TPDHeader *tsdhdr, int maxindex )
00546 {
00547     void      *newdataitems = NULL;
00548     int              count;
00549 
00550     if ( tsdhdr == NULL ) {
00551        /* allocate a new thread private data header */
00552        if (( tsdhdr = PR_Calloc( 1, sizeof( PRLDAP_TPDHeader ))) == NULL ) {
00553            return( NULL );
00554        }
00555        (void)PR_SetThreadPrivate( prldap_tpdindex, tsdhdr );
00556     }
00557 
00558     /*
00559      * Make the size of the new array the next highest multiple of
00560      * the array increment value that is greater than maxindex.
00561      */
00562     count = PRLDAP_TPD_ARRAY_INCREMENT *
00563               ( 1 + ( maxindex / PRLDAP_TPD_ARRAY_INCREMENT ));
00564 
00565     /* increase the size of the data item array if necessary */
00566     if ( count > tsdhdr->ptpdh_tpd_count  ) {
00567        newdataitems = (PRLDAP_ErrorInfo *)PR_Calloc( count, sizeof( void * ));
00568        if ( newdataitems == NULL ) {
00569            return( NULL );
00570        }
00571        if ( tsdhdr->ptpdh_dataitems != NULL ) {  /* preserve old data */
00572            memcpy( newdataitems, tsdhdr->ptpdh_dataitems,
00573                      tsdhdr->ptpdh_tpd_count * sizeof( void * ));
00574            PR_Free( tsdhdr->ptpdh_dataitems );
00575        }
00576 
00577        tsdhdr->ptpdh_tpd_count = count;
00578        tsdhdr->ptpdh_dataitems = newdataitems;
00579     }
00580 
00581     return( tsdhdr );
00582 }
00583 
00584 
00585 /*
00586  * Function: prldap_tsd_destroy()
00587  * Description: Free a thread-private data array. Installed as an NSPR TPD
00588  *     destructor function
00589  * Returns: nothing.
00590  * Note: this function assumes that each TPD item installed at the PRLDAP
00591  *     level can be freed with a call to PR_Free().
00592  */
00593 static void
00594 prldap_tsd_destroy( void *priv )
00595 {
00596     PRLDAP_TPDHeader *tsdhdr;
00597     int                     i;
00598 
00599     tsdhdr = (PRLDAP_TPDHeader *)priv;
00600     if ( tsdhdr != NULL ) {
00601        if ( tsdhdr->ptpdh_dataitems != NULL ) {
00602            for ( i = 0; i < tsdhdr->ptpdh_tpd_count; ++i ) {
00603               if ( tsdhdr->ptpdh_dataitems[ i ] != NULL ) {
00604                   PR_Free( tsdhdr->ptpdh_dataitems[ i ] );
00605                   tsdhdr->ptpdh_dataitems[ i ] = NULL;
00606               }
00607            }
00608            PR_Free( tsdhdr->ptpdh_dataitems );
00609            tsdhdr->ptpdh_dataitems = NULL;
00610        }
00611        PR_Free( tsdhdr );
00612     }
00613 }