]> git.sur5r.net Git - openldap/blob - servers/slapd/back-bdb2/cache.c
Update LDBM cache so that it manages it's own state.
[openldap] / servers / slapd / back-bdb2 / 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-bdb2.h"
14
15 static int      cache_delete_entry_internal(struct cache *cache, Entry *e);
16 #ifdef LDAP_DEBUG
17 static void     lru_print(struct cache *cache);
18 #endif
19
20 /*
21  * the cache has three entry points (ways to find things):
22  *
23  *      by entry        e.g., if you already have an entry from the cache
24  *                      and want to delete it. (really by entry ptr)
25  *      by dn           e.g., when looking for the base object of a search
26  *      by id           e.g., for search candidates
27  *
28  * these correspond to three different avl trees that are maintained.
29  */
30 #define cache_entry_cmp entry_cmp
31 #define cache_entrydn_cmp entry_dn_cmp
32 #define cache_entryid_cmp entry_id_cmp
33
34 void
35 bdb2i_cache_set_state( struct cache *cache, Entry *e, int state )
36 {
37         /* set cache mutex */
38         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
39
40         e->e_state = state;
41
42         /* free cache mutex */
43         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
44 }
45
46 #ifdef not_used
47 static void
48 cache_return_entry( struct cache *cache, Entry *e )
49 {
50         /* set cache mutex */
51         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
52
53         if ( --e->e_refcnt == 0 && e->e_state == ENTRY_STATE_DELETED ) {
54                 entry_free( e );
55         }
56
57         /* free cache mutex */
58         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
59 }
60 #endif
61
62 static void
63 cache_return_entry_rw( struct cache *cache, Entry *e, int rw )
64 {
65         Debug( LDAP_DEBUG_TRACE, "====> cache_return_entry_%s\n",
66                 rw ? "w" : "r", 0, 0);
67
68         /* set cache mutex */
69         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
70
71         entry_rdwr_unlock(e, rw);
72
73         if ( --e->e_refcnt == 0 && e->e_state == ENTRY_STATE_DELETED ) {
74                 entry_free( e );
75         }
76
77         /* free cache mutex */
78         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
79 }
80
81 void
82 bdb2i_cache_return_entry_r( struct cache *cache, Entry *e )
83 {
84         cache_return_entry_rw(cache, e, 0);
85 }
86
87 void
88 bdb2i_cache_return_entry_w( struct cache *cache, Entry *e )
89 {
90         cache_return_entry_rw(cache, e, 1);
91 }
92
93
94 #define LRU_DELETE( cache, e ) { \
95         if ( e->e_lruprev != NULL ) { \
96                 e->e_lruprev->e_lrunext = e->e_lrunext; \
97         } else { \
98                 cache->c_lruhead = e->e_lrunext; \
99         } \
100         if ( e->e_lrunext != NULL ) { \
101                 e->e_lrunext->e_lruprev = e->e_lruprev; \
102         } else { \
103                 cache->c_lrutail = e->e_lruprev; \
104         } \
105 }
106
107 #define LRU_ADD( cache, e ) { \
108         e->e_lrunext = cache->c_lruhead; \
109         if ( e->e_lrunext != NULL ) { \
110                 e->e_lrunext->e_lruprev = e; \
111         } \
112         cache->c_lruhead = e; \
113         e->e_lruprev = NULL; \
114         if ( cache->c_lrutail == NULL ) { \
115                 cache->c_lrutail = e; \
116         } \
117 }
118
119 /*
120  * cache_create_entry_lock - create an entry in the cache, and lock it.
121  * returns:     0       entry has been created and locked
122  *              1       entry already existed
123  *              -1      something bad happened
124  */
125 int
126 bdb2i_cache_add_entry_lock(
127     struct cache        *cache,
128     Entry               *e,
129     int                 state
130 )
131 {
132         int     i, rc;
133         Entry   *ee;
134
135         /* set cache mutex */
136         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
137
138         if ( avl_insert( &cache->c_dntree, (caddr_t) e,
139                 cache_entrydn_cmp, avl_dup_error ) != 0 )
140         {
141                 Debug( LDAP_DEBUG_TRACE,
142                         "====> cache_add_entry lock: entry %20s id %lu already in dn cache\n",
143                     e->e_dn, e->e_id, 0 );
144
145                 /* free cache mutex */
146                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
147                 return( 1 );
148         }
149
150         /* id tree */
151         if ( avl_insert( &cache->c_idtree, (caddr_t) e,
152                 cache_entryid_cmp, avl_dup_error ) != 0 )
153         {
154                 Debug( LDAP_DEBUG_ANY,
155                         "====> entry %20s id %lu already in id cache\n",
156                     e->e_dn, e->e_id, 0 );
157
158                 /* delete from dn tree inserted above */
159                 if ( avl_delete( &cache->c_dntree, (caddr_t) e,
160                         cache_entrydn_cmp ) == NULL )
161                 {
162                         Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
163                             0, 0, 0 );
164                 }
165
166                 /* free cache mutex */
167                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
168                 return( -1 );
169         }
170
171         e->e_state = state;
172         e->e_refcnt = 1;
173
174         /* lru */
175         LRU_ADD( cache, e );
176         if ( ++cache->c_cursize > cache->c_maxsize ) {
177                 /*
178                  * find the lru entry not currently in use and delete it.
179                  * in case a lot of entries are in use, only look at the
180                  * first 10 on the tail of the list.
181                  */
182                 i = 0;
183                 while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
184                     != 0 && i < 10 ) {
185                         /* move this in-use entry to the front of the q */
186                         ee = cache->c_lrutail;
187                         LRU_DELETE( cache, ee );
188                         LRU_ADD( cache, ee );
189                         i++;
190                 }
191
192                 /*
193                  * found at least one to delete - try to get back under
194                  * the max cache size.
195                  */
196                 while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
197                     == 0 && cache->c_cursize > cache->c_maxsize ) {
198                         e = cache->c_lrutail;
199
200                 /* check for active readers/writer lock */
201 #ifdef LDAP_DEBUG
202                         assert(!ldap_pvt_thread_rdwr_active( &e->e_rdwr ));
203 #endif
204
205                         /* delete from cache and lru q */
206                         rc = cache_delete_entry_internal( cache, e );
207
208                         entry_free( e );
209                 }
210         }
211
212         /* free cache mutex */
213         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
214         return( 0 );
215 }
216
217 /*
218  * cache_find_entry_dn2id - find an entry in the cache, given dn
219  */
220
221 ID
222 bdb2i_cache_find_entry_dn2id(
223         BackendDB               *be,
224     struct cache        *cache,
225     char                *dn
226 )
227 {
228         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
229         Entry           e, *ep;
230         ID                      id;
231
232         /* set cache mutex */
233         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
234
235         e.e_dn = dn;
236         e.e_ndn = dn_normalize_case( ch_strdup( dn ) );
237
238         if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
239                 cache_entrydn_cmp )) != NULL )
240         {
241                 /*
242                  * ep now points to an unlocked entry
243                  * we do not need to lock the entry if we only
244                  * check the state, refcnt, LRU, and id.
245                  */
246                 free(e.e_ndn);
247
248                 Debug(LDAP_DEBUG_TRACE, "====> cache_find_entry_dn2id: found dn: %s\n",
249                         dn, 0, 0);
250
251                 /*
252                  * entry is deleted or not fully created yet
253                  */
254                 if ( ep->e_state == ENTRY_STATE_DELETED ||
255                         ep->e_state == ENTRY_STATE_CREATING )
256                 {
257                         /* free cache mutex */
258                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
259                         return( NOID );
260                 }
261
262                 /* lru */
263                 LRU_DELETE( cache, ep );
264                 LRU_ADD( cache, ep );
265
266                 /* save id */
267                 id = ep->e_id;
268
269                 /* free cache mutex */
270                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
271
272                 return( id );
273         }
274
275         free(e.e_ndn);
276
277         /* free cache mutex */
278         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
279
280         return( NOID );
281 }
282
283 /*
284  * cache_find_entry_id - find an entry in the cache, given id
285  */
286
287 Entry *
288 bdb2i_cache_find_entry_id(
289         struct cache    *cache,
290         ID                              id,
291         int                             rw
292 )
293 {
294         Entry   e;
295         Entry   *ep;
296
297         e.e_id = id;
298
299 try_again:
300         /* set cache mutex */
301         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
302
303         if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
304                 cache_entryid_cmp )) != NULL )
305         {
306                 Debug(LDAP_DEBUG_TRACE,
307                         "====> cache_find_entry_dn2id: found id: %ld rw: %d\n",
308                         id, rw, 0);
309
310                 /*
311                  * entry is deleted or not fully created yet
312                  */
313                 if ( ep->e_state == ENTRY_STATE_DELETED ||
314                         ep->e_state == ENTRY_STATE_CREATING )
315                 {
316                         /* free cache mutex */
317                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
318                         return( NULL );
319                 }
320
321                 /* acquire reader lock */
322                 if ( entry_rdwr_trylock(ep, rw) == LDAP_PVT_THREAD_EBUSY ) {
323                         /* could not acquire entry lock...
324                          * owner cannot free as we have the cache locked.
325                          * so, unlock the cache, yield, and try again.
326                          */
327
328                         /* free cache mutex */
329                         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
330                         ldap_pvt_thread_yield();
331                         goto try_again;
332                 }
333
334                 /* lru */
335                 LRU_DELETE( cache, ep );
336                 LRU_ADD( cache, ep );
337                 
338                 ep->e_refcnt++;
339
340                 /* free cache mutex */
341                 ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
342
343                 return( ep );
344         }
345
346         /* free cache mutex */
347         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
348
349         return( NULL );
350 }
351
352 /*
353  * cache_delete_entry - delete the entry e from the cache.  the caller
354  * should have obtained e (increasing its ref count) via a call to one
355  * of the cache_find_* routines.  the caller should *not* call the
356  * cache_return_entry() routine prior to calling cache_delete_entry().
357  * it performs this function.
358  *
359  * returns:     0       e was deleted ok
360  *              1       e was not in the cache
361  *              -1      something bad happened
362  */
363 int
364 bdb2i_cache_delete_entry(
365     struct cache        *cache,
366     Entry               *e
367 )
368 {
369         int     rc;
370
371         Debug( LDAP_DEBUG_TRACE, "====> cache_delete_entry:\n", 0, 0, 0 );
372
373         /* set cache mutex */
374         ldap_pvt_thread_mutex_lock( &cache->c_mutex );
375
376         /* XXX check for writer lock - should also check no readers pending */
377 #ifdef LDAP_DEBUG
378         assert(ldap_pvt_thread_rdwr_writers( &e->e_rdwr ) == 1);
379 #endif
380
381         rc = cache_delete_entry_internal( cache, e );
382
383         /* free cache mutex */
384         ldap_pvt_thread_mutex_unlock( &cache->c_mutex );
385         return( rc );
386 }
387
388 static int
389 cache_delete_entry_internal(
390     struct cache        *cache,
391     Entry               *e
392 )
393 {
394         int rc = 0;     /* return code */
395
396         /* dn tree */
397         if ( avl_delete( &cache->c_dntree, (caddr_t) e, cache_entrydn_cmp )
398                 == NULL )
399         {
400                 rc = -1;
401         }
402
403         /* id tree */
404         if ( avl_delete( &cache->c_idtree, (caddr_t) e, cache_entryid_cmp )
405                 == NULL )
406         {
407                 rc = -1;
408         }
409
410         if (rc != 0) {
411                 return rc;
412         }
413
414         /* lru */
415         LRU_DELETE( cache, e );
416         cache->c_cursize--;
417
418         /*
419          * flag entry to be freed later by a call to cache_return_entry()
420          */
421         e->e_state = ENTRY_STATE_DELETED;
422
423         return( 0 );
424 }
425
426 #ifdef LDAP_DEBUG
427
428 static void
429 lru_print( struct cache *cache )
430 {
431         Entry   *e;
432
433         fprintf( stderr, "LRU queue (head to tail):\n" );
434         for ( e = cache->c_lruhead; e != NULL; e = e->e_lrunext ) {
435                 fprintf( stderr, "\tdn %20s id %lu refcnt %d\n", e->e_dn,
436                     e->e_id, e->e_refcnt );
437         }
438         fprintf( stderr, "LRU queue (tail to head):\n" );
439         for ( e = cache->c_lrutail; e != NULL; e = e->e_lruprev ) {
440                 fprintf( stderr, "\tdn %20s id %lu refcnt %d\n", e->e_dn,
441                     e->e_id, e->e_refcnt );
442         }
443 }
444
445 #endif
446