]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/cache.c
Import alias deref finding bug fix from devel
[openldap] / servers / slapd / back-ldbm / cache.c
1 /* cache.c - routines to maintain an in-core cache of entries */
2 /* $OpenLDAP$ */
3 /*
4  * Copyright 1998-2000 The OpenLDAP Foundation, All Rights Reserved.
5  * COPYING RESTRICTIONS APPLY, see COPYRIGHT file
6  */
7
8 #include "portable.h"
9
10 #include <stdio.h>
11
12 #include <ac/errno.h>
13 #include <ac/string.h>
14 #include <ac/socket.h>
15
16 #include "slap.h"
17
18 #include "back-ldbm.h"
19
20 /* LDBM backend specific entry info -- visible only to the cache */
21 typedef struct ldbm_entry_info {
22         ldap_pvt_thread_rdwr_t  lei_rdwr;       /* reader/writer lock */
23
24         /*
25          * remaining fields require backend cache lock to access
26          * These items are specific to the LDBM backend and should
27          * be hidden.
28          */
29         int             lei_state;      /* for the cache */
30 #define CACHE_ENTRY_UNDEFINED   0
31 #define CACHE_ENTRY_CREATING    1
32 #define CACHE_ENTRY_READY               2
33 #define CACHE_ENTRY_DELETED             3
34
35         int             lei_refcnt;     /* # threads ref'ing this entry */
36         Entry   *lei_lrunext;   /* for cache lru list */
37         Entry   *lei_lruprev;
38 } EntryInfo;
39 #undef LEI
40 #define LEI(e)  ((EntryInfo *) ((e)->e_private))
41
42 static int      cache_delete_entry_internal(Cache *cache, Entry *e);
43 #ifdef LDAP_DEBUG
44 static void     lru_print(Cache *cache);
45 #endif
46
47 static int
48 cache_entry_rdwr_lock(Entry *e, int rw)
49 {
50         Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%slock: ID: %ld\n",
51                 rw ? "w" : "r", e->e_id, 0);
52
53         if (rw)
54                 return ldap_pvt_thread_rdwr_wlock(&LEI(e)->lei_rdwr);
55         else
56                 return ldap_pvt_thread_rdwr_rlock(&LEI(e)->lei_rdwr);
57 }
58
59 static int
60 cache_entry_rdwr_trylock(Entry *e, int rw)
61 {
62         Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%strylock: ID: %ld\n",
63                 rw ? "w" : "r", e->e_id, 0);
64
65         if (rw)
66                 return ldap_pvt_thread_rdwr_wtrylock(&LEI(e)->lei_rdwr);
67         else
68                 return ldap_pvt_thread_rdwr_rtrylock(&LEI(e)->lei_rdwr);
69 }
70
71 static int
72 cache_entry_rdwr_unlock(Entry *e, int rw)
73 {
74         Debug( LDAP_DEBUG_ARGS, "entry_rdwr_%sunlock: ID: %ld\n",
75                 rw ? "w" : "r", e->e_id, 0);
76
77         if (rw)
78                 return ldap_pvt_thread_rdwr_wunlock(&LEI(e)->lei_rdwr);
79         else
80                 return ldap_pvt_thread_rdwr_runlock(&LEI(e)->lei_rdwr);
81 }
82
83 static int
84 cache_entry_rdwr_init(Entry *e)
85 {
86         return ldap_pvt_thread_rdwr_init( &LEI(e)->lei_rdwr );
87 }
88
89 static int
90 cache_entry_rdwr_destroy(Entry *e)
91 {
92         return ldap_pvt_thread_rdwr_destroy( &LEI(e)->lei_rdwr );
93 }
94
95 static int
96 cache_entry_private_init( Entry*e )
97 {
98         assert( e->e_private == NULL );
99
100         if( e->e_private != NULL ) {
101                 /* this should never happen */
102                 return 1;
103         }
104
105         e->e_private = ch_calloc(1, sizeof(struct ldbm_entry_info));
106
107         if( cache_entry_rdwr_init( e ) != 0 ) {
108                 free( LEI(e) );
109                 e->e_private = NULL;
110                 return 1;
111         } 
112
113         return 0;
114 }
115
116 static int
117 cache_entry_private_destroy( Entry*e )
118 {
119         assert( e->e_private );
120
121         cache_entry_rdwr_destroy( e );
122
123         free( e->e_private );
124         e->e_private = NULL;
125         return 0;
126 }
127
128 void
129 cache_return_entry_rw( Cache *cache, Entry *e, int rw )
130 {
131         ID id;
132         int refcnt;
133
134         /* set cache mutex */
135         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
136
137         assert( e->e_private );
138
139         cache_entry_rdwr_unlock(e, rw);
140
141         id = e->e_id;
142         refcnt = --LEI(e)->lei_refcnt;
143
144         if ( LEI(e)->lei_state == CACHE_ENTRY_CREATING ) {
145                 LEI(e)->lei_state = CACHE_ENTRY_READY;
146
147                 /* free cache mutex */
148                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
149
150                 Debug( LDAP_DEBUG_TRACE,
151                         "====> cache_return_entry_%s( %ld ): created (%d)\n",
152                         rw ? "w" : "r", id, refcnt );
153
154         } else if ( LEI(e)->lei_state == CACHE_ENTRY_DELETED ) {
155                 if( refcnt > 0 ) {
156                         /* free cache mutex */
157                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
158
159                         Debug( LDAP_DEBUG_TRACE,
160                                 "====> cache_return_entry_%s( %ld ): delete pending (%d)\n",
161                                 rw ? "w" : "r", id, refcnt );
162
163                 } else {
164                         cache_entry_private_destroy( e );
165                         entry_free( e );
166
167                         /* free cache mutex */
168                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
169
170                         Debug( LDAP_DEBUG_TRACE,
171                                 "====> cache_return_entry_%s( %ld ): deleted (%d)\n",
172                                 rw ? "w" : "r", id, refcnt );
173                 }
174
175         } else {
176                 /* free cache mutex */
177                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
178
179                 Debug( LDAP_DEBUG_TRACE,
180                         "====> cache_return_entry_%s( %ld ): returned (%d)\n",
181                         rw ? "w" : "r", id, refcnt);
182         }
183 }
184
185 #define LRU_DELETE( cache, e ) do { \
186         if ( LEI(e)->lei_lruprev != NULL ) { \
187                 LEI(LEI(e)->lei_lruprev)->lei_lrunext = LEI(e)->lei_lrunext; \
188         } else { \
189                 (cache)->c_lruhead = LEI(e)->lei_lrunext; \
190         } \
191         if ( LEI(e)->lei_lrunext != NULL ) { \
192                 LEI(LEI(e)->lei_lrunext)->lei_lruprev = LEI(e)->lei_lruprev; \
193         } else { \
194                 (cache)->c_lrutail = LEI(e)->lei_lruprev; \
195         } \
196 } while(0)
197
198 #define LRU_ADD( cache, e ) do { \
199         LEI(e)->lei_lrunext = (cache)->c_lruhead; \
200         if ( LEI(e)->lei_lrunext != NULL ) { \
201                 LEI(LEI(e)->lei_lrunext)->lei_lruprev = (e); \
202         } \
203         (cache)->c_lruhead = (e); \
204         LEI(e)->lei_lruprev = NULL; \
205         if ( (cache)->c_lrutail == NULL ) { \
206                 (cache)->c_lrutail = (e); \
207         } \
208 } while(0)
209
210 /*
211  * cache_add_entry_rw - create and lock an entry in the cache
212  * returns:     0       entry has been created and locked
213  *              1       entry already existed
214  *              -1      something bad happened
215  */
216 int
217 cache_add_entry_rw(
218     Cache       *cache,
219     Entry               *e,
220         int             rw
221 )
222 {
223         int     i, rc;
224         Entry   *ee;
225
226         /* set cache mutex */
227         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
228
229         assert( e->e_private == NULL );
230
231         if( cache_entry_private_init(e) != 0 ) {
232                 /* free cache mutex */
233                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
234
235                 Debug( LDAP_DEBUG_ANY,
236                         "====> cache_add_entry( %ld ): \"%s\": private init failed!\n",
237                     e->e_id, e->e_dn, 0 );
238
239                 return( -1 );
240         }
241
242         if ( avl_insert( &cache->c_dntree, (caddr_t) e,
243                 (AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
244         {
245                 /* free cache mutex */
246                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
247
248                 Debug( LDAP_DEBUG_TRACE,
249                         "====> cache_add_entry( %ld ): \"%s\": already in dn cache\n",
250                     e->e_id, e->e_dn, 0 );
251
252                 cache_entry_private_destroy(e);
253
254                 return( 1 );
255         }
256
257         /* id tree */
258         if ( avl_insert( &cache->c_idtree, (caddr_t) e,
259                 (AVL_CMP) entry_id_cmp, avl_dup_error ) != 0 )
260         {
261                 Debug( LDAP_DEBUG_ANY,
262                         "====> cache_add_entry( %ld ): \"%s\": already in id cache\n",
263                     e->e_id, e->e_dn, 0 );
264
265
266                 /* delete from dn tree inserted above */
267                 if ( avl_delete( &cache->c_dntree, (caddr_t) e,
268                         (AVL_CMP) entry_dn_cmp ) == NULL )
269                 {
270                         Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
271                             0, 0, 0 );
272                 }
273
274                 cache_entry_private_destroy(e);
275
276                 /* free cache mutex */
277                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
278                 return( -1 );
279         }
280
281         cache_entry_rdwr_lock( e, rw );
282
283         /* put the entry into 'CREATING' state */
284         /* will be marked after when entry is returned */
285         LEI(e)->lei_state = CACHE_ENTRY_CREATING;
286         LEI(e)->lei_refcnt = 1;
287
288         /* lru */
289         LRU_ADD( cache, e );
290         if ( ++cache->c_cursize > cache->c_maxsize ) {
291                 /*
292                  * find the lru entry not currently in use and delete it.
293                  * in case a lot of entries are in use, only look at the
294                  * first 10 on the tail of the list.
295                  */
296                 i = 0;
297                 while ( cache->c_lrutail != NULL &&
298                         LEI(cache->c_lrutail)->lei_refcnt != 0 &&
299                         i < 10 )
300                 {
301                         /* move this in-use entry to the front of the q */
302                         ee = cache->c_lrutail;
303                         LRU_DELETE( cache, ee );
304                         LRU_ADD( cache, ee );
305                         i++;
306                 }
307
308                 /*
309                  * found at least one to delete - try to get back under
310                  * the max cache size.
311                  */
312                 while ( cache->c_lrutail != NULL &&
313                         LEI(cache->c_lrutail)->lei_refcnt == 0 &&
314                         cache->c_cursize > cache->c_maxsize )
315                 {
316                         e = cache->c_lrutail;
317
318                         /* delete from cache and lru q */
319                         /* XXX do we need rc ? */
320                         rc = cache_delete_entry_internal( cache, e );
321                         cache_entry_private_destroy( e );
322                         entry_free( e );
323                 }
324         }
325
326         /* free cache mutex */
327         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
328         return( 0 );
329 }
330
331 /*
332  * cache_update_entry - update a LOCKED entry which has been deleted.
333  * returns:     0       entry has been created and locked
334  *              1       entry already existed
335  *              -1      something bad happened
336  */
337 int
338 cache_update_entry(
339     Cache       *cache,
340     Entry               *e
341 )
342 {
343         int     i, rc;
344         Entry   *ee;
345
346         /* set cache mutex */
347         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
348
349         assert( e->e_private );
350
351         if ( avl_insert( &cache->c_dntree, (caddr_t) e,
352                 (AVL_CMP) entry_dn_cmp, avl_dup_error ) != 0 )
353         {
354                 Debug( LDAP_DEBUG_TRACE,
355                         "====> cache_update_entry( %ld ): \"%s\": already in dn cache\n",
356                     e->e_id, e->e_dn, 0 );
357
358                 /* free cache mutex */
359                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
360                 return( 1 );
361         }
362
363         /* id tree */
364         if ( avl_insert( &cache->c_idtree, (caddr_t) e,
365                 (AVL_CMP) entry_id_cmp, avl_dup_error ) != 0 )
366         {
367                 Debug( LDAP_DEBUG_ANY,
368                         "====> cache_update_entry( %ld ): \"%s\": already in id cache\n",
369                     e->e_id, e->e_dn, 0 );
370
371                 /* delete from dn tree inserted above */
372                 if ( avl_delete( &cache->c_dntree, (caddr_t) e,
373                         (AVL_CMP) entry_dn_cmp ) == NULL )
374                 {
375                         Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
376                             0, 0, 0 );
377                 }
378
379                 /* free cache mutex */
380                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
381                 return( -1 );
382         }
383
384
385         /* put the entry into 'CREATING' state */
386         /* will be marked after when entry is returned */
387         LEI(e)->lei_state = CACHE_ENTRY_CREATING;
388
389         /* lru */
390         LRU_ADD( cache, e );
391         if ( ++cache->c_cursize > cache->c_maxsize ) {
392                 /*
393                  * find the lru entry not currently in use and delete it.
394                  * in case a lot of entries are in use, only look at the
395                  * first 10 on the tail of the list.
396                  */
397                 i = 0;
398                 while ( cache->c_lrutail != NULL &&
399                         LEI(cache->c_lrutail)->lei_refcnt != 0 &&
400                         i < 10 )
401                 {
402                         /* move this in-use entry to the front of the q */
403                         ee = cache->c_lrutail;
404                         LRU_DELETE( cache, ee );
405                         LRU_ADD( cache, ee );
406                         i++;
407                 }
408
409                 /*
410                  * found at least one to delete - try to get back under
411                  * the max cache size.
412                  */
413                 while ( cache->c_lrutail != NULL &&
414                         LEI(cache->c_lrutail)->lei_refcnt == 0 &&
415                         cache->c_cursize > cache->c_maxsize )
416                 {
417                         e = cache->c_lrutail;
418
419                         /* delete from cache and lru q */
420                         /* XXX do we need rc ? */
421                         rc = cache_delete_entry_internal( cache, e );
422                         cache_entry_private_destroy( e );
423                         entry_free( e );
424                 }
425         }
426
427         /* free cache mutex */
428         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
429         return( 0 );
430 }
431
432 /*
433  * cache_find_entry_dn2id - find an entry in the cache, given dn
434  */
435
436 ID
437 cache_find_entry_dn2id(
438         Backend         *be,
439     Cache       *cache,
440     const char          *dn
441 )
442 {
443         Entry           e, *ep;
444         ID                      id;
445         int count = 0;
446
447         e.e_dn = (char *) dn;
448         e.e_ndn = ch_strdup( dn );
449         (void) dn_normalize( e.e_ndn );
450
451 try_again:
452         /* set cache mutex */
453         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
454
455         if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
456                 (AVL_CMP) entry_dn_cmp )) != NULL )
457         {
458                 int state;
459                 count++;
460
461                 /*
462                  * ep now points to an unlocked entry
463                  * we do not need to lock the entry if we only
464                  * check the state, refcnt, LRU, and id.
465                  */
466
467                 assert( ep->e_private );
468
469                 /* save id */
470                 id = ep->e_id;
471                 state = LEI(ep)->lei_state;
472
473                 /*
474                  * entry is deleted or not fully created yet
475                  */
476                 if ( state != CACHE_ENTRY_READY ) {
477                         assert(state != CACHE_ENTRY_UNDEFINED);
478
479                         /* free cache mutex */
480                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
481
482                         Debug(LDAP_DEBUG_TRACE,
483                                 "====> cache_find_entry_dn2id(\"%s\"): %ld (not ready) %d\n",
484                                 dn, id, state);
485
486                         ldap_pvt_thread_yield();
487                         goto try_again;
488                 }
489
490                 /* lru */
491                 LRU_DELETE( cache, ep );
492                 LRU_ADD( cache, ep );
493                 
494                 /* free cache mutex */
495                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
496
497                 Debug(LDAP_DEBUG_TRACE,
498                         "====> cache_find_entry_dn2id(\"%s\"): %ld (%d tries)\n",
499                         dn, id, count);
500
501         } else {
502                 /* free cache mutex */
503                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
504
505                 id = NOID;
506         }
507
508         free(e.e_ndn);
509
510         return( id );
511 }
512
513 /*
514  * cache_find_entry_id - find an entry in the cache, given id
515  */
516
517 Entry *
518 cache_find_entry_id(
519         Cache   *cache,
520         ID                              id,
521         int                             rw
522 )
523 {
524         Entry   e;
525         Entry   *ep;
526         int     count = 0;
527
528         e.e_id = id;
529
530 try_again:
531         /* set cache mutex */
532         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
533
534         if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
535                 (AVL_CMP) entry_id_cmp )) != NULL )
536         {
537                 int state;
538                 ID      ep_id;
539
540                 count++;
541
542                 assert( ep->e_private );
543
544                 ep_id = ep->e_id; 
545                 state = LEI(ep)->lei_state;
546
547                 /*
548                  * entry is deleted or not fully created yet
549                  */
550                 if ( state != CACHE_ENTRY_READY ) {
551
552                         assert(state != CACHE_ENTRY_UNDEFINED);
553
554                         /* free cache mutex */
555                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
556
557                         Debug(LDAP_DEBUG_TRACE,
558                                 "====> cache_find_entry_id( %ld ): %ld (not ready) %d\n",
559                                 id, ep_id, state);
560
561                         ldap_pvt_thread_yield();
562                         goto try_again;
563                 }
564
565                 /* acquire reader lock */
566                 if ( cache_entry_rdwr_trylock(ep, rw) == LDAP_PVT_THREAD_EBUSY ) {
567                         /* could not acquire entry lock...
568                          * owner cannot free as we have the cache locked.
569                          * so, unlock the cache, yield, and try again.
570                          */
571
572                         /* free cache mutex */
573                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
574
575                         Debug(LDAP_DEBUG_TRACE,
576                                 "====> cache_find_entry_id( %ld ): %ld (busy) %d\n",
577                                 id, ep_id, state);
578
579                         ldap_pvt_thread_yield();
580                         goto try_again;
581                 }
582
583                 /* lru */
584                 LRU_DELETE( cache, ep );
585                 LRU_ADD( cache, ep );
586                 
587                 LEI(ep)->lei_refcnt++;
588
589                 /* free cache mutex */
590                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
591
592                 Debug(LDAP_DEBUG_TRACE,
593                         "====> cache_find_entry_id( %ld ) \"%s\" (found) (%d tries)\n",
594                         ep_id, ep->e_dn, count);
595
596                 return( ep );
597         }
598
599         /* free cache mutex */
600         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
601
602         return( NULL );
603 }
604
605 /*
606  * cache_delete_entry - delete the entry e from the cache.  the caller
607  * should have obtained e (increasing its ref count) via a call to one
608  * of the cache_find_* routines.  the caller should *not* call the
609  * cache_return_entry() routine prior to calling cache_delete_entry().
610  * it performs this function.
611  *
612  * returns:     0       e was deleted ok
613  *              1       e was not in the cache
614  *              -1      something bad happened
615  */
616 int
617 cache_delete_entry(
618     Cache       *cache,
619     Entry               *e
620 )
621 {
622         int     rc;
623
624         /* set cache mutex */
625         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
626
627         assert( e->e_private );
628
629         Debug( LDAP_DEBUG_TRACE, "====> cache_delete_entry( %ld )\n",
630                 e->e_id, 0, 0 );
631
632         rc = cache_delete_entry_internal( cache, e );
633
634         /* free cache mutex */
635         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
636         return( rc );
637 }
638
639 static int
640 cache_delete_entry_internal(
641     Cache       *cache,
642     Entry               *e
643 )
644 {
645         int rc = 0;     /* return code */
646
647         /* dn tree */
648         if ( avl_delete( &cache->c_dntree, (caddr_t) e, (AVL_CMP) entry_dn_cmp )
649                 == NULL )
650         {
651                 rc = -1;
652         }
653
654         /* id tree */
655         if ( avl_delete( &cache->c_idtree, (caddr_t) e, (AVL_CMP) entry_id_cmp )
656                 == NULL )
657         {
658                 rc = -1;
659         }
660
661         if (rc != 0) {
662                 return rc;
663         }
664
665         /* lru */
666         LRU_DELETE( cache, e );
667         cache->c_cursize--;
668
669         /*
670          * flag entry to be freed later by a call to cache_return_entry()
671          */
672         LEI(e)->lei_state = CACHE_ENTRY_DELETED;
673
674         return( 0 );
675 }
676
677 void
678 cache_release_all( Cache *cache )
679 {
680         Entry *e;
681         int rc;
682
683         /* set cache mutex */
684         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
685
686         Debug( LDAP_DEBUG_TRACE, "====> cache_release_all\n", 0, 0, 0 );
687
688         while ( (e = cache->c_lrutail) != NULL && LEI(e)->lei_refcnt == 0 ) {
689 #ifdef LDAP_RDWR_DEBUG
690                 assert(!ldap_pvt_thread_rdwr_active(&LEI(e)->lei_rdwr));
691 #endif
692
693                 /* delete from cache and lru q */
694                 /* XXX do we need rc ? */
695                 rc = cache_delete_entry_internal( cache, e );
696                 cache_entry_private_destroy( e );
697                 entry_free( e );
698         }
699
700         if ( cache->c_cursize ) {
701                 Debug( LDAP_DEBUG_TRACE, "Entry-cache could not be emptied\n", 0, 0, 0 );
702         }
703
704         /* free cache mutex */
705         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
706 }
707
708 #ifdef LDAP_DEBUG
709
710 static void
711 lru_print( Cache *cache )
712 {
713         Entry   *e;
714
715         fprintf( stderr, "LRU queue (head to tail):\n" );
716         for ( e = cache->c_lruhead; e != NULL; e = LEI(e)->lei_lrunext ) {
717                 fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n",
718                         e->e_dn, e->e_id, LEI(e)->lei_refcnt );
719         }
720         fprintf( stderr, "LRU queue (tail to head):\n" );
721         for ( e = cache->c_lrutail; e != NULL; e = LEI(e)->lei_lruprev ) {
722                 fprintf( stderr, "\tdn \"%20s\" id %ld refcnt %d\n",
723                         e->e_dn, e->e_id, LEI(e)->lei_refcnt );
724         }
725 }
726
727 #endif
728