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