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