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