xref: /AOO41X/main/sal/rtl/source/alloc_cache.c (revision ff0525f24f03981d56b7579b645949f111420994)
1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  You may obtain a copy of the License at
10  *
11  *   http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 #include "alloc_cache.h"
25 #include "alloc_impl.h"
26 #include "alloc_arena.h"
27 #include "internal/once.h"
28 #include "sal/macros.h"
29 #include "osl/diagnose.h"
30 
31 #ifndef INCLUDED_STRING_H
32 #include <string.h>
33 #endif
34 
35 #ifndef INCLUDED_STDIO_H
36 #include <stdio.h>
37 #endif
38 
39 #ifdef OS2
40 #undef OSL_TRACE
41 #define OSL_TRACE                  1 ? ((void)0) : _OSL_GLOBAL osl_trace
42 #endif
43 
44 /* ================================================================= *
45  *
46  * cache internals.
47  *
48  * ================================================================= */
49 
50 /** g_cache_list
51  *  @internal
52  */
53 struct rtl_cache_list_st
54 {
55     rtl_memory_lock_type m_lock;
56     rtl_cache_type       m_cache_head;
57 
58 #if defined(SAL_UNX) || defined(SAL_OS2)
59     pthread_t            m_update_thread;
60     pthread_cond_t       m_update_cond;
61 #elif defined(SAL_W32)
62     HANDLE               m_update_thread;
63     HANDLE               m_update_cond;
64 #endif /* SAL_UNX || SAL_W32 */
65     int                  m_update_done;
66 };
67 
68 static struct rtl_cache_list_st g_cache_list;
69 
70 
71 /** gp_cache_arena
72  *  provided for cache_type allocations, and hash_table resizing.
73  *
74  *  @internal
75  */
76 static rtl_arena_type * gp_cache_arena = 0;
77 
78 
79 /** gp_cache_magazine_cache
80  *  @internal
81  */
82 static rtl_cache_type * gp_cache_magazine_cache = 0;
83 
84 
85 /** gp_cache_slab_cache
86  *  @internal
87  */
88 static rtl_cache_type * gp_cache_slab_cache = 0;
89 
90 
91 /** gp_cache_bufctl_cache
92  *  @internal
93  */
94 static rtl_cache_type * gp_cache_bufctl_cache = 0;
95 
96 
97 /** rtl_cache_init()
98  *  @internal
99  */
100 static int
101 rtl_cache_init (void);
102 
103 
104 /* ================================================================= */
105 
106 /** RTL_CACHE_HASH_INDEX()
107  */
108 #define RTL_CACHE_HASH_INDEX_IMPL(a, s, q, m) \
109     ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m))
110 
111 #define RTL_CACHE_HASH_INDEX(cache, addr) \
112     RTL_CACHE_HASH_INDEX_IMPL((addr), (cache)->m_hash_shift, (cache)->m_type_shift, ((cache)->m_hash_size - 1))
113 
114 
115 /** rtl_cache_hash_rescale()
116  */
117 static void
118 rtl_cache_hash_rescale (
119     rtl_cache_type * cache,
120     sal_Size         new_size
121 )
122 {
123     rtl_cache_bufctl_type ** new_table;
124     sal_Size                 new_bytes;
125 
126     new_bytes = new_size * sizeof(rtl_cache_bufctl_type*);
127     new_table = (rtl_cache_bufctl_type**)rtl_arena_alloc(gp_cache_arena, &new_bytes);
128 
129     if (new_table != 0)
130     {
131         rtl_cache_bufctl_type ** old_table;
132         sal_Size                 old_size, i;
133 
134         memset (new_table, 0, new_bytes);
135 
136         RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_slab_lock));
137 
138         old_table = cache->m_hash_table;
139         old_size  = cache->m_hash_size;
140 
141         OSL_TRACE(
142             "rtl_cache_hash_rescale(\"%s\"): "
143             "nbuf: % " PRIu64 " (ave: %" PRIu64 "), frees: %" PRIu64 " "
144             "[old_size: %lu, new_size: %lu]",
145             cache->m_name,
146             cache->m_slab_stats.m_alloc - cache->m_slab_stats.m_free,
147             (cache->m_slab_stats.m_alloc - cache->m_slab_stats.m_free) >> cache->m_hash_shift,
148             cache->m_slab_stats.m_free,
149             old_size, new_size);
150 
151         cache->m_hash_table = new_table;
152         cache->m_hash_size  = new_size;
153         cache->m_hash_shift = highbit(cache->m_hash_size) - 1;
154 
155         for (i = 0; i < old_size; i++)
156         {
157             rtl_cache_bufctl_type * curr = old_table[i];
158             while (curr != 0)
159             {
160                 rtl_cache_bufctl_type  * next = curr->m_next;
161                 rtl_cache_bufctl_type ** head;
162 
163                 head = &(cache->m_hash_table[RTL_CACHE_HASH_INDEX(cache, curr->m_addr)]);
164                 curr->m_next = (*head);
165                 (*head) = curr;
166 
167                 curr = next;
168             }
169             old_table[i] = 0;
170         }
171 
172         RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
173 
174         if (old_table != cache->m_hash_table_0)
175         {
176             sal_Size old_bytes = old_size * sizeof(rtl_cache_bufctl_type*);
177             rtl_arena_free (gp_cache_arena, old_table, old_bytes);
178         }
179     }
180 }
181 
182 /** rtl_cache_hash_insert()
183  */
184 static RTL_MEMORY_INLINE sal_uIntPtr
185 rtl_cache_hash_insert (
186     rtl_cache_type *        cache,
187     rtl_cache_bufctl_type * bufctl
188 )
189 {
190     rtl_cache_bufctl_type ** ppHead;
191 
192     ppHead = &(cache->m_hash_table[RTL_CACHE_HASH_INDEX(cache, bufctl->m_addr)]);
193 
194     bufctl->m_next = (*ppHead);
195     (*ppHead) = bufctl;
196 
197     return (bufctl->m_addr);
198 }
199 
200 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
201 #pragma inline(rtl_cache_hash_insert)
202 #endif /* __SUNPRO_C */
203 
204 
205 /** rtl_cache_hash_remove()
206  */
207 static rtl_cache_bufctl_type *
208 rtl_cache_hash_remove (
209     rtl_cache_type * cache,
210     sal_uIntPtr      addr
211 )
212 {
213     rtl_cache_bufctl_type ** ppHead;
214     rtl_cache_bufctl_type  * bufctl;
215     sal_Size                 lookups = 0;
216 
217     ppHead = &(cache->m_hash_table[RTL_CACHE_HASH_INDEX(cache, addr)]);
218     while ((bufctl = *ppHead) != 0)
219     {
220         if (bufctl->m_addr == addr)
221         {
222             *ppHead = bufctl->m_next, bufctl->m_next = 0;
223             break;
224         }
225 
226         lookups += 1;
227         ppHead = &(bufctl->m_next);
228     }
229 
230     OSL_ASSERT (bufctl != 0); /* bad free */
231 
232     if (lookups > 1)
233     {
234         sal_Size nbuf = (sal_Size)(cache->m_slab_stats.m_alloc - cache->m_slab_stats.m_free);
235         if (nbuf > 4 * cache->m_hash_size)
236         {
237             if (!(cache->m_features & RTL_CACHE_FEATURE_RESCALE))
238             {
239                 sal_Size ave = nbuf >> cache->m_hash_shift;
240                 sal_Size new_size = cache->m_hash_size << (highbit(ave) - 1);
241 
242                 cache->m_features |= RTL_CACHE_FEATURE_RESCALE;
243                 RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
244                 rtl_cache_hash_rescale (cache, new_size);
245                 RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_slab_lock));
246                 cache->m_features &= ~RTL_CACHE_FEATURE_RESCALE;
247             }
248         }
249     }
250 
251     return (bufctl);
252 }
253 
254 /* ================================================================= */
255 
256 /** RTL_CACHE_SLAB()
257  */
258 #define RTL_CACHE_SLAB(addr, size) \
259     (((rtl_cache_slab_type*)(RTL_MEMORY_P2END((sal_uIntPtr)(addr), (size)))) - 1)
260 
261 
262 /** rtl_cache_slab_constructor()
263  */
264 static int
265 rtl_cache_slab_constructor (void * obj, void * arg)
266 {
267     rtl_cache_slab_type * slab = (rtl_cache_slab_type*)(obj);
268 
269     (void) arg; /* unused */
270 
271     QUEUE_START_NAMED(slab, slab_);
272     slab->m_ntypes = 0;
273 
274     return (1);
275 }
276 
277 
278 /** rtl_cache_slab_destructor()
279  */
280 static void
281 rtl_cache_slab_destructor (void * obj, void * arg)
282 {
283 #if OSL_DEBUG_LEVEL == 0
284     (void) obj; /* unused */
285 #else /* OSL_DEBUG_LEVEL */
286     rtl_cache_slab_type * slab = (rtl_cache_slab_type*)(obj);
287 
288     /* assure removed from queue(s) */
289     OSL_ASSERT(QUEUE_STARTED_NAMED(slab, slab_));
290 
291     /* assure no longer referenced */
292     OSL_ASSERT(slab->m_ntypes == 0);
293 #endif /* OSL_DEBUG_LEVEL */
294 
295     (void) arg; /* unused */
296 }
297 
298 
299 /** rtl_cache_slab_create()
300  *
301  *  @precond cache->m_slab_lock released.
302  */
303 static rtl_cache_slab_type *
304 rtl_cache_slab_create (
305     rtl_cache_type * cache
306 )
307 {
308     rtl_cache_slab_type * slab = 0;
309     void *                addr;
310     sal_Size              size;
311 
312     size = cache->m_slab_size;
313     addr = rtl_arena_alloc (cache->m_source, &size);
314     if (addr != 0)
315     {
316         OSL_ASSERT(size >= cache->m_slab_size);
317 
318         if (cache->m_features & RTL_CACHE_FEATURE_HASH)
319         {
320             /* allocate slab struct from slab cache */
321             OSL_ASSERT (cache != gp_cache_slab_cache);
322             slab = (rtl_cache_slab_type*)rtl_cache_alloc (gp_cache_slab_cache);
323         }
324         else
325         {
326             /* construct embedded slab struct */
327             slab = RTL_CACHE_SLAB(addr, cache->m_slab_size);
328             (void) rtl_cache_slab_constructor (slab, 0);
329         }
330         if (slab != 0)
331         {
332             slab->m_data = (sal_uIntPtr)(addr);
333 
334             /* dynamic freelist initialization */
335             slab->m_bp = slab->m_data;
336             slab->m_sp = 0;
337         }
338         else
339         {
340             rtl_arena_free (cache->m_source, addr, size);
341         }
342     }
343     return (slab);
344 }
345 
346 
347 /** rtl_cache_slab_destroy()
348  *
349  *  @precond cache->m_slab_lock released.
350  */
351 static void
352 rtl_cache_slab_destroy (
353     rtl_cache_type *      cache,
354     rtl_cache_slab_type * slab
355 )
356 {
357     void *   addr   = (void*)(slab->m_data);
358     sal_Size refcnt = slab->m_ntypes; slab->m_ntypes = 0;
359 
360     if (cache->m_features & RTL_CACHE_FEATURE_HASH)
361     {
362         /* cleanup bufctl(s) for free buffer(s) */
363         sal_Size ntypes = (slab->m_bp - slab->m_data) / cache->m_type_size;
364         for (ntypes -= refcnt; slab->m_sp != 0; ntypes--)
365         {
366             rtl_cache_bufctl_type * bufctl = slab->m_sp;
367 
368             /* pop from freelist */
369             slab->m_sp = bufctl->m_next, bufctl->m_next = 0;
370 
371             /* return bufctl struct to bufctl cache */
372             rtl_cache_free (gp_cache_bufctl_cache, bufctl);
373         }
374         OSL_ASSERT(ntypes == 0);
375 
376         /* return slab struct to slab cache */
377         rtl_cache_free (gp_cache_slab_cache, slab);
378     }
379     else
380     {
381         /* destruct embedded slab struct */
382         rtl_cache_slab_destructor (slab, 0);
383     }
384 
385     if ((refcnt == 0) || (cache->m_features & RTL_CACHE_FEATURE_BULKDESTROY))
386     {
387         /* free memory */
388         rtl_arena_free (cache->m_source, addr, cache->m_slab_size);
389     }
390 }
391 
392 
393 /** rtl_cache_slab_populate()
394  *
395  *  @precond cache->m_slab_lock acquired.
396  */
397 static int
398 rtl_cache_slab_populate (
399     rtl_cache_type * cache
400 )
401 {
402     rtl_cache_slab_type * slab;
403 
404     RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
405     slab = rtl_cache_slab_create (cache);
406     RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_slab_lock));
407     if (slab != 0)
408     {
409         /* update buffer start addr w/ current color */
410         slab->m_bp += cache->m_ncolor;
411 
412         /* update color for next slab */
413         cache->m_ncolor += cache->m_type_align;
414         if (cache->m_ncolor > cache->m_ncolor_max)
415             cache->m_ncolor = 0;
416 
417         /* update stats */
418         cache->m_slab_stats.m_mem_total += cache->m_slab_size;
419 
420         /* insert onto 'free' queue */
421         QUEUE_INSERT_HEAD_NAMED(&(cache->m_free_head), slab, slab_);
422     }
423     return (slab != 0);
424 }
425 
426 /* ================================================================= */
427 
428 /** rtl_cache_slab_alloc()
429  *
430  *  Allocate a buffer from slab layer; used by magazine layer.
431  */
432 static void *
433 rtl_cache_slab_alloc (
434     rtl_cache_type * cache
435 )
436 {
437     void                * addr = 0;
438     rtl_cache_slab_type * head;
439 
440     RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_slab_lock));
441 
442     head = &(cache->m_free_head);
443     if ((head->m_slab_next != head) || rtl_cache_slab_populate (cache))
444     {
445         rtl_cache_slab_type   * slab;
446         rtl_cache_bufctl_type * bufctl;
447 
448         slab = head->m_slab_next;
449         OSL_ASSERT(slab->m_ntypes < cache->m_ntypes);
450 
451         if (slab->m_sp == 0)
452         {
453             /* initialize bufctl w/ current 'slab->m_bp' */
454             OSL_ASSERT (slab->m_bp < slab->m_data + cache->m_ntypes * cache->m_type_size + cache->m_ncolor_max);
455             if (cache->m_features & RTL_CACHE_FEATURE_HASH)
456             {
457                 /* allocate bufctl */
458                 OSL_ASSERT (cache != gp_cache_bufctl_cache);
459                 bufctl = (rtl_cache_bufctl_type*)rtl_cache_alloc (gp_cache_bufctl_cache);
460                 if (bufctl == 0)
461                 {
462                     /* out of memory */
463                     RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
464                     return (0);
465                 }
466 
467                 bufctl->m_addr = slab->m_bp;
468                 bufctl->m_slab = (sal_uIntPtr)(slab);
469             }
470             else
471             {
472                 /* embedded bufctl */
473                 bufctl = (rtl_cache_bufctl_type*)(slab->m_bp);
474             }
475             bufctl->m_next = 0;
476 
477             /* update 'slab->m_bp' to next free buffer */
478             slab->m_bp += cache->m_type_size;
479 
480             /* assign bufctl to freelist */
481             slab->m_sp = bufctl;
482         }
483 
484         /* pop front */
485         bufctl = slab->m_sp;
486         slab->m_sp = bufctl->m_next;
487 
488         /* increment usage, check for full slab */
489         if ((slab->m_ntypes += 1) == cache->m_ntypes)
490         {
491             /* remove from 'free' queue */
492             QUEUE_REMOVE_NAMED(slab, slab_);
493 
494             /* insert onto 'used' queue (tail) */
495             QUEUE_INSERT_TAIL_NAMED(&(cache->m_used_head), slab, slab_);
496         }
497 
498         /* update stats */
499         cache->m_slab_stats.m_alloc     += 1;
500         cache->m_slab_stats.m_mem_alloc += cache->m_type_size;
501 
502         if (cache->m_features & RTL_CACHE_FEATURE_HASH)
503             addr = (void*)rtl_cache_hash_insert (cache, bufctl);
504         else
505             addr = bufctl;
506 
507         /* DEBUG ONLY: mark allocated, undefined */
508         OSL_DEBUG_ONLY(memset(addr, 0x77777777, cache->m_type_size));
509         VALGRIND_MEMPOOL_ALLOC(cache, addr, cache->m_type_size);
510     }
511 
512     RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
513     return (addr);
514 }
515 
516 
517 /** rtl_cache_slab_free()
518  *
519  *  Return a buffer to slab layer; used by magazine layer.
520  */
521 static void
522 rtl_cache_slab_free (
523     rtl_cache_type * cache,
524     void *           addr
525 )
526 {
527     rtl_cache_bufctl_type * bufctl;
528     rtl_cache_slab_type   * slab;
529 
530     RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_slab_lock));
531 
532     /* DEBUG ONLY: mark unallocated, undefined */
533     VALGRIND_MEMPOOL_FREE(cache, addr);
534     /* OSL_DEBUG_ONLY() */ VALGRIND_MAKE_MEM_UNDEFINED(addr, cache->m_type_size);
535     OSL_DEBUG_ONLY(memset(addr, 0x33333333, cache->m_type_size));
536 
537     /* determine slab from addr */
538     if (cache->m_features & RTL_CACHE_FEATURE_HASH)
539     {
540         bufctl = rtl_cache_hash_remove (cache, (sal_uIntPtr)(addr));
541         slab = (bufctl != 0) ? (rtl_cache_slab_type*)(bufctl->m_slab) : 0;
542     }
543     else
544     {
545         /* embedded slab struct */
546         bufctl = (rtl_cache_bufctl_type*)(addr);
547         slab = RTL_CACHE_SLAB(addr, cache->m_slab_size);
548     }
549 
550     if (slab != 0)
551     {
552         /* check for full slab */
553         if (slab->m_ntypes == cache->m_ntypes)
554         {
555             /* remove from 'used' queue */
556             QUEUE_REMOVE_NAMED(slab, slab_);
557 
558             /* insert onto 'free' queue (head) */
559             QUEUE_INSERT_HEAD_NAMED(&(cache->m_free_head), slab, slab_);
560         }
561 
562         /* push front */
563         bufctl->m_next = slab->m_sp;
564         slab->m_sp = bufctl;
565 
566         /* update stats */
567         cache->m_slab_stats.m_free      += 1;
568         cache->m_slab_stats.m_mem_alloc -= cache->m_type_size;
569 
570         /* decrement usage, check for empty slab */
571         if ((slab->m_ntypes -= 1) == 0)
572         {
573             /* remove from 'free' queue */
574             QUEUE_REMOVE_NAMED(slab, slab_);
575 
576             /* update stats */
577             cache->m_slab_stats.m_mem_total -= cache->m_slab_size;
578 
579             /* free 'empty' slab */
580             RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
581             rtl_cache_slab_destroy (cache, slab);
582             return;
583         }
584     }
585 
586     RTL_MEMORY_LOCK_RELEASE(&(cache->m_slab_lock));
587 }
588 
589 /* ================================================================= */
590 
591 /** rtl_cache_magazine_constructor()
592  */
593 static int
594 rtl_cache_magazine_constructor (void * obj, void * arg)
595 {
596     rtl_cache_magazine_type * mag = (rtl_cache_magazine_type*)(obj);
597     /* @@@ sal_Size size = (sal_Size)(arg); @@@ */
598 
599     (void) arg; /* unused */
600 
601     mag->m_mag_next = 0;
602     mag->m_mag_size = RTL_CACHE_MAGAZINE_SIZE;
603     mag->m_mag_used = 0;
604 
605     return (1);
606 }
607 
608 
609 /** rtl_cache_magazine_destructor()
610  */
611 static void
612 rtl_cache_magazine_destructor (void * obj, void * arg)
613 {
614 #if OSL_DEBUG_LEVEL == 0
615     (void) obj; /* unused */
616 #else /* OSL_DEBUG_LEVEL */
617     rtl_cache_magazine_type * mag = (rtl_cache_magazine_type*)(obj);
618 
619     /* assure removed from queue(s) */
620     OSL_ASSERT(mag->m_mag_next == 0);
621 
622     /* assure no longer referenced */
623     OSL_ASSERT(mag->m_mag_used == 0);
624 #endif /* OSL_DEBUG_LEVEL */
625 
626     (void) arg; /* unused */
627 }
628 
629 
630 /** rtl_cache_magazine_clear()
631  */
632 static void
633 rtl_cache_magazine_clear (
634     rtl_cache_type *          cache,
635     rtl_cache_magazine_type * mag
636 )
637 {
638     for (; mag->m_mag_used > 0; --mag->m_mag_used)
639     {
640         void * obj = mag->m_objects[mag->m_mag_used - 1];
641         mag->m_objects[mag->m_mag_used - 1] = 0;
642 
643         /* DEBUG ONLY: mark cached object allocated, undefined */
644         VALGRIND_MEMPOOL_ALLOC(cache, obj, cache->m_type_size);
645         if (cache->m_destructor != 0)
646         {
647             /* DEBUG ONLY: keep constructed object defined */
648             VALGRIND_MAKE_MEM_DEFINED(obj, cache->m_type_size);
649 
650             /* destruct object */
651             (cache->m_destructor)(obj, cache->m_userarg);
652         }
653 
654         /* return buffer to slab layer */
655         rtl_cache_slab_free (cache, obj);
656     }
657 }
658 
659 /* ================================================================= */
660 
661 /** rtl_cache_depot_enqueue()
662  *
663  *  @precond cache->m_depot_lock acquired.
664  */
665 static RTL_MEMORY_INLINE void
666 rtl_cache_depot_enqueue (
667     rtl_cache_depot_type *    depot,
668     rtl_cache_magazine_type * mag
669 )
670 {
671     /* enqueue empty magazine */
672     mag->m_mag_next = depot->m_mag_next;
673     depot->m_mag_next = mag;
674 
675     /* update depot stats */
676     depot->m_mag_count++;
677 }
678 
679 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
680 #pragma inline(rtl_cache_depot_enqueue)
681 #endif /* __SUNPRO_C */
682 
683 
684 /** rtl_cache_depot_dequeue()
685  *
686  *  @precond cache->m_depot_lock acquired.
687  */
688 static RTL_MEMORY_INLINE rtl_cache_magazine_type *
689 rtl_cache_depot_dequeue (
690     rtl_cache_depot_type * depot
691 )
692 {
693     rtl_cache_magazine_type * mag = 0;
694     if (depot->m_mag_count > 0)
695     {
696         /* dequeue magazine */
697         OSL_ASSERT(depot->m_mag_next != 0);
698 
699         mag = depot->m_mag_next;
700         depot->m_mag_next = mag->m_mag_next;
701         mag->m_mag_next = 0;
702 
703         /* update depot stats */
704         depot->m_mag_count--;
705         depot->m_curr_min = SAL_MIN(depot->m_curr_min, depot->m_mag_count);
706     }
707     return (mag);
708 }
709 
710 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
711 #pragma inline(rtl_cache_depot_dequeue)
712 #endif /* __SUNPRO_C */
713 
714 
715 /** rtl_cache_depot_exchange_alloc()
716  *
717  *  @precond cache->m_depot_lock acquired.
718  */
719 static RTL_MEMORY_INLINE rtl_cache_magazine_type *
720 rtl_cache_depot_exchange_alloc (
721     rtl_cache_type *          cache,
722     rtl_cache_magazine_type * empty
723 )
724 {
725     rtl_cache_magazine_type * full;
726 
727     OSL_ASSERT((empty == 0) || (empty->m_mag_used == 0));
728 
729     /* dequeue full magazine */
730     full = rtl_cache_depot_dequeue (&(cache->m_depot_full));
731     if ((full != 0) && (empty != 0))
732     {
733         /* enqueue empty magazine */
734         rtl_cache_depot_enqueue (&(cache->m_depot_empty), empty);
735     }
736 
737     OSL_ASSERT((full == 0) || (full->m_mag_used > 0));
738 
739     return (full);
740 }
741 
742 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
743 #pragma inline(rtl_cache_depot_exchange_alloc)
744 #endif /* __SUNPRO_C */
745 
746 
747 /** rtl_cache_depot_exchange_free()
748  *
749  *  @precond cache->m_depot_lock acquired.
750  */
751 static RTL_MEMORY_INLINE rtl_cache_magazine_type *
752 rtl_cache_depot_exchange_free (
753     rtl_cache_type *          cache,
754     rtl_cache_magazine_type * full
755 )
756 {
757     rtl_cache_magazine_type * empty;
758 
759     OSL_ASSERT((full == 0) || (full->m_mag_used > 0));
760 
761     /* dequeue empty magazine */
762     empty = rtl_cache_depot_dequeue (&(cache->m_depot_empty));
763     if ((empty != 0) && (full != 0))
764     {
765         /* enqueue full magazine */
766         rtl_cache_depot_enqueue (&(cache->m_depot_full), full);
767     }
768 
769     OSL_ASSERT((empty == 0) || (empty->m_mag_used == 0));
770 
771     return (empty);
772 }
773 
774 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
775 #pragma inline(rtl_cache_depot_exchange_free)
776 #endif /* __SUNPRO_C */
777 
778 
779 /** rtl_cache_depot_populate()
780  *
781  *  @precond cache->m_depot_lock acquired.
782  */
783 static int
784 rtl_cache_depot_populate (
785     rtl_cache_type * cache
786 )
787 {
788     rtl_cache_magazine_type * empty = 0;
789 
790     if (cache->m_magazine_cache != 0)
791     {
792         /* allocate new empty magazine */
793         RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
794         empty = (rtl_cache_magazine_type*)rtl_cache_alloc (cache->m_magazine_cache);
795         RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_depot_lock));
796         if (empty != 0)
797         {
798             /* enqueue (new) empty magazine */
799             rtl_cache_depot_enqueue (&(cache->m_depot_empty), empty);
800         }
801     }
802     return (empty != 0);
803 }
804 
805 /* ================================================================= */
806 
807 /** rtl_cache_constructor()
808  */
809 static int
810 rtl_cache_constructor (void * obj)
811 {
812     rtl_cache_type * cache = (rtl_cache_type*)(obj);
813 
814     memset (cache, 0, sizeof(rtl_cache_type));
815 
816     /* linkage */
817     QUEUE_START_NAMED(cache, cache_);
818 
819     /* slab layer */
820     (void)RTL_MEMORY_LOCK_INIT(&(cache->m_slab_lock));
821 
822     QUEUE_START_NAMED(&(cache->m_free_head), slab_);
823     QUEUE_START_NAMED(&(cache->m_used_head), slab_);
824 
825     cache->m_hash_table = cache->m_hash_table_0;
826     cache->m_hash_size  = RTL_CACHE_HASH_SIZE;
827     cache->m_hash_shift = highbit(cache->m_hash_size) - 1;
828 
829     /* depot layer */
830     (void)RTL_MEMORY_LOCK_INIT(&(cache->m_depot_lock));
831 
832     return (1);
833 }
834 
835 /** rtl_cache_destructor()
836  */
837 static void
838 rtl_cache_destructor (void * obj)
839 {
840     rtl_cache_type * cache = (rtl_cache_type*)(obj);
841 
842     /* linkage */
843     OSL_ASSERT(QUEUE_STARTED_NAMED(cache, cache_));
844 
845     /* slab layer */
846     (void)RTL_MEMORY_LOCK_DESTROY(&(cache->m_slab_lock));
847 
848     OSL_ASSERT(QUEUE_STARTED_NAMED(&(cache->m_free_head), slab_));
849     OSL_ASSERT(QUEUE_STARTED_NAMED(&(cache->m_used_head), slab_));
850 
851     OSL_ASSERT(cache->m_hash_table == cache->m_hash_table_0);
852     OSL_ASSERT(cache->m_hash_size  == RTL_CACHE_HASH_SIZE);
853     OSL_ASSERT(cache->m_hash_shift == (sal_Size)(highbit(cache->m_hash_size) - 1));
854 
855     /* depot layer */
856     (void)RTL_MEMORY_LOCK_DESTROY(&(cache->m_depot_lock));
857 }
858 
859 /* ================================================================= */
860 
861 /** rtl_cache_activate()
862  */
863 static rtl_cache_type *
864 rtl_cache_activate (
865     rtl_cache_type * cache,
866     const char *     name,
867     size_t           objsize,
868     size_t           objalign,
869     int  (SAL_CALL * constructor)(void * obj, void * userarg),
870     void (SAL_CALL * destructor) (void * obj, void * userarg),
871     void (SAL_CALL * reclaim)    (void * userarg),
872     void *           userarg,
873     rtl_arena_type * source,
874     int              flags
875 )
876 {
877     OSL_ASSERT(cache != 0);
878     if (cache != 0)
879     {
880         sal_Size slabsize;
881 
882         snprintf (cache->m_name, sizeof(cache->m_name), "%s", name);
883 
884         /* ensure minimum size (embedded bufctl linkage) */
885         objsize = SAL_MAX(objsize, sizeof(rtl_cache_bufctl_type*));
886 
887         if (objalign == 0)
888         {
889             /* determine default alignment */
890             if (objsize >= RTL_MEMORY_ALIGNMENT_8)
891                 objalign = RTL_MEMORY_ALIGNMENT_8;
892             else
893                 objalign = RTL_MEMORY_ALIGNMENT_4;
894         }
895         else
896         {
897             /* ensure minimum alignment */
898             objalign = SAL_MAX(objalign, RTL_MEMORY_ALIGNMENT_4);
899         }
900         OSL_ASSERT(RTL_MEMORY_ISP2(objalign));
901 
902         cache->m_type_size  = objsize = RTL_MEMORY_P2ROUNDUP(objsize, objalign);
903         cache->m_type_align = objalign;
904         cache->m_type_shift = highbit(cache->m_type_size) - 1;
905 
906         cache->m_constructor = constructor;
907         cache->m_destructor  = destructor;
908         cache->m_reclaim     = reclaim;
909         cache->m_userarg     = userarg;
910 
911         /* slab layer */
912         cache->m_source = source;
913 
914         slabsize = source->m_quantum; /* minimum slab size */
915         if (flags & RTL_CACHE_FLAG_QUANTUMCACHE)
916         {
917             /* next power of 2 above 3 * qcache_max */
918             slabsize = SAL_MAX(slabsize, (1UL << highbit(3 * source->m_qcache_max)));
919         }
920         else
921         {
922             /* waste at most 1/8 of slab */
923             slabsize = SAL_MAX(slabsize, cache->m_type_size * 8);
924         }
925 
926         slabsize = RTL_MEMORY_P2ROUNDUP(slabsize, source->m_quantum);
927         if (!RTL_MEMORY_ISP2(slabsize))
928             slabsize = 1UL << highbit(slabsize);
929         cache->m_slab_size = slabsize;
930 
931         if (cache->m_slab_size > source->m_quantum)
932         {
933             OSL_ASSERT(gp_cache_slab_cache != 0);
934             OSL_ASSERT(gp_cache_bufctl_cache != 0);
935 
936             cache->m_features  |= RTL_CACHE_FEATURE_HASH;
937             cache->m_ntypes     = cache->m_slab_size / cache->m_type_size;
938             cache->m_ncolor_max = cache->m_slab_size % cache->m_type_size;
939         }
940         else
941         {
942             /* embedded slab struct */
943             cache->m_ntypes     = (cache->m_slab_size - sizeof(rtl_cache_slab_type)) / cache->m_type_size;
944             cache->m_ncolor_max = (cache->m_slab_size - sizeof(rtl_cache_slab_type)) % cache->m_type_size;
945         }
946 
947         OSL_ASSERT(cache->m_ntypes > 0);
948         cache->m_ncolor = 0;
949 
950         if (flags & RTL_CACHE_FLAG_BULKDESTROY)
951         {
952             /* allow bulk slab delete upon cache deactivation */
953             cache->m_features |= RTL_CACHE_FEATURE_BULKDESTROY;
954         }
955 
956         /* magazine layer */
957         if (!(flags & RTL_CACHE_FLAG_NOMAGAZINE))
958         {
959             OSL_ASSERT(gp_cache_magazine_cache != 0);
960             cache->m_magazine_cache = gp_cache_magazine_cache;
961         }
962 
963         /* insert into cache list */
964         RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
965         QUEUE_INSERT_TAIL_NAMED(&(g_cache_list.m_cache_head), cache, cache_);
966         RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
967     }
968     return (cache);
969 }
970 
971 /** rtl_cache_deactivate()
972  */
973 static void
974 rtl_cache_deactivate (
975     rtl_cache_type * cache
976 )
977 {
978     int active = 1;
979 
980     /* remove from cache list */
981     RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
982     active = QUEUE_STARTED_NAMED(cache, cache_) == 0;
983     QUEUE_REMOVE_NAMED(cache, cache_);
984     RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
985 
986     OSL_PRECOND(active, "rtl_cache_deactivate(): orphaned cache.");
987 
988     /* cleanup magazine layer */
989     if (cache->m_magazine_cache != 0)
990     {
991         rtl_cache_type *          mag_cache;
992         rtl_cache_magazine_type * mag;
993 
994         /* prevent recursion */
995         mag_cache = cache->m_magazine_cache, cache->m_magazine_cache = 0;
996 
997         /* cleanup cpu layer */
998         if ((mag = cache->m_cpu_curr) != 0)
999         {
1000             cache->m_cpu_curr = 0;
1001             rtl_cache_magazine_clear (cache, mag);
1002             rtl_cache_free (mag_cache, mag);
1003         }
1004         if ((mag = cache->m_cpu_prev) != 0)
1005         {
1006             cache->m_cpu_prev = 0;
1007             rtl_cache_magazine_clear (cache, mag);
1008             rtl_cache_free (mag_cache, mag);
1009         }
1010 
1011         /* cleanup depot layer */
1012         while ((mag = rtl_cache_depot_dequeue(&(cache->m_depot_full))) != 0)
1013         {
1014             rtl_cache_magazine_clear (cache, mag);
1015             rtl_cache_free (mag_cache, mag);
1016         }
1017         while ((mag = rtl_cache_depot_dequeue(&(cache->m_depot_empty))) != 0)
1018         {
1019             rtl_cache_magazine_clear (cache, mag);
1020             rtl_cache_free (mag_cache, mag);
1021         }
1022     }
1023 
1024     OSL_TRACE(
1025         "rtl_cache_deactivate(\"%s\"): "
1026         "[slab]: allocs: %"PRIu64", frees: %"PRIu64"; total: %lu, used: %lu; "
1027         "[cpu]: allocs: %"PRIu64", frees: %"PRIu64"; "
1028         "[total]: allocs: %"PRIu64", frees: %"PRIu64"",
1029         cache->m_name,
1030         cache->m_slab_stats.m_alloc, cache->m_slab_stats.m_free,
1031         cache->m_slab_stats.m_mem_total, cache->m_slab_stats.m_mem_alloc,
1032         cache->m_cpu_stats.m_alloc, cache->m_cpu_stats.m_free,
1033         cache->m_slab_stats.m_alloc + cache->m_cpu_stats.m_alloc,
1034         cache->m_slab_stats.m_free  + cache->m_cpu_stats.m_free
1035     );
1036 
1037     /* cleanup slab layer */
1038     if (cache->m_slab_stats.m_alloc > cache->m_slab_stats.m_free)
1039     {
1040         OSL_TRACE(
1041             "rtl_cache_deactivate(\"%s\"): "
1042             "cleaning up %"PRIu64" leaked buffer(s) [%lu bytes] [%lu total]",
1043             cache->m_name,
1044             cache->m_slab_stats.m_alloc - cache->m_slab_stats.m_free,
1045             cache->m_slab_stats.m_mem_alloc, cache->m_slab_stats.m_mem_total
1046         );
1047 
1048         if (cache->m_features & RTL_CACHE_FEATURE_HASH)
1049         {
1050             /* cleanup bufctl(s) for leaking buffer(s) */
1051             sal_Size i, n = cache->m_hash_size;
1052             for (i = 0; i < n; i++)
1053             {
1054                 rtl_cache_bufctl_type * bufctl;
1055                 while ((bufctl = cache->m_hash_table[i]) != 0)
1056                 {
1057                     /* pop from hash table */
1058                     cache->m_hash_table[i] = bufctl->m_next, bufctl->m_next = 0;
1059 
1060                     /* return to bufctl cache */
1061                     rtl_cache_free (gp_cache_bufctl_cache, bufctl);
1062                 }
1063             }
1064         }
1065         {
1066             /* force cleanup of remaining slabs */
1067             rtl_cache_slab_type *head, *slab;
1068 
1069             head = &(cache->m_used_head);
1070             for (slab = head->m_slab_next; slab != head; slab = head->m_slab_next)
1071             {
1072                 /* remove from 'used' queue */
1073                 QUEUE_REMOVE_NAMED(slab, slab_);
1074 
1075                 /* update stats */
1076                 cache->m_slab_stats.m_mem_total -= cache->m_slab_size;
1077 
1078                 /* free slab */
1079                 rtl_cache_slab_destroy (cache, slab);
1080             }
1081 
1082             head = &(cache->m_free_head);
1083             for (slab = head->m_slab_next; slab != head; slab = head->m_slab_next)
1084             {
1085                 /* remove from 'free' queue */
1086                 QUEUE_REMOVE_NAMED(slab, slab_);
1087 
1088                 /* update stats */
1089                 cache->m_slab_stats.m_mem_total -= cache->m_slab_size;
1090 
1091                 /* free slab */
1092                 rtl_cache_slab_destroy (cache, slab);
1093             }
1094         }
1095     }
1096 
1097     if (cache->m_hash_table != cache->m_hash_table_0)
1098     {
1099         rtl_arena_free (
1100             gp_cache_arena,
1101             cache->m_hash_table,
1102             cache->m_hash_size * sizeof(rtl_cache_bufctl_type*));
1103 
1104         cache->m_hash_table = cache->m_hash_table_0;
1105         cache->m_hash_size  = RTL_CACHE_HASH_SIZE;
1106         cache->m_hash_shift = highbit(cache->m_hash_size) - 1;
1107     }
1108 }
1109 
1110 /* ================================================================= *
1111  *
1112  * cache implementation.
1113  *
1114  * ================================================================= */
1115 
1116 /** rtl_cache_create()
1117  */
1118 rtl_cache_type *
1119 SAL_CALL rtl_cache_create (
1120     const char *     name,
1121     sal_Size         objsize,
1122     sal_Size         objalign,
1123     int  (SAL_CALL * constructor)(void * obj, void * userarg),
1124     void (SAL_CALL * destructor) (void * obj, void * userarg),
1125     void (SAL_CALL * reclaim)    (void * userarg),
1126     void *           userarg,
1127     rtl_arena_type * source,
1128     int              flags
1129 ) SAL_THROW_EXTERN_C()
1130 {
1131     rtl_cache_type * result = 0;
1132     sal_Size         size   = sizeof(rtl_cache_type);
1133 
1134 try_alloc:
1135     result = (rtl_cache_type*)rtl_arena_alloc (gp_cache_arena, &size);
1136     if (result != 0)
1137     {
1138         rtl_cache_type * cache = result;
1139         VALGRIND_CREATE_MEMPOOL(cache, 0, 0);
1140         (void) rtl_cache_constructor (cache);
1141 
1142         if (!source)
1143         {
1144             /* use default arena */
1145             OSL_ASSERT(gp_default_arena != 0);
1146             source = gp_default_arena;
1147         }
1148 
1149         result = rtl_cache_activate (
1150             cache,
1151             name,
1152             objsize,
1153             objalign,
1154             constructor,
1155             destructor,
1156             reclaim,
1157             userarg,
1158             source,
1159             flags
1160         );
1161 
1162         if (result == 0)
1163         {
1164             /* activation failed */
1165             rtl_cache_deactivate (cache);
1166             rtl_cache_destructor (cache);
1167             VALGRIND_DESTROY_MEMPOOL(cache);
1168             rtl_arena_free (gp_cache_arena, cache, size);
1169         }
1170     }
1171     else if (gp_cache_arena == 0)
1172     {
1173         if (rtl_cache_init())
1174         {
1175             /* try again */
1176             goto try_alloc;
1177         }
1178     }
1179     return (result);
1180 }
1181 
1182 /** rtl_cache_destroy()
1183  */
1184 void SAL_CALL rtl_cache_destroy (
1185     rtl_cache_type * cache
1186 ) SAL_THROW_EXTERN_C()
1187 {
1188     if (cache != 0)
1189     {
1190         rtl_cache_deactivate (cache);
1191         rtl_cache_destructor (cache);
1192         VALGRIND_DESTROY_MEMPOOL(cache);
1193         rtl_arena_free (gp_cache_arena, cache, sizeof(rtl_cache_type));
1194     }
1195 }
1196 
1197 /** rtl_cache_alloc()
1198  */
1199 void *
1200 SAL_CALL rtl_cache_alloc (
1201     rtl_cache_type * cache
1202 ) SAL_THROW_EXTERN_C()
1203 {
1204     void * obj = 0;
1205 
1206     if (cache == 0)
1207         return (0);
1208 
1209     if (cache->m_cpu_curr != 0)
1210     {
1211         RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_depot_lock));
1212 
1213         for (;;)
1214         {
1215             /* take object from magazine layer */
1216             rtl_cache_magazine_type *curr, *prev, *temp;
1217 
1218             curr = cache->m_cpu_curr;
1219             if ((curr != 0) && (curr->m_mag_used > 0))
1220             {
1221                 obj = curr->m_objects[--curr->m_mag_used];
1222 #if defined(HAVE_VALGRIND_MEMCHECK_H)
1223                 VALGRIND_MEMPOOL_ALLOC(cache, obj, cache->m_type_size);
1224                 if (cache->m_constructor != 0)
1225                 {
1226                     /* keep constructed object defined */
1227                     VALGRIND_MAKE_MEM_DEFINED(obj, cache->m_type_size);
1228                 }
1229 #endif /* HAVE_VALGRIND_MEMCHECK_H */
1230                 cache->m_cpu_stats.m_alloc += 1;
1231                 RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
1232 
1233                 return (obj);
1234             }
1235 
1236             prev = cache->m_cpu_prev;
1237             if ((prev != 0) && (prev->m_mag_used > 0))
1238             {
1239                 temp = cache->m_cpu_curr;
1240                 cache->m_cpu_curr = cache->m_cpu_prev;
1241                 cache->m_cpu_prev = temp;
1242 
1243                 continue;
1244             }
1245 
1246             temp = rtl_cache_depot_exchange_alloc (cache, prev);
1247             if (temp != 0)
1248             {
1249                 cache->m_cpu_prev = cache->m_cpu_curr;
1250                 cache->m_cpu_curr = temp;
1251 
1252                 continue;
1253             }
1254 
1255             /* no full magazine: fall through to slab layer */
1256             break;
1257         }
1258 
1259         RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
1260     }
1261 
1262     /* alloc buffer from slab layer */
1263     obj = rtl_cache_slab_alloc (cache);
1264     if ((obj != 0) && (cache->m_constructor != 0))
1265     {
1266         /* construct object */
1267         if (!((cache->m_constructor)(obj, cache->m_userarg)))
1268         {
1269             /* construction failure */
1270             rtl_cache_slab_free (cache, obj), obj = 0;
1271         }
1272     }
1273     return (obj);
1274 }
1275 
1276 /** rtl_cache_free()
1277  */
1278 void
1279 SAL_CALL rtl_cache_free (
1280     rtl_cache_type * cache,
1281     void *           obj
1282 ) SAL_THROW_EXTERN_C()
1283 {
1284     if ((obj != 0) && (cache != 0))
1285     {
1286         RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_depot_lock));
1287 
1288         for (;;)
1289         {
1290             /* return object to magazine layer */
1291             rtl_cache_magazine_type *curr, *prev, *temp;
1292 
1293             curr = cache->m_cpu_curr;
1294             if ((curr != 0) && (curr->m_mag_used < curr->m_mag_size))
1295             {
1296                 curr->m_objects[curr->m_mag_used++] = obj;
1297 #if defined(HAVE_VALGRIND_MEMCHECK_H)
1298                 VALGRIND_MEMPOOL_FREE(cache, obj);
1299 #endif /* HAVE_VALGRIND_MEMCHECK_H */
1300                 cache->m_cpu_stats.m_free += 1;
1301                 RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
1302 
1303                 return;
1304             }
1305 
1306             prev = cache->m_cpu_prev;
1307             if ((prev != 0) && (prev->m_mag_used == 0))
1308             {
1309                 temp = cache->m_cpu_curr;
1310                 cache->m_cpu_curr = cache->m_cpu_prev;
1311                 cache->m_cpu_prev = temp;
1312 
1313                 continue;
1314             }
1315 
1316             temp = rtl_cache_depot_exchange_free (cache, prev);
1317             if (temp != 0)
1318             {
1319                 cache->m_cpu_prev = cache->m_cpu_curr;
1320                 cache->m_cpu_curr = temp;
1321 
1322                 continue;
1323             }
1324 
1325             if (rtl_cache_depot_populate(cache) != 0)
1326             {
1327                 continue;
1328             }
1329 
1330             /* no empty magazine: fall through to slab layer */
1331             break;
1332         }
1333 
1334         RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
1335 
1336         /* no space for constructed object in magazine layer */
1337         if (cache->m_destructor != 0)
1338         {
1339             /* destruct object */
1340             (cache->m_destructor)(obj, cache->m_userarg);
1341         }
1342 
1343         /* return buffer to slab layer */
1344         rtl_cache_slab_free (cache, obj);
1345     }
1346 }
1347 
1348 /* ================================================================= *
1349  *
1350  * cache wsupdate (machdep) internals.
1351  *
1352  * ================================================================= */
1353 
1354 /** rtl_cache_wsupdate_init()
1355  *
1356  *  @precond g_cache_list.m_lock initialized
1357  */
1358 static void
1359 rtl_cache_wsupdate_init (void);
1360 
1361 
1362 /** rtl_cache_wsupdate_wait()
1363  *
1364  *  @precond g_cache_list.m_lock acquired
1365  */
1366 static void
1367 rtl_cache_wsupdate_wait (
1368     unsigned int seconds
1369 );
1370 
1371 /** rtl_cache_wsupdate_fini()
1372  *
1373  */
1374 static void
1375 rtl_cache_wsupdate_fini (void);
1376 
1377 /* ================================================================= */
1378 
1379 #if defined(SAL_UNX) || defined(SAL_OS2)
1380 
1381 #include <sys/time.h>
1382 
1383 static void *
1384 rtl_cache_wsupdate_all (void * arg);
1385 
1386 static void
1387 rtl_cache_wsupdate_init (void)
1388 {
1389     RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1390     g_cache_list.m_update_done = 0;
1391     (void) pthread_cond_init (&(g_cache_list.m_update_cond), NULL);
1392     if (pthread_create (
1393             &(g_cache_list.m_update_thread), NULL, rtl_cache_wsupdate_all, (void*)(10)) != 0)
1394     {
1395         /* failure */
1396         g_cache_list.m_update_thread = (pthread_t)(0);
1397     }
1398     RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1399 }
1400 
1401 static void
1402 rtl_cache_wsupdate_wait (unsigned int seconds)
1403 {
1404     if (seconds > 0)
1405     {
1406         struct timeval  now;
1407         struct timespec wakeup;
1408 
1409         gettimeofday(&now, 0);
1410         wakeup.tv_sec  = now.tv_sec + (seconds);
1411         wakeup.tv_nsec = now.tv_usec * 1000;
1412 
1413         (void) pthread_cond_timedwait (
1414             &(g_cache_list.m_update_cond),
1415             &(g_cache_list.m_lock),
1416             &wakeup);
1417     }
1418 }
1419 
1420 static void
1421 rtl_cache_wsupdate_fini (void)
1422 {
1423     RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1424     g_cache_list.m_update_done = 1;
1425     pthread_cond_signal (&(g_cache_list.m_update_cond));
1426     RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1427 
1428     if (g_cache_list.m_update_thread != (pthread_t)(0))
1429         pthread_join (g_cache_list.m_update_thread, NULL);
1430 }
1431 
1432 /* ================================================================= */
1433 
1434 #elif defined(SAL_W32)
1435 
1436 static DWORD WINAPI
1437 rtl_cache_wsupdate_all (void * arg);
1438 
1439 static void
1440 rtl_cache_wsupdate_init (void)
1441 {
1442     DWORD dwThreadId;
1443 
1444     RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1445     g_cache_list.m_update_done = 0;
1446     g_cache_list.m_update_cond = CreateEvent (0, TRUE, FALSE, 0);
1447 
1448     g_cache_list.m_update_thread =
1449         CreateThread (NULL, 0, rtl_cache_wsupdate_all, (LPVOID)(10), 0, &dwThreadId);
1450     RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1451 }
1452 
1453 static void
1454 rtl_cache_wsupdate_wait (unsigned int seconds)
1455 {
1456     if (seconds > 0)
1457     {
1458         RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1459         WaitForSingleObject (g_cache_list.m_update_cond, (DWORD)(seconds * 1000));
1460         RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1461     }
1462 }
1463 
1464 static void
1465 rtl_cache_wsupdate_fini (void)
1466 {
1467     RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1468     g_cache_list.m_update_done = 1;
1469     SetEvent (g_cache_list.m_update_cond);
1470     RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1471 
1472     WaitForSingleObject (g_cache_list.m_update_thread, INFINITE);
1473 }
1474 
1475 #endif /* SAL_UNX || SAL_W32 */
1476 
1477 /* ================================================================= */
1478 
1479 /** rtl_cache_depot_wsupdate()
1480  *  update depot stats and purge excess magazines.
1481  *
1482  *  @precond cache->m_depot_lock acquired
1483  */
1484 static void
1485 rtl_cache_depot_wsupdate (
1486     rtl_cache_type *       cache,
1487     rtl_cache_depot_type * depot
1488 )
1489 {
1490     sal_Size npurge;
1491 
1492     depot->m_prev_min = depot->m_curr_min;
1493     depot->m_curr_min = depot->m_mag_count;
1494 
1495     npurge = SAL_MIN(depot->m_curr_min, depot->m_prev_min);
1496     for (; npurge > 0; npurge--)
1497     {
1498         rtl_cache_magazine_type * mag = rtl_cache_depot_dequeue (depot);
1499         if (mag != 0)
1500         {
1501             RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
1502             rtl_cache_magazine_clear (cache, mag);
1503             rtl_cache_free (cache->m_magazine_cache, mag);
1504             RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_depot_lock));
1505         }
1506     }
1507 }
1508 
1509 /** rtl_cache_wsupdate()
1510  *
1511  *  @precond cache->m_depot_lock released
1512  */
1513 static void
1514 rtl_cache_wsupdate (
1515     rtl_cache_type * cache
1516 )
1517 {
1518     if (cache->m_magazine_cache != 0)
1519     {
1520         RTL_MEMORY_LOCK_ACQUIRE(&(cache->m_depot_lock));
1521 
1522         OSL_TRACE(
1523             "rtl_cache_wsupdate(\"%s\") "
1524             "[depot: count, curr_min, prev_min] "
1525             "full: %lu, %lu, %lu; empty: %lu, %lu, %lu",
1526             cache->m_name,
1527             cache->m_depot_full.m_mag_count,
1528             cache->m_depot_full.m_curr_min,
1529             cache->m_depot_full.m_prev_min,
1530             cache->m_depot_empty.m_mag_count,
1531             cache->m_depot_empty.m_curr_min,
1532             cache->m_depot_empty.m_prev_min
1533         );
1534 
1535         rtl_cache_depot_wsupdate (cache, &(cache->m_depot_full));
1536         rtl_cache_depot_wsupdate (cache, &(cache->m_depot_empty));
1537 
1538         RTL_MEMORY_LOCK_RELEASE(&(cache->m_depot_lock));
1539     }
1540 }
1541 
1542 /** rtl_cache_wsupdate_all()
1543  *
1544  */
1545 #if defined(SAL_UNX) || defined(SAL_OS2)
1546 static void *
1547 #elif defined(SAL_W32)
1548 static DWORD WINAPI
1549 #endif /* SAL_UNX || SAL_W32 */
1550 rtl_cache_wsupdate_all (void * arg)
1551 {
1552     unsigned int seconds = (unsigned int)SAL_INT_CAST(sal_uIntPtr, arg);
1553 
1554     RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1555     while (!g_cache_list.m_update_done)
1556     {
1557         rtl_cache_wsupdate_wait (seconds);
1558         if (!g_cache_list.m_update_done)
1559         {
1560             rtl_cache_type * head, * cache;
1561 
1562             head = &(g_cache_list.m_cache_head);
1563             for (cache  = head->m_cache_next;
1564                  cache != head;
1565                  cache  = cache->m_cache_next)
1566             {
1567                 rtl_cache_wsupdate (cache);
1568             }
1569         }
1570     }
1571     RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1572 
1573     return (0);
1574 }
1575 
1576 /* ================================================================= *
1577  *
1578  * cache initialization.
1579  *
1580  * ================================================================= */
1581 
1582 static void
1583 rtl_cache_once_init (void)
1584 {
1585     {
1586         /* list of caches */
1587         RTL_MEMORY_LOCK_INIT(&(g_cache_list.m_lock));
1588         (void) rtl_cache_constructor (&(g_cache_list.m_cache_head));
1589     }
1590     {
1591         /* cache: internal arena */
1592         OSL_ASSERT(gp_cache_arena == 0);
1593 
1594         gp_cache_arena = rtl_arena_create (
1595             "rtl_cache_internal_arena",
1596             64,   /* quantum */
1597             0,    /* no quantum caching */
1598             NULL, /* default source */
1599             rtl_arena_alloc,
1600             rtl_arena_free,
1601             0     /* flags */
1602         );
1603         OSL_ASSERT(gp_cache_arena != 0);
1604 
1605         /* check 'gp_default_arena' initialization */
1606         OSL_ASSERT(gp_default_arena != 0);
1607     }
1608     {
1609         /* cache: magazine cache */
1610         static rtl_cache_type g_cache_magazine_cache;
1611 
1612         OSL_ASSERT(gp_cache_magazine_cache == 0);
1613         VALGRIND_CREATE_MEMPOOL(&g_cache_magazine_cache, 0, 0);
1614         (void) rtl_cache_constructor (&g_cache_magazine_cache);
1615 
1616         gp_cache_magazine_cache = rtl_cache_activate (
1617             &g_cache_magazine_cache,
1618             "rtl_cache_magazine_cache",
1619             sizeof(rtl_cache_magazine_type), /* objsize  */
1620             0,                               /* objalign */
1621             rtl_cache_magazine_constructor,
1622             rtl_cache_magazine_destructor,
1623             0, /* reclaim */
1624             0, /* userarg: NYI */
1625             gp_default_arena, /* source */
1626             RTL_CACHE_FLAG_NOMAGAZINE /* during bootstrap; activated below */
1627         );
1628         OSL_ASSERT(gp_cache_magazine_cache != 0);
1629 
1630         /* activate magazine layer */
1631         g_cache_magazine_cache.m_magazine_cache = gp_cache_magazine_cache;
1632     }
1633     {
1634         /* cache: slab (struct) cache */
1635         static rtl_cache_type g_cache_slab_cache;
1636 
1637         OSL_ASSERT(gp_cache_slab_cache == 0);
1638         VALGRIND_CREATE_MEMPOOL(&g_cache_slab_cache, 0, 0);
1639         (void) rtl_cache_constructor (&g_cache_slab_cache);
1640 
1641         gp_cache_slab_cache = rtl_cache_activate (
1642             &g_cache_slab_cache,
1643             "rtl_cache_slab_cache",
1644             sizeof(rtl_cache_slab_type), /* objsize  */
1645             0,                           /* objalign */
1646             rtl_cache_slab_constructor,
1647             rtl_cache_slab_destructor,
1648             0,                           /* reclaim */
1649             0,                           /* userarg: none */
1650             gp_default_arena,            /* source */
1651             0                            /* flags: none */
1652         );
1653         OSL_ASSERT(gp_cache_slab_cache != 0);
1654     }
1655     {
1656         /* cache: bufctl cache */
1657         static rtl_cache_type g_cache_bufctl_cache;
1658 
1659         OSL_ASSERT(gp_cache_bufctl_cache == 0);
1660         VALGRIND_CREATE_MEMPOOL(&g_cache_bufctl_cache, 0, 0);
1661         (void) rtl_cache_constructor (&g_cache_bufctl_cache);
1662 
1663         gp_cache_bufctl_cache = rtl_cache_activate (
1664             &g_cache_bufctl_cache,
1665             "rtl_cache_bufctl_cache",
1666             sizeof(rtl_cache_bufctl_type), /* objsize */
1667             0,                             /* objalign  */
1668             0,                /* constructor */
1669             0,                /* destructor */
1670             0,                /* reclaim */
1671             0,                /* userarg */
1672             gp_default_arena, /* source */
1673             0                 /* flags: none */
1674         );
1675         OSL_ASSERT(gp_cache_bufctl_cache != 0);
1676     }
1677 
1678     rtl_cache_wsupdate_init();
1679 }
1680 
1681 static int
1682 rtl_cache_init (void)
1683 {
1684     static sal_once_type g_once = SAL_ONCE_INIT;
1685     SAL_ONCE(&g_once, rtl_cache_once_init);
1686     return (gp_cache_arena != 0);
1687 }
1688 
1689 /* ================================================================= */
1690 
1691 /*
1692   Issue http://udk.openoffice.org/issues/show_bug.cgi?id=92388
1693 
1694   Mac OS X does not seem to support "__cxa__atexit", thus leading
1695   to the situation that "__attribute__((destructor))__" functions
1696   (in particular "rtl_{memory|cache|arena}_fini") become called
1697   _before_ global C++ object d'tors.
1698 
1699   Delegated the call to "rtl_cache_fini()" into a dummy C++ object,
1700   see alloc_fini.cxx .
1701 */
1702 #if defined(__GNUC__) && !defined(MACOSX)
1703 static void rtl_cache_fini (void) __attribute__((destructor));
1704 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
1705 #pragma fini(rtl_cache_fini)
1706 static void rtl_cache_fini (void);
1707 #endif /* __GNUC__ || __SUNPRO_C */
1708 
1709 void
1710 rtl_cache_fini (void)
1711 {
1712     if (gp_cache_arena != 0)
1713     {
1714         rtl_cache_type * cache, * head;
1715 
1716         rtl_cache_wsupdate_fini();
1717 
1718         if (gp_cache_bufctl_cache != 0)
1719         {
1720             cache = gp_cache_bufctl_cache, gp_cache_bufctl_cache = 0;
1721             rtl_cache_deactivate (cache);
1722             rtl_cache_destructor (cache);
1723             VALGRIND_DESTROY_MEMPOOL(cache);
1724         }
1725         if (gp_cache_slab_cache != 0)
1726         {
1727             cache = gp_cache_slab_cache, gp_cache_slab_cache = 0;
1728             rtl_cache_deactivate (cache);
1729             rtl_cache_destructor (cache);
1730             VALGRIND_DESTROY_MEMPOOL(cache);
1731         }
1732         if (gp_cache_magazine_cache != 0)
1733         {
1734             cache = gp_cache_magazine_cache, gp_cache_magazine_cache = 0;
1735             rtl_cache_deactivate (cache);
1736             rtl_cache_destructor (cache);
1737             VALGRIND_DESTROY_MEMPOOL(cache);
1738         }
1739         if (gp_cache_arena != 0)
1740         {
1741             rtl_arena_destroy (gp_cache_arena);
1742             gp_cache_arena = 0;
1743         }
1744 
1745         RTL_MEMORY_LOCK_ACQUIRE(&(g_cache_list.m_lock));
1746         head = &(g_cache_list.m_cache_head);
1747         for (cache = head->m_cache_next; cache != head; cache = cache->m_cache_next)
1748         {
1749             OSL_TRACE(
1750                 "rtl_cache_fini(\"%s\") "
1751                 "[slab]: allocs: %"PRIu64", frees: %"PRIu64"; total: %lu, used: %lu; "
1752                 "[cpu]: allocs: %"PRIu64", frees: %"PRIu64"; "
1753                 "[total]: allocs: %"PRIu64", frees: %"PRIu64"",
1754                 cache->m_name,
1755                 cache->m_slab_stats.m_alloc, cache->m_slab_stats.m_free,
1756                 cache->m_slab_stats.m_mem_total, cache->m_slab_stats.m_mem_alloc,
1757                 cache->m_cpu_stats.m_alloc, cache->m_cpu_stats.m_free,
1758                 cache->m_slab_stats.m_alloc + cache->m_cpu_stats.m_alloc,
1759                 cache->m_slab_stats.m_free + cache->m_cpu_stats.m_free
1760             );
1761         }
1762         RTL_MEMORY_LOCK_RELEASE(&(g_cache_list.m_lock));
1763     }
1764 }
1765 
1766 /* ================================================================= */
1767