]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/cache.c
More changes from -devel.
[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/socket.h>
8 #include "slap.h"
9
10 #include "back-ldbm.h"
11
12 static int      cache_delete_entry_internal();
13 #ifdef LDAP_DEBUG
14 static void     lru_print();
15 #endif
16
17 /*
18  * the cache has three entry points (ways to find things):
19  *
20  *      by entry        e.g., if you already have an entry from the cache
21  *                      and want to delete it. (really by entry ptr)
22  *      by dn           e.g., when looking for the base object of a search
23  *      by id           e.g., for search candidates
24  *
25  * these correspond to three different avl trees that are maintained.
26  */
27
28 static int
29 cache_entry_cmp( Entry *e1, Entry *e2 )
30 {
31         return( e1 < e2 ? -1 : (e1 > e2 ? 1 : 0) );
32 }
33
34 static int
35 cache_entrydn_cmp( Entry *e1, Entry *e2 )
36 {
37         return( strcasecmp( e1->e_dn, e2->e_dn ) );
38 }
39
40 static int
41 cache_entryid_cmp( Entry *e1, Entry *e2 )
42 {
43         return( e1->e_id < e2->e_id ? -1 : (e1->e_id > e2->e_id ? 1 : 0) );
44 }
45
46 void
47 cache_set_state( struct cache *cache, Entry *e, int state )
48 {
49         /* set cache mutex */
50         pthread_mutex_lock( &cache->c_mutex );
51
52         e->e_state = state;
53
54         /* free cache mutex */
55         pthread_mutex_unlock( &cache->c_mutex );
56 }
57
58 static void
59 cache_return_entry( struct cache *cache, Entry *e )
60 {
61         /* set cache mutex */
62         pthread_mutex_lock( &cache->c_mutex );
63
64         if ( --e->e_refcnt == 0 && e->e_state == ENTRY_STATE_DELETED ) {
65                 entry_free( e );
66         }
67
68         /* free cache mutex */
69         pthread_mutex_unlock( &cache->c_mutex );
70 }
71
72 static void
73 cache_return_entry_rw( struct cache *cache, Entry *e, int rw )
74 {
75         Debug( LDAP_DEBUG_TRACE, "====> cache_return_entry_%s\n",
76                 rw ? "w" : "r", 0, 0);
77         entry_rdwr_unlock(e, rw);;
78         cache_return_entry(cache, e);
79 }
80
81 void
82 cache_return_entry_r( struct cache *cache, Entry *e )
83 {
84         cache_return_entry_rw(cache, e, 0);
85 }
86
87 void
88 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 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         pthread_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 %d already in dn cache\n",
143                     e->e_dn, e->e_id, 0 );
144
145                 /* free cache mutex */
146                 pthread_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 %d 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                 pthread_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                         /* XXX check for writer lock - should also check no readers pending */
201 #ifdef LDAP_DEBUG
202                         assert(pthread_rdwr_wchk_np(&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         pthread_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 cache_find_entry_dn2id(
223         Backend         *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         pthread_mutex_lock( &cache->c_mutex );
234
235         e.e_dn = dn;
236
237         if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
238                 cache_entrydn_cmp )) != NULL )
239         {
240                 Debug(LDAP_DEBUG_TRACE, "====> cache_find_entry_dn2id: found dn: %s\n",
241                         dn, 0, 0);
242
243                 /*
244                  * entry is deleted or not fully created yet
245                  */
246                 if ( ep->e_state == ENTRY_STATE_DELETED ||
247                         ep->e_state == ENTRY_STATE_CREATING )
248                 {
249                         /* free cache mutex */
250                         pthread_mutex_unlock( &cache->c_mutex );
251                         return( NOID );
252                 }
253
254                 /* XXX is this safe without writer lock? */
255                 ep->e_refcnt++;
256
257                 /* lru */
258                 LRU_DELETE( cache, ep );
259                 LRU_ADD( cache, ep );
260
261                 /* acquire reader lock */
262                 entry_rdwr_lock(ep, 0);
263
264                 /* re-check */
265                 if ( ep->e_state == ENTRY_STATE_DELETED ||
266                         ep->e_state == ENTRY_STATE_CREATING )
267                 {
268                         /* XXX check that is is required */
269                         ep->e_refcnt--;
270
271                         /* free reader lock */
272                         entry_rdwr_unlock(ep, 0);
273                         /* free cache mutex */
274                         pthread_mutex_unlock( &cache->c_mutex );
275
276                         return( NOID );
277                 }
278
279                 /* save id */
280                 id = ep->e_id;
281
282                 /* free reader lock */
283                 entry_rdwr_unlock(ep, 0);
284
285                 /* free cache mutex */
286                 pthread_mutex_unlock( &cache->c_mutex );
287
288                 cache_return_entry( &li->li_cache, ep );
289
290                 return( id );
291         }
292
293         /* free cache mutex */
294         pthread_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         /* set cache mutex */
314         pthread_mutex_lock( &cache->c_mutex );
315
316         e.e_id = id;
317
318         if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
319                 cache_entryid_cmp )) != NULL )
320         {
321                 Debug(LDAP_DEBUG_TRACE,
322                         "====> cache_find_entry_dn2id: found id: %ld rw: %d\n",
323                         id, rw, 0);
324
325                 /*
326                  * entry is deleted or not fully created yet
327                  */
328                 if ( ep->e_state == ENTRY_STATE_DELETED ||
329                         ep->e_state == ENTRY_STATE_CREATING )
330                 {
331                         /* free cache mutex */
332                         pthread_mutex_unlock( &cache->c_mutex );
333                         return( NULL );
334                 }
335                 /* XXX is this safe without writer lock? */
336                 ep->e_refcnt++;
337
338                 /* lru */
339                 LRU_DELETE( cache, ep );
340                 LRU_ADD( cache, ep );
341                 
342                 /* acquire reader lock */
343                 entry_rdwr_lock(ep, 0);
344
345                 /* re-check */
346                 if ( ep->e_state == ENTRY_STATE_DELETED ||
347                         ep->e_state == ENTRY_STATE_CREATING ) {
348
349                         /* XXX check that is is required */
350                         ep->e_refcnt--;
351
352                         /* free reader lock */
353                         entry_rdwr_unlock(ep, 0);
354
355                         /* free cache mutex */
356                         pthread_mutex_unlock( &cache->c_mutex );
357                         return( NULL );
358                 }
359
360                 if ( rw ) {
361                         entry_rdwr_unlock(ep, 0);
362                         entry_rdwr_lock(ep, 1);
363                 }
364
365                 /* free cache mutex */
366                 pthread_mutex_unlock( &cache->c_mutex );
367
368                 return( ep );
369         }
370
371         /* free cache mutex */
372         pthread_mutex_unlock( &cache->c_mutex );
373
374         return( NULL );
375 }
376
377 /*
378  * cache_delete_entry - delete the entry e from the cache.  the caller
379  * should have obtained e (increasing its ref count) via a call to one
380  * of the cache_find_* routines.  the caller should *not* call the
381  * cache_return_entry() routine prior to calling cache_delete_entry().
382  * it performs this function.
383  *
384  * returns:     0       e was deleted ok
385  *              1       e was not in the cache
386  *              -1      something bad happened
387  */
388 int
389 cache_delete_entry(
390     struct cache        *cache,
391     Entry               *e
392 )
393 {
394         int     rc;
395
396         Debug( LDAP_DEBUG_TRACE, "====> cache_delete_entry:\n", 0, 0, 0 );
397
398         /* XXX check for writer lock - should also check no readers pending */
399 #ifdef LDAP_DEBUG
400         assert(pthread_rdwr_wchk_np(&e->e_rdwr));
401 #endif
402
403         /* set cache mutex */
404         pthread_mutex_lock( &cache->c_mutex );
405
406         rc = cache_delete_entry_internal( cache, e );
407
408         /* free cache mutex */
409         pthread_mutex_unlock( &cache->c_mutex );
410         return( rc );
411 }
412
413 static int
414 cache_delete_entry_internal(
415     struct cache        *cache,
416     Entry               *e
417 )
418 {
419         /* dn tree */
420         if ( avl_delete( &cache->c_dntree, (caddr_t) e, cache_entrydn_cmp )
421                 == NULL )
422         {
423                 return( -1 );
424         }
425
426         /* id tree */
427         if ( avl_delete( &cache->c_idtree, (caddr_t) e, cache_entryid_cmp )
428                 == NULL )
429         {
430                 return( -1 );
431         }
432
433         /* lru */
434         LRU_DELETE( cache, e );
435         cache->c_cursize--;
436
437         /*
438          * flag entry to be freed later by a call to cache_return_entry()
439          */
440         e->e_state = ENTRY_STATE_DELETED;
441
442         return( 0 );
443 }
444
445 #ifdef LDAP_DEBUG
446
447 static void
448 lru_print( struct cache *cache )
449 {
450         Entry   *e;
451
452         fprintf( stderr, "LRU queue (head to tail):\n" );
453         for ( e = cache->c_lruhead; e != NULL; e = e->e_lrunext ) {
454                 fprintf( stderr, "\tdn %20s id %d refcnt %d\n", e->e_dn,
455                     e->e_id, e->e_refcnt );
456         }
457         fprintf( stderr, "LRU queue (tail to head):\n" );
458         for ( e = cache->c_lrutail; e != NULL; e = e->e_lruprev ) {
459                 fprintf( stderr, "\tdn %20s id %d refcnt %d\n", e->e_dn,
460                     e->e_id, e->e_refcnt );
461         }
462 }
463
464 #endif
465