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