]> git.sur5r.net Git - openldap/blob - servers/slapd/back-ldbm/cache.c
a9248865ef20182911cfc8bc2388f9887e758085
[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 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 #define LRU_DELETE( cache, e ) { \
70         if ( e->e_lruprev != NULL ) { \
71                 e->e_lruprev->e_lrunext = e->e_lrunext; \
72         } else { \
73                 cache->c_lruhead = e->e_lrunext; \
74         } \
75         if ( e->e_lrunext != NULL ) { \
76                 e->e_lrunext->e_lruprev = e->e_lruprev; \
77         } else { \
78                 cache->c_lrutail = e->e_lruprev; \
79         } \
80 }
81
82 #define LRU_ADD( cache, e ) { \
83         e->e_lrunext = cache->c_lruhead; \
84         if ( e->e_lrunext != NULL ) { \
85                 e->e_lrunext->e_lruprev = e; \
86         } \
87         cache->c_lruhead = e; \
88         e->e_lruprev = NULL; \
89         if ( cache->c_lrutail == NULL ) { \
90                 cache->c_lrutail = e; \
91         } \
92 }
93
94 /*
95  * cache_create_entry_lock - create an entry in the cache, and lock it.
96  * returns:     0       entry has been created and locked
97  *              1       entry already existed
98  *              -1      something bad happened
99  */
100 int
101 cache_add_entry_lock(
102     struct cache        *cache,
103     Entry               *e,
104     int                 state
105 )
106 {
107         int     i, rc;
108         Entry   *ee;
109
110         /* set cache mutex */
111         pthread_mutex_lock( &cache->c_mutex );
112
113         if ( avl_insert( &cache->c_dntree, (caddr_t) e,
114                 cache_entrydn_cmp, avl_dup_error ) != 0 )
115         {
116                 Debug( LDAP_DEBUG_TRACE,
117                     "entry %20s id %d already in dn cache\n", e->e_dn,
118                     e->e_id, 0 );
119
120                 /* free cache mutex */
121                 pthread_mutex_unlock( &cache->c_mutex );
122                 return( 1 );
123         }
124
125         /* id tree */
126         if ( avl_insert( &cache->c_idtree, (caddr_t) e,
127                 cache_entryid_cmp, avl_dup_error ) != 0 )
128         {
129                 Debug( LDAP_DEBUG_ANY, "entry %20s id %d already in id cache\n",
130                     e->e_dn, e->e_id, 0 );
131
132                 /* delete from dn tree inserted above */
133                 if ( avl_delete( &cache->c_dntree, (caddr_t) e,
134                         cache_entrydn_cmp ) == NULL )
135                 {
136                         Debug( LDAP_DEBUG_ANY, "can't delete from dn cache\n",
137                             0, 0, 0 );
138                 }
139
140                 /* free cache mutex */
141                 pthread_mutex_unlock( &cache->c_mutex );
142                 return( -1 );
143         }
144
145         e->e_state = state;
146         e->e_refcnt = 1;
147
148         /* lru */
149         LRU_ADD( cache, e );
150         if ( ++cache->c_cursize > cache->c_maxsize ) {
151                 /*
152                  * find the lru entry not currently in use and delete it.
153                  * in case a lot of entries are in use, only look at the
154                  * first 10 on the tail of the list.
155                  */
156                 i = 0;
157                 while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
158                     != 0 && i < 10 ) {
159                         /* move this in-use entry to the front of the q */
160                         ee = cache->c_lrutail;
161                         LRU_DELETE( cache, ee );
162                         LRU_ADD( cache, ee );
163                         i++;
164                 }
165
166                 /*
167                  * found at least one to delete - try to get back under
168                  * the max cache size.
169                  */
170                 while ( cache->c_lrutail != NULL && cache->c_lrutail->e_refcnt
171                     == 0 && cache->c_cursize > cache->c_maxsize ) {
172                         e = cache->c_lrutail;
173
174                         /* delete from cache and lru q */
175                         rc = cache_delete_entry_internal( cache, e );
176
177                         entry_free( e );
178                 }
179         }
180
181         /* free cache mutex */
182         pthread_mutex_unlock( &cache->c_mutex );
183         return( 0 );
184 }
185
186 /*
187  * cache_find_entry_dn - find an entry in the cache, given dn
188  */
189
190 Entry *
191 cache_find_entry_dn(
192     struct cache        *cache,
193     char                *dn
194 )
195 {
196         Entry           e, *ep;
197
198         /* set cache mutex */
199         pthread_mutex_lock( &cache->c_mutex );
200
201         e.e_dn = dn;
202         if ( (ep = (Entry *) avl_find( cache->c_dntree, (caddr_t) &e,
203                 cache_entrydn_cmp )) != NULL )
204         {
205                 /*
206                  * entry is deleted or not fully created yet
207                  */
208                 if ( ep->e_state == ENTRY_STATE_DELETED ||
209                     ep->e_state == ENTRY_STATE_CREATING )
210                 {
211                         /* free cache mutex */
212                         pthread_mutex_unlock( &cache->c_mutex );
213                         return( NULL );
214                 }
215                 ep->e_refcnt++;
216
217                 /* lru */
218                 LRU_DELETE( cache, ep );
219                 LRU_ADD( cache, ep );
220         }
221
222         /* free cache mutex */
223         pthread_mutex_unlock( &cache->c_mutex );
224
225         return( ep );
226 }
227
228 /*
229  * cache_find_entry_id - find an entry in the cache, given id
230  */
231
232 Entry *
233 cache_find_entry_id(
234     struct cache        *cache,
235     ID                  id
236 )
237 {
238         Entry   e;
239         Entry   *ep;
240
241         /* set cache mutex */
242         pthread_mutex_lock( &cache->c_mutex );
243
244         e.e_id = id;
245         if ( (ep = (Entry *) avl_find( cache->c_idtree, (caddr_t) &e,
246                 cache_entryid_cmp )) != NULL )
247         {
248                 /*
249                  * entry is deleted or not fully created yet
250                  */
251                 if ( ep->e_state == ENTRY_STATE_DELETED ||
252                     ep->e_state == ENTRY_STATE_CREATING )
253                 {
254                         /* free cache mutex */
255                         pthread_mutex_unlock( &cache->c_mutex );
256                         return( NULL );
257                 }
258                 ep->e_refcnt++;
259
260                 /* lru */
261                 LRU_DELETE( cache, ep );
262                 LRU_ADD( cache, ep );
263         }
264
265         /* free cache mutex */
266         pthread_mutex_unlock( &cache->c_mutex );
267
268         return( ep );
269 }
270
271 /*
272  * cache_delete_entry - delete the entry e from the cache.  the caller
273  * should have obtained e (increasing its ref count) via a call to one
274  * of the cache_find_* routines.  the caller should *not* call the
275  * cache_return_entry() routine prior to calling cache_delete_entry().
276  * it performs this function.
277  *
278  * returns:     0       e was deleted ok
279  *              1       e was not in the cache
280  *              -1      something bad happened
281  */
282 int
283 cache_delete_entry(
284     struct cache        *cache,
285     Entry               *e
286 )
287 {
288         int     rc;
289
290         /* set cache mutex */
291         pthread_mutex_lock( &cache->c_mutex );
292
293         rc = cache_delete_entry_internal( cache, e );
294
295         /* free cache mutex */
296         pthread_mutex_unlock( &cache->c_mutex );
297         return( rc );
298 }
299
300 static int
301 cache_delete_entry_internal(
302     struct cache        *cache,
303     Entry               *e
304 )
305 {
306         /* dn tree */
307         if ( avl_delete( &cache->c_dntree, (caddr_t) e, cache_entrydn_cmp )
308                 == NULL )
309         {
310                 return( -1 );
311         }
312
313         /* id tree */
314         if ( avl_delete( &cache->c_idtree, (caddr_t) e, cache_entryid_cmp )
315                 == NULL )
316         {
317                 return( -1 );
318         }
319
320         /* lru */
321         LRU_DELETE( cache, e );
322         cache->c_cursize--;
323
324         /*
325          * flag entry to be freed later by a call to cache_return_entry()
326          */
327         e->e_state = ENTRY_STATE_DELETED;
328
329         return( 0 );
330 }
331
332 #ifdef LDAP_DEBUG
333
334 static void
335 lru_print( struct cache *cache )
336 {
337         Entry   *e;
338
339         fprintf( stderr, "LRU queue (head to tail):\n" );
340         for ( e = cache->c_lruhead; e != NULL; e = e->e_lrunext ) {
341                 fprintf( stderr, "\tdn %20s id %d refcnt %d\n", e->e_dn,
342                     e->e_id, e->e_refcnt );
343         }
344         fprintf( stderr, "LRU queue (tail to head):\n" );
345         for ( e = cache->c_lrutail; e != NULL; e = e->e_lruprev ) {
346                 fprintf( stderr, "\tdn %20s id %d refcnt %d\n", e->e_dn,
347                     e->e_id, e->e_refcnt );
348         }
349 }
350
351 #endif