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