]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/cache.c
remove lint reported by Hallvard
[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, e, cache_entrydn_cmp, avl_dup_error )
136             != 0 ) {
137                 Debug( LDAP_DEBUG_TRACE,
138                         "====> cache_add_entry lock: entry %20s id %d already in dn cache\n",
139                     e->e_dn, e->e_id, 0 );
140
141                 /* free cache mutex */
142                 pthread_mutex_unlock( &cache->c_mutex );
143                 return( 1 );
144         }
145
146         /* id tree */
147         if ( avl_insert( &cache->c_idtree, e, cache_entryid_cmp, avl_dup_error )
148             != 0 ) {
149                 Debug( LDAP_DEBUG_ANY,
150                         "====> entry %20s id %d already in id cache\n",
151                     e->e_dn, e->e_id, 0 );
152
153                 /* delete from dn tree inserted above */
154                 if ( avl_delete( &cache->c_dntree, e, cache_entrydn_cmp )
155                     == NULL ) {
156                         Debug( LDAP_DEBUG_ANY, "====> can't delete from dn cache\n",
157                             0, 0, 0 );
158                 }
159
160                 /* free cache mutex */
161                 pthread_mutex_unlock( &cache->c_mutex );
162                 return( -1 );
163         }
164
165         e->e_state = state;
166         e->e_refcnt = 1;
167
168         /* lru */
169         LRU_ADD( cache, e );
170         if ( ++cache->c_cursize > cache->c_maxsize ) {
171                 /*
172                  * find the lru entry not currently in use and delete it.
173                  * in case a lot of entries are in use, only look at the
174                  * first 10 on the tail of the list.
175                  */
176                 i = 0;
177                 while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
178                     != 0 && i < 10 ) {
179                         /* move this in-use entry to the front of the q */
180                         ee = cache->c_lrutail;
181                         LRU_DELETE( cache, ee );
182                         LRU_ADD( cache, ee );
183                         i++;
184                 }
185
186                 /*
187                  * found at least one to delete - try to get back under
188                  * the max cache size.
189                  */
190                 while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
191                     == 0 && cache->c_cursize > cache->c_maxsize ) {
192                         e = cache->c_lrutail;
193
194                         /* XXX check for writer lock - should also check no readers pending */
195                         assert(pthread_rdwr_wchk_np(&e->e_rdwr));
196
197                         /* delete from cache and lru q */
198                         rc = cache_delete_entry_internal( cache, e );
199
200                         entry_free( e );
201                 }
202         }
203
204         /* free cache mutex */
205         pthread_mutex_unlock( &cache->c_mutex );
206         return( 0 );
207 }
208
209 /*
210  * cache_find_entry_dn2id - find an entry in the cache, given dn
211  */
212
213 ID
214 cache_find_entry_dn2id(
215         Backend         *be,
216     struct cache        *cache,
217     char                *dn
218 )
219 {
220         struct ldbminfo *li = (struct ldbminfo *) be->be_private;
221         Entry           e, *ep;
222         ID                      id;
223
224         /* set cache mutex */
225         pthread_mutex_lock( &cache->c_mutex );
226
227         e.e_dn = dn;
228
229         if ( (ep = (Entry *) avl_find( cache->c_dntree, &e, cache_entrydn_cmp ))
230                 != NULL ) {
231
232                 Debug(LDAP_DEBUG_TRACE, "====> cache_find_entry_dn2id: found dn: %s\n",
233                         dn, 0, 0);
234
235                 /*
236                  * entry is deleted or not fully created yet
237                  */
238                 if ( ep->e_state == ENTRY_STATE_DELETED ||
239                         ep->e_state == ENTRY_STATE_CREATING )
240                 {
241                         /* free cache mutex */
242                         pthread_mutex_unlock( &cache->c_mutex );
243                         return( NOID );
244                 }
245
246                 /* XXX is this safe without writer lock? */
247                 ep->e_refcnt++;
248
249                 /* lru */
250                 LRU_DELETE( cache, ep );
251                 LRU_ADD( cache, ep );
252
253                 /* acquire reader lock */
254                 entry_rdwr_lock(ep, 0);
255
256                 /* re-check */
257                 if ( ep->e_state == ENTRY_STATE_DELETED ||
258                         ep->e_state == ENTRY_STATE_CREATING )
259                 {
260                         /* XXX check that is is required */
261                         ep->e_refcnt--;
262
263                         /* free reader lock */
264                         entry_rdwr_unlock(ep, 0);
265                         /* free cache mutex */
266                         pthread_mutex_unlock( &cache->c_mutex );
267
268                         return( NOID );
269                 }
270
271                 /* save id */
272                 id = ep->e_id;
273
274                 /* free reader lock */
275                 entry_rdwr_unlock(ep, 0);
276
277                 /* free cache mutex */
278                 pthread_mutex_unlock( &cache->c_mutex );
279
280                 cache_return_entry( &li->li_cache, ep );
281
282                 return( id );
283         }
284
285         /* free cache mutex */
286         pthread_mutex_unlock( &cache->c_mutex );
287
288         return( NOID );
289 }
290
291 /*
292  * cache_find_entry_id - find an entry in the cache, given id
293  */
294
295 Entry *
296 cache_find_entry_id(
297         struct cache    *cache,
298         ID                              id,
299         int                             rw
300 )
301 {
302         Entry   e;
303         Entry   *ep;
304
305         /* set cache mutex */
306         pthread_mutex_lock( &cache->c_mutex );
307
308         e.e_id = id;
309
310         if ( (ep = (Entry *) avl_find( cache->c_idtree, &e, cache_entryid_cmp ))
311                 != NULL ) {
312
313                 Debug(LDAP_DEBUG_TRACE,
314                         "====> cache_find_entry_dn2id: found id: %ld rw: %d\n",
315                         id, rw, 0);
316
317                 /*
318                  * entry is deleted or not fully created yet
319                  */
320                 if ( ep->e_state == ENTRY_STATE_DELETED ||
321                         ep->e_state == ENTRY_STATE_CREATING )
322                 {
323                         /* free cache mutex */
324                         pthread_mutex_unlock( &cache->c_mutex );
325                         return( NULL );
326                 }
327                 /* XXX is this safe without writer lock? */
328                 ep->e_refcnt++;
329
330                 /* lru */
331                 LRU_DELETE( cache, ep );
332                 LRU_ADD( cache, ep );
333                 
334                 /* acquire reader lock */
335                 entry_rdwr_lock(ep, 0);
336
337                 /* re-check */
338                 if ( ep->e_state == ENTRY_STATE_DELETED ||
339                         ep->e_state == ENTRY_STATE_CREATING ) {
340
341                         /* XXX check that is is required */
342                         ep->e_refcnt--;
343
344                         /* free reader lock */
345                         entry_rdwr_unlock(ep, 0);
346
347                         /* free cache mutex */
348                         pthread_mutex_unlock( &cache->c_mutex );
349                         return( NULL );
350                 }
351
352                 if ( rw ) {
353                         entry_rdwr_unlock(ep, 0);
354                         entry_rdwr_lock(ep, 1);
355                 }
356
357                 /* free cache mutex */
358                 pthread_mutex_unlock( &cache->c_mutex );
359
360                 return( ep );
361         }
362
363         /* free cache mutex */
364         pthread_mutex_unlock( &cache->c_mutex );
365
366         return( NULL );
367 }
368
369 /*
370  * cache_delete_entry - delete the entry e from the cache.  the caller
371  * should have obtained e (increasing its ref count) via a call to one
372  * of the cache_find_* routines.  the caller should *not* call the
373  * cache_return_entry() routine prior to calling cache_delete_entry().
374  * it performs this function.
375  *
376  * returns:     0       e was deleted ok
377  *              1       e was not in the cache
378  *              -1      something bad happened
379  */
380 int
381 cache_delete_entry(
382     struct cache        *cache,
383     Entry               *e
384 )
385 {
386         int     rc;
387
388         Debug( LDAP_DEBUG_TRACE, "====> cache_delete_entry:\n", 0, 0, 0 );
389
390         /* XXX check for writer lock - should also check no readers pending */
391         assert(pthread_rdwr_wchk_np(&e->e_rdwr));
392
393         /* set cache mutex */
394         pthread_mutex_lock( &cache->c_mutex );
395
396         rc = cache_delete_entry_internal( cache, e );
397
398         /* free cache mutex */
399         pthread_mutex_unlock( &cache->c_mutex );
400         return( rc );
401 }
402
403 static int
404 cache_delete_entry_internal(
405     struct cache        *cache,
406     Entry               *e
407 )
408 {
409         /* dn tree */
410         if ( avl_delete( &cache->c_dntree, e, cache_entrydn_cmp ) == NULL ) {
411                 return( -1 );
412         }
413
414         /* id tree */
415         if ( avl_delete( &cache->c_idtree, e, cache_entryid_cmp ) == NULL ) {
416                 return( -1 );
417         }
418
419         /* lru */
420         LRU_DELETE( cache, e );
421         cache->c_cursize--;
422
423         /*
424          * flag entry to be freed later by a call to cache_return_entry()
425          */
426         e->e_state = ENTRY_STATE_DELETED;
427
428         return( 0 );
429 }
430
431 #ifdef LDAP_DEBUG
432
433 static void
434 lru_print( struct cache *cache )
435 {
436         Entry   *e;
437
438         fprintf( stderr, "LRU queue (head to tail):\n" );
439         for ( e = cache->c_lruhead; e != NULL; e = e->e_lrunext ) {
440                 fprintf( stderr, "\tdn %20s id %d refcnt %d\n", e->e_dn,
441                     e->e_id, e->e_refcnt );
442         }
443         fprintf( stderr, "LRU queue (tail to head):\n" );
444         for ( e = cache->c_lrutail; e != NULL; e = e->e_lruprev ) {
445                 fprintf( stderr, "\tdn %20s id %d refcnt %d\n", e->e_dn,
446                     e->e_id, e->e_refcnt );
447         }
448 }
449
450 #endif
451