merge the formfield patch from ooo-build
[ooovba.git] / sal / rtl / source / alloc_arena.c
blob926222a0d83bf48b4ccb5cd5c9632f636adcf002
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: alloc_arena.c,v $
10 * $Revision: 1.7 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 #define _BSD_SOURCE /* sys/mman.h: MAP_ANON */
32 #include "alloc_arena.h"
34 #ifndef INCLUDED_RTL_ARENA_IMPL_H
35 #include "alloc_impl.h"
36 #endif
37 #include "internal/once.h"
38 #include "sal/macros.h"
39 #include "osl/diagnose.h"
41 #ifndef INCLUDED_STRING_H
42 #include <string.h>
43 #endif
45 #ifndef INCLUDED_STDIO_H
46 #include <stdio.h>
47 #endif
49 #include "sal/types.h"
51 #ifdef OS2
52 #undef OSL_TRACE
53 #define OSL_TRACE 1 ? ((void)0) : _OSL_GLOBAL osl_trace
54 #define INCL_DOS
55 #include <os2.h>
56 #endif
58 /* ================================================================= *
60 * arena internals.
62 * ================================================================= */
64 /** g_arena_list
65 * @internal
67 struct rtl_arena_list_st
69 rtl_memory_lock_type m_lock;
70 rtl_arena_type m_arena_head;
73 static struct rtl_arena_list_st g_arena_list;
76 /** gp_arena_arena
77 * provided for arena_type allocations, and hash_table resizing.
79 * @internal
81 static rtl_arena_type * gp_arena_arena = 0;
84 /** gp_machdep_arena
86 * Low level virtual memory (pseudo) arena
87 * (platform dependent implementation)
89 * @internal
91 static rtl_arena_type * gp_machdep_arena = 0;
94 static void *
95 SAL_CALL rtl_machdep_alloc (
96 rtl_arena_type * pArena,
97 sal_Size * pSize
100 static void
101 SAL_CALL rtl_machdep_free (
102 rtl_arena_type * pArena,
103 void * pAddr,
104 sal_Size nSize
107 static sal_Size
108 rtl_machdep_pagesize (void);
111 /** gp_default_arena
113 rtl_arena_type * gp_default_arena = 0;
116 /** rtl_arena_init()
117 * @internal
119 static int
120 rtl_arena_init (void);
123 /* ================================================================= */
125 /** rtl_arena_segment_constructor()
127 static int
128 rtl_arena_segment_constructor (void * obj)
130 rtl_arena_segment_type * segment = (rtl_arena_segment_type*)(obj);
132 QUEUE_START_NAMED(segment, s);
133 QUEUE_START_NAMED(segment, f);
135 return (1);
139 /** rtl_arena_segment_destructor()
141 static void
142 rtl_arena_segment_destructor (void * obj)
144 #if OSL_DEBUG_LEVEL == 0
145 (void) obj; /* unused */
146 #else /* OSL_DEBUG_LEVEL */
147 rtl_arena_segment_type * segment = (rtl_arena_segment_type*)(obj);
149 OSL_ASSERT(QUEUE_STARTED_NAMED(segment, s));
150 OSL_ASSERT(QUEUE_STARTED_NAMED(segment, f));
151 #endif /* OSL_DEBUG_LEVEL */
154 /* ================================================================= */
156 /** rtl_arena_segment_populate()
158 * @precond arena->m_lock acquired.
160 static int
161 rtl_arena_segment_populate (
162 rtl_arena_type * arena
165 rtl_arena_segment_type *span;
166 sal_Size size = rtl_machdep_pagesize();
168 span = rtl_machdep_alloc(gp_machdep_arena, &size);
169 if (span != 0)
171 rtl_arena_segment_type *first, *last, *head;
172 sal_Size count = size / sizeof(rtl_arena_segment_type);
174 /* insert onto reserve span list */
175 QUEUE_INSERT_TAIL_NAMED(&(arena->m_segment_reserve_span_head), span, s);
176 QUEUE_START_NAMED(span, f);
177 span->m_addr = (sal_uIntPtr)(span);
178 span->m_size = size;
179 span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN;
181 /* insert onto reserve list */
182 head = &(arena->m_segment_reserve_head);
183 for (first = span + 1, last = span + count; first < last; ++first)
185 QUEUE_INSERT_TAIL_NAMED(head, first, s);
186 QUEUE_START_NAMED(first, f);
187 first->m_addr = 0;
188 first->m_size = 0;
189 first->m_type = 0;
192 return (span != 0);
196 /** rtl_arena_segment_get()
198 * @precond arena->m_lock acquired.
199 * @precond (*ppSegment == 0)
201 static RTL_MEMORY_INLINE void
202 rtl_arena_segment_get (
203 rtl_arena_type * arena,
204 rtl_arena_segment_type ** ppSegment
207 rtl_arena_segment_type * head;
209 OSL_ASSERT(*ppSegment == 0);
211 head = &(arena->m_segment_reserve_head);
212 if ((head->m_snext != head) || rtl_arena_segment_populate (arena))
214 (*ppSegment) = head->m_snext;
215 QUEUE_REMOVE_NAMED((*ppSegment), s);
219 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
220 #pragma inline(rtl_arena_segment_get)
221 #endif
224 /** rtl_arena_segment_put()
226 * @precond arena->m_lock acquired.
227 * @postcond (*ppSegment == 0)
229 static RTL_MEMORY_INLINE void
230 rtl_arena_segment_put (
231 rtl_arena_type * arena,
232 rtl_arena_segment_type ** ppSegment
235 rtl_arena_segment_type * head;
237 OSL_ASSERT(QUEUE_STARTED_NAMED((*ppSegment), s));
238 OSL_ASSERT(QUEUE_STARTED_NAMED((*ppSegment), f));
240 (*ppSegment)->m_addr = 0;
241 (*ppSegment)->m_size = 0;
243 OSL_ASSERT((*ppSegment)->m_type != RTL_ARENA_SEGMENT_TYPE_HEAD);
244 (*ppSegment)->m_type = 0;
246 /* keep as reserve */
247 head = &(arena->m_segment_reserve_head);
248 QUEUE_INSERT_HEAD_NAMED(head, (*ppSegment), s);
250 /* clear */
251 (*ppSegment) = 0;
254 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
255 #pragma inline(rtl_arena_segment_put)
256 #endif
258 /* ================================================================= */
260 /** rtl_arena_freelist_insert()
262 * @precond arena->m_lock acquired.
264 static RTL_MEMORY_INLINE void
265 rtl_arena_freelist_insert (
266 rtl_arena_type * arena,
267 rtl_arena_segment_type * segment
270 rtl_arena_segment_type * head;
272 head = &(arena->m_freelist_head[highbit(segment->m_size) - 1]);
273 QUEUE_INSERT_TAIL_NAMED(head, segment, f);
275 arena->m_freelist_bitmap |= head->m_size;
278 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
279 #pragma inline(rtl_arena_freelist_insert)
280 #endif /* __SUNPRO_C */
283 /** rtl_arena_freelist_remove()
285 * @precond arena->m_lock acquired.
287 static RTL_MEMORY_INLINE void
288 rtl_arena_freelist_remove (
289 rtl_arena_type * arena,
290 rtl_arena_segment_type * segment
293 if ((segment->m_fnext->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD) &&
294 (segment->m_fprev->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD) )
296 rtl_arena_segment_type * head;
298 head = segment->m_fprev;
299 OSL_ASSERT(arena->m_freelist_bitmap & head->m_size);
300 arena->m_freelist_bitmap ^= head->m_size;
302 QUEUE_REMOVE_NAMED(segment, f);
305 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
306 #pragma inline(rtl_arena_freelist_remove)
307 #endif /* __SUNPRO_C */
310 /* ================================================================= */
312 /** RTL_ARENA_HASH_INDEX()
314 #define RTL_ARENA_HASH_INDEX_IMPL(a, s, q, m) \
315 ((((a) + ((a) >> (s)) + ((a) >> ((s) << 1))) >> (q)) & (m))
317 #define RTL_ARENA_HASH_INDEX(arena, addr) \
318 RTL_ARENA_HASH_INDEX_IMPL((addr), (arena)->m_hash_shift, (arena)->m_quantum_shift, ((arena)->m_hash_size - 1))
320 /** rtl_arena_hash_rescale()
322 * @precond arena->m_lock released.
324 static void
325 rtl_arena_hash_rescale (
326 rtl_arena_type * arena,
327 sal_Size new_size
330 rtl_arena_segment_type ** new_table;
331 sal_Size new_bytes;
333 new_bytes = new_size * sizeof(rtl_arena_segment_type*);
334 new_table = (rtl_arena_segment_type **)rtl_arena_alloc (gp_arena_arena, &new_bytes);
336 if (new_table != 0)
338 rtl_arena_segment_type ** old_table;
339 sal_Size old_size, i;
341 memset (new_table, 0, new_bytes);
343 RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
345 old_table = arena->m_hash_table;
346 old_size = arena->m_hash_size;
348 OSL_TRACE(
349 "rtl_arena_hash_rescale(\"%s\"): "
350 "nseg: %"PRIu64" (ave: %"PRIu64"), frees: %"PRIu64" "
351 "[old_size: %lu, new_size: %lu]",
352 arena->m_name,
353 arena->m_stats.m_alloc - arena->m_stats.m_free,
354 (arena->m_stats.m_alloc - arena->m_stats.m_free) >> arena->m_hash_shift,
355 arena->m_stats.m_free,
356 old_size, new_size
359 #if 0 /* DBG */
360 int i;
361 for (i = 0; i < arena->m_hash_size; i++)
363 sal_Size k = 0; rtl_arena_segment_type ** segpp = &(arena->m_hash_table[i]);
364 while (*segpp)
366 k += 1;
367 segpp = &((*segpp)->m_fnext);
369 fprintf(stdout, "%d, ", k);
371 fprintf(stdout, "\n");
372 #endif /* DBG */
374 arena->m_hash_table = new_table;
375 arena->m_hash_size = new_size;
376 arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
378 for (i = 0; i < old_size; i++)
380 rtl_arena_segment_type * curr = old_table[i];
381 while (curr != 0)
383 rtl_arena_segment_type * next = curr->m_fnext;
384 rtl_arena_segment_type ** head;
386 head = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, curr->m_addr)]);
387 curr->m_fnext = (*head);
388 (*head) = curr;
390 curr = next;
392 old_table[i] = 0;
395 RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
397 if (old_table != arena->m_hash_table_0)
399 sal_Size old_bytes = old_size * sizeof(rtl_arena_segment_type*);
400 rtl_arena_free (gp_arena_arena, old_table, old_bytes);
406 /** rtl_arena_hash_insert()
407 * ...and update stats.
409 static RTL_MEMORY_INLINE void
410 rtl_arena_hash_insert (
411 rtl_arena_type * arena,
412 rtl_arena_segment_type * segment
415 rtl_arena_segment_type ** ppSegment;
417 ppSegment = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, segment->m_addr)]);
419 segment->m_fnext = (*ppSegment);
420 (*ppSegment) = segment;
422 arena->m_stats.m_alloc += 1;
423 arena->m_stats.m_mem_alloc += segment->m_size;
426 #if defined(__SUNPRO_C) || defined(__SUNPRO_CC)
427 #pragma inline(rtl_arena_hash_insert)
428 #endif /* __SUNPRO_C */
431 /** rtl_arena_hash_remove()
432 * ...and update stats.
434 static rtl_arena_segment_type *
435 rtl_arena_hash_remove (
436 rtl_arena_type * arena,
437 sal_uIntPtr addr,
438 sal_Size size
441 rtl_arena_segment_type *segment, **segpp;
442 sal_Size lookups = 0;
444 #if OSL_DEBUG_LEVEL == 0
445 (void) size; /* unused */
446 #endif /* OSL_DEBUG_LEVEL */
448 segpp = &(arena->m_hash_table[RTL_ARENA_HASH_INDEX(arena, addr)]);
449 while ((segment = *segpp) != 0)
451 if (segment->m_addr == addr)
453 *segpp = segment->m_fnext, segment->m_fnext = segment->m_fprev = segment;
454 break;
457 /* update lookup miss stats */
458 lookups += 1;
459 segpp = &(segment->m_fnext);
462 OSL_POSTCOND(segment != 0, "rtl_arena_hash_remove(): bad free.");
463 if (segment != 0)
465 OSL_POSTCOND(segment->m_size == size, "rtl_arena_hash_remove(): wrong size.");
467 arena->m_stats.m_free += 1;
468 arena->m_stats.m_mem_alloc -= segment->m_size;
470 if (lookups > 1)
472 sal_Size nseg = (sal_Size)(arena->m_stats.m_alloc - arena->m_stats.m_free);
473 if (nseg > 4 * arena->m_hash_size)
475 if (!(arena->m_flags & RTL_ARENA_FLAG_RESCALE))
477 sal_Size ave = nseg >> arena->m_hash_shift;
478 sal_Size new_size = arena->m_hash_size << (highbit(ave) - 1);
480 arena->m_flags |= RTL_ARENA_FLAG_RESCALE;
481 RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
482 rtl_arena_hash_rescale (arena, new_size);
483 RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
484 arena->m_flags &= ~RTL_ARENA_FLAG_RESCALE;
490 return (segment);
493 /* ================================================================= */
495 /** rtl_arena_segment_alloc()
496 * allocate (and remove) segment from freelist
498 * @precond arena->m_lock acquired
499 * @precond (*ppSegment == 0)
501 static int
502 rtl_arena_segment_alloc (
503 rtl_arena_type * arena,
504 sal_Size size,
505 rtl_arena_segment_type ** ppSegment
508 int index = 0;
510 OSL_ASSERT(*ppSegment == 0);
511 if (!RTL_MEMORY_ISP2(size))
513 int msb = highbit(size);
514 if (RTL_ARENA_FREELIST_SIZE == SAL_INT_CAST(size_t, msb))
516 /* highest possible freelist: fall back to first fit */
517 rtl_arena_segment_type *head, *segment;
519 head = &(arena->m_freelist_head[msb - 1]);
520 for (segment = head->m_fnext; segment != head; segment = segment->m_fnext)
522 if (segment->m_size >= size)
524 /* allocate first fit segment */
525 (*ppSegment) = segment;
526 break;
529 goto dequeue_and_leave;
532 /* roundup to next power of 2 */
533 size = (1UL << msb);
536 index = lowbit(RTL_MEMORY_P2ALIGN(arena->m_freelist_bitmap, size));
537 if (index > 0)
539 /* instant fit: allocate first free segment */
540 rtl_arena_segment_type *head;
542 head = &(arena->m_freelist_head[index - 1]);
543 (*ppSegment) = head->m_fnext;
544 OSL_ASSERT((*ppSegment) != head);
547 dequeue_and_leave:
548 if (*ppSegment != 0)
550 /* remove from freelist */
551 rtl_arena_freelist_remove (arena, (*ppSegment));
553 return (*ppSegment != 0);
557 /** rtl_arena_segment_create()
558 * import new (span) segment from source arena
560 * @precond arena->m_lock acquired
561 * @precond (*ppSegment == 0)
563 static int
564 rtl_arena_segment_create (
565 rtl_arena_type * arena,
566 sal_Size size,
567 rtl_arena_segment_type ** ppSegment
570 OSL_ASSERT((*ppSegment) == 0);
571 if (arena->m_source_alloc != 0)
573 rtl_arena_segment_get (arena, ppSegment);
574 if (*ppSegment != 0)
576 rtl_arena_segment_type * span = 0;
577 rtl_arena_segment_get (arena, &span);
578 if (span != 0)
580 /* import new span from source arena */
581 RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
583 span->m_size = size;
584 span->m_addr = (sal_uIntPtr)(arena->m_source_alloc)(
585 arena->m_source_arena, &(span->m_size));
587 RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
588 if (span->m_addr != 0)
590 /* insert onto segment list, update stats */
591 span->m_type = RTL_ARENA_SEGMENT_TYPE_SPAN;
592 QUEUE_INSERT_HEAD_NAMED(&(arena->m_segment_head), span, s);
593 arena->m_stats.m_mem_total += span->m_size;
595 (*ppSegment)->m_addr = span->m_addr;
596 (*ppSegment)->m_size = span->m_size;
597 (*ppSegment)->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
598 QUEUE_INSERT_HEAD_NAMED(span, (*ppSegment), s);
600 /* report success */
601 return (1);
603 rtl_arena_segment_put (arena, &span);
605 rtl_arena_segment_put (arena, ppSegment);
608 return (0);
612 /** rtl_arena_segment_coalesce()
613 * mark as free and join with adjacent free segment(s)
615 * @precond arena->m_lock acquired
616 * @precond segment marked 'used'
618 static void
619 rtl_arena_segment_coalesce (
620 rtl_arena_type * arena,
621 rtl_arena_segment_type * segment
624 rtl_arena_segment_type *next, *prev;
626 /* mark segment free */
627 OSL_ASSERT(segment->m_type == RTL_ARENA_SEGMENT_TYPE_USED);
628 segment->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
630 /* try to merge w/ next segment */
631 next = segment->m_snext;
632 if (next->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
634 OSL_ASSERT(segment->m_addr + segment->m_size == next->m_addr);
635 segment->m_size += next->m_size;
637 /* remove from freelist */
638 rtl_arena_freelist_remove (arena, next);
640 /* remove from segment list */
641 QUEUE_REMOVE_NAMED(next, s);
643 /* release segment descriptor */
644 rtl_arena_segment_put (arena, &next);
647 /* try to merge w/ prev segment */
648 prev = segment->m_sprev;
649 if (prev->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
651 OSL_ASSERT(prev->m_addr + prev->m_size == segment->m_addr);
652 segment->m_addr = prev->m_addr;
653 segment->m_size += prev->m_size;
655 /* remove from freelist */
656 rtl_arena_freelist_remove (arena, prev);
658 /* remove from segment list */
659 QUEUE_REMOVE_NAMED(prev, s);
661 /* release segment descriptor */
662 rtl_arena_segment_put (arena, &prev);
666 /* ================================================================= */
668 /** rtl_arena_constructor()
670 static void
671 rtl_arena_constructor (void * obj)
673 rtl_arena_type * arena = (rtl_arena_type*)(obj);
674 rtl_arena_segment_type * head;
675 size_t i;
677 memset (arena, 0, sizeof(rtl_arena_type));
679 QUEUE_START_NAMED(arena, arena_);
681 (void) RTL_MEMORY_LOCK_INIT(&(arena->m_lock));
683 head = &(arena->m_segment_reserve_span_head);
684 rtl_arena_segment_constructor (head);
685 head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
687 head = &(arena->m_segment_reserve_head);
688 rtl_arena_segment_constructor (head);
689 head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
691 head = &(arena->m_segment_head);
692 rtl_arena_segment_constructor (head);
693 head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
695 for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++)
697 head = &(arena->m_freelist_head[i]);
698 rtl_arena_segment_constructor (head);
700 head->m_size = (1UL << i);
701 head->m_type = RTL_ARENA_SEGMENT_TYPE_HEAD;
704 arena->m_hash_table = arena->m_hash_table_0;
705 arena->m_hash_size = RTL_ARENA_HASH_SIZE;
706 arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
710 /** rtl_arena_destructor()
712 static void
713 rtl_arena_destructor (void * obj)
715 rtl_arena_type * arena = (rtl_arena_type*)(obj);
716 rtl_arena_segment_type * head;
717 size_t i;
719 OSL_ASSERT(QUEUE_STARTED_NAMED(arena, arena_));
721 RTL_MEMORY_LOCK_DESTROY(&(arena->m_lock));
723 head = &(arena->m_segment_reserve_span_head);
724 OSL_ASSERT(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
725 rtl_arena_segment_destructor (head);
727 head = &(arena->m_segment_reserve_head);
728 OSL_ASSERT(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
729 rtl_arena_segment_destructor (head);
731 head = &(arena->m_segment_head);
732 OSL_ASSERT(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
733 rtl_arena_segment_destructor (head);
735 for (i = 0; i < RTL_ARENA_FREELIST_SIZE; i++)
737 head = &(arena->m_freelist_head[i]);
739 OSL_ASSERT(head->m_size == (1UL << i));
740 OSL_ASSERT(head->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD);
742 rtl_arena_segment_destructor (head);
745 OSL_ASSERT(arena->m_hash_table == arena->m_hash_table_0);
746 OSL_ASSERT(arena->m_hash_size == RTL_ARENA_HASH_SIZE);
747 OSL_ASSERT(
748 arena->m_hash_shift ==
749 SAL_INT_CAST(unsigned, highbit(arena->m_hash_size) - 1));
752 /* ================================================================= */
754 /** rtl_arena_activate()
756 static rtl_arena_type *
757 rtl_arena_activate (
758 rtl_arena_type * arena,
759 const char * name,
760 sal_Size quantum,
761 sal_Size quantum_cache_max,
762 rtl_arena_type * source_arena,
763 void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *),
764 void (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size)
767 OSL_ASSERT(arena != 0);
768 if (arena != 0)
770 (void) snprintf (arena->m_name, sizeof(arena->m_name), "%s", name);
772 if (!RTL_MEMORY_ISP2(quantum))
774 /* roundup to next power of 2 */
775 quantum = (1UL << highbit(quantum));
777 quantum_cache_max = RTL_MEMORY_P2ROUNDUP(quantum_cache_max, quantum);
779 arena->m_quantum = quantum;
780 arena->m_quantum_shift = highbit(arena->m_quantum) - 1;
781 arena->m_qcache_max = quantum_cache_max;
783 arena->m_source_arena = source_arena;
784 arena->m_source_alloc = source_alloc;
785 arena->m_source_free = source_free;
787 if (arena->m_qcache_max > 0)
789 char name[RTL_ARENA_NAME_LENGTH + 1];
790 int i, n = (arena->m_qcache_max >> arena->m_quantum_shift);
792 sal_Size size = n * sizeof(rtl_cache_type*);
793 arena->m_qcache_ptr = (rtl_cache_type**)rtl_arena_alloc (gp_arena_arena, &size);
794 if (!(arena->m_qcache_ptr))
796 /* out of memory */
797 return (0);
799 for (i = 1; i <= n; i++)
801 size = i * arena->m_quantum;
802 (void) snprintf (name, sizeof(name), "%s_%lu", arena->m_name, size);
803 arena->m_qcache_ptr[i - 1] = rtl_cache_create(name, size, 0, NULL, NULL, NULL, NULL, arena, RTL_CACHE_FLAG_QUANTUMCACHE);
807 /* insert into arena list */
808 RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
809 QUEUE_INSERT_TAIL_NAMED(&(g_arena_list.m_arena_head), arena, arena_);
810 RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
812 return (arena);
815 /** rtl_arena_deactivate()
817 static void
818 rtl_arena_deactivate (
819 rtl_arena_type * arena
822 rtl_arena_segment_type * head, * segment;
824 /* remove from arena list */
825 RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
826 QUEUE_REMOVE_NAMED(arena, arena_);
827 RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
829 /* cleanup quantum cache(s) */
830 if ((arena->m_qcache_max > 0) && (arena->m_qcache_ptr != 0))
832 int i, n = (arena->m_qcache_max >> arena->m_quantum_shift);
833 for (i = 1; i <= n; i++)
835 if (arena->m_qcache_ptr[i - 1] != 0)
837 rtl_cache_destroy (arena->m_qcache_ptr[i - 1]);
838 arena->m_qcache_ptr[i - 1] = 0;
841 rtl_arena_free (
842 gp_arena_arena,
843 arena->m_qcache_ptr,
844 n * sizeof(rtl_cache_type*));
846 arena->m_qcache_ptr = 0;
849 /* check for leaked segments */
850 OSL_TRACE(
851 "rtl_arena_deactivate(\"%s\"): "
852 "allocs: %"PRIu64", frees: %"PRIu64"; total: %lu, used: %lu",
853 arena->m_name,
854 arena->m_stats.m_alloc, arena->m_stats.m_free,
855 arena->m_stats.m_mem_total, arena->m_stats.m_mem_alloc
857 if (arena->m_stats.m_alloc > arena->m_stats.m_free)
859 sal_Size i, n;
861 OSL_TRACE(
862 "rtl_arena_deactivate(\"%s\"): "
863 "cleaning up %"PRIu64" leaked segment(s) [%lu bytes]",
864 arena->m_name,
865 arena->m_stats.m_alloc - arena->m_stats.m_free,
866 arena->m_stats.m_mem_alloc
869 /* cleanup still used segment(s) */
870 for (i = 0, n = arena->m_hash_size; i < n; i++)
872 while ((segment = arena->m_hash_table[i]) != 0)
874 /* pop from hash table */
875 arena->m_hash_table[i] = segment->m_fnext, segment->m_fnext = segment->m_fprev = segment;
877 /* coalesce w/ adjacent free segment(s) */
878 rtl_arena_segment_coalesce (arena, segment);
880 /* insert onto freelist */
881 rtl_arena_freelist_insert (arena, segment);
886 /* cleanup hash table */
887 if (arena->m_hash_table != arena->m_hash_table_0)
889 rtl_arena_free (
890 gp_arena_arena,
891 arena->m_hash_table,
892 arena->m_hash_size * sizeof(rtl_arena_segment_type*));
894 arena->m_hash_table = arena->m_hash_table_0;
895 arena->m_hash_size = RTL_ARENA_HASH_SIZE;
896 arena->m_hash_shift = highbit(arena->m_hash_size) - 1;
899 /* cleanup segment list */
900 head = &(arena->m_segment_head);
901 for (segment = head->m_snext; segment != head; segment = head->m_snext)
903 if (segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE)
905 /* remove from freelist */
906 rtl_arena_freelist_remove (arena, segment);
908 else
910 /* can have only free and span segments here */
911 OSL_ASSERT(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN);
914 /* remove from segment list */
915 QUEUE_REMOVE_NAMED(segment, s);
917 /* release segment descriptor */
918 rtl_arena_segment_put (arena, &segment);
921 /* cleanup segment reserve list */
922 head = &(arena->m_segment_reserve_head);
923 for (segment = head->m_snext; segment != head; segment = head->m_snext)
925 /* remove from segment list */
926 QUEUE_REMOVE_NAMED(segment, s);
929 /* cleanup segment reserve span(s) */
930 head = &(arena->m_segment_reserve_span_head);
931 for (segment = head->m_snext; segment != head; segment = head->m_snext)
933 /* can have only span segments here */
934 OSL_ASSERT(segment->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN);
936 /* remove from segment list */
937 QUEUE_REMOVE_NAMED(segment, s);
939 /* return span to g_machdep_arena */
940 rtl_machdep_free (gp_machdep_arena, (void*)(segment->m_addr), segment->m_size);
944 /* ================================================================= *
946 * arena implementation.
948 * ================================================================= */
950 /** rtl_arena_create()
952 rtl_arena_type *
953 SAL_CALL rtl_arena_create (
954 const char * name,
955 sal_Size quantum,
956 sal_Size quantum_cache_max,
957 rtl_arena_type * source_arena,
958 void * (SAL_CALL * source_alloc)(rtl_arena_type *, sal_Size *),
959 void (SAL_CALL * source_free) (rtl_arena_type *, void *, sal_Size),
960 int flags
961 ) SAL_THROW_EXTERN_C()
963 rtl_arena_type * result = 0;
964 sal_Size size = sizeof(rtl_arena_type);
966 (void) flags; /* unused */
968 try_alloc:
969 result = (rtl_arena_type*)rtl_arena_alloc (gp_arena_arena, &size);
970 if (result != 0)
972 rtl_arena_type * arena = result;
973 rtl_arena_constructor (arena);
975 if (!source_arena)
977 OSL_ASSERT(gp_default_arena != 0);
978 source_arena = gp_default_arena;
981 result = rtl_arena_activate (
982 arena,
983 name,
984 quantum,
985 quantum_cache_max,
986 source_arena,
987 source_alloc,
988 source_free
991 if (result == 0)
993 rtl_arena_deactivate (arena);
994 rtl_arena_destructor (arena);
995 rtl_arena_free (gp_arena_arena, arena, size);
998 else if (gp_arena_arena == 0)
1000 if (rtl_arena_init())
1002 /* try again */
1003 goto try_alloc;
1006 return (result);
1009 /** rtl_arena_destroy()
1011 void
1012 SAL_CALL rtl_arena_destroy (
1013 rtl_arena_type * arena
1016 if (arena != 0)
1018 rtl_arena_deactivate (arena);
1019 rtl_arena_destructor (arena);
1020 rtl_arena_free (gp_arena_arena, arena, sizeof(rtl_arena_type));
1024 /** rtl_arena_alloc()
1026 void *
1027 SAL_CALL rtl_arena_alloc (
1028 rtl_arena_type * arena,
1029 sal_Size * pSize
1030 ) SAL_THROW_EXTERN_C()
1032 void * addr = 0;
1034 if ((arena != 0) && (pSize != 0))
1036 sal_Size size = RTL_MEMORY_ALIGN((*pSize), arena->m_quantum);
1037 if (size > arena->m_qcache_max)
1039 /* allocate from segment list */
1040 rtl_arena_segment_type *segment = 0;
1042 RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
1043 if (rtl_arena_segment_alloc (arena, size, &segment) ||
1044 rtl_arena_segment_create(arena, size, &segment) )
1046 /* shrink to fit */
1047 sal_Size oversize;
1049 /* mark segment used */
1050 OSL_ASSERT(segment->m_type == RTL_ARENA_SEGMENT_TYPE_FREE);
1051 segment->m_type = RTL_ARENA_SEGMENT_TYPE_USED;
1053 /* resize */
1054 OSL_ASSERT(segment->m_size >= size);
1055 oversize = segment->m_size - size;
1056 if (oversize >= SAL_MAX(arena->m_quantum, arena->m_qcache_max))
1058 rtl_arena_segment_type * remainder = 0;
1059 rtl_arena_segment_get (arena, &remainder);
1060 if (remainder != 0)
1062 segment->m_size = size;
1064 remainder->m_addr = segment->m_addr + segment->m_size;
1065 remainder->m_size = oversize;
1066 remainder->m_type = RTL_ARENA_SEGMENT_TYPE_FREE;
1067 QUEUE_INSERT_HEAD_NAMED(segment, remainder, s);
1069 rtl_arena_freelist_insert (arena, remainder);
1073 rtl_arena_hash_insert (arena, segment);
1075 (*pSize) = segment->m_size;
1076 addr = (void*)(segment->m_addr);
1078 RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
1080 else if (size > 0)
1082 /* allocate from quantum cache(s) */
1083 int index = (size >> arena->m_quantum_shift) - 1;
1084 OSL_ASSERT (arena->m_qcache_ptr[index] != 0);
1086 addr = rtl_cache_alloc (arena->m_qcache_ptr[index]);
1087 if (addr != 0)
1088 (*pSize) = size;
1091 return (addr);
1094 /** rtl_arena_free()
1096 void
1097 SAL_CALL rtl_arena_free (
1098 rtl_arena_type * arena,
1099 void * addr,
1100 sal_Size size
1101 ) SAL_THROW_EXTERN_C()
1103 if (arena != 0)
1105 size = RTL_MEMORY_ALIGN(size, arena->m_quantum);
1106 if (size > arena->m_qcache_max)
1108 /* free to segment list */
1109 rtl_arena_segment_type * segment;
1111 RTL_MEMORY_LOCK_ACQUIRE(&(arena->m_lock));
1113 segment = rtl_arena_hash_remove (arena, (sal_uIntPtr)(addr), size);
1114 if (segment != 0)
1116 rtl_arena_segment_type *next, *prev;
1118 /* coalesce w/ adjacent free segment(s) */
1119 rtl_arena_segment_coalesce (arena, segment);
1121 /* determine (new) next and prev segment */
1122 next = segment->m_snext, prev = segment->m_sprev;
1124 /* entire span free when prev is a span, and next is either a span or a list head */
1125 if (((prev->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN)) &&
1126 ((next->m_type == RTL_ARENA_SEGMENT_TYPE_SPAN) ||
1127 (next->m_type == RTL_ARENA_SEGMENT_TYPE_HEAD)) )
1129 OSL_ASSERT((prev->m_addr == segment->m_addr) &&
1130 (prev->m_size == segment->m_size) );
1132 if (arena->m_source_free)
1134 addr = (void*)(prev->m_addr);
1135 size = prev->m_size;
1137 /* remove from segment list */
1138 QUEUE_REMOVE_NAMED(segment, s);
1140 /* release segment descriptor */
1141 rtl_arena_segment_put (arena, &segment);
1143 /* remove from segment list */
1144 QUEUE_REMOVE_NAMED(prev, s);
1146 /* release (span) segment descriptor */
1147 rtl_arena_segment_put (arena, &prev);
1149 /* update stats, return span to source arena */
1150 arena->m_stats.m_mem_total -= size;
1151 RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
1153 (arena->m_source_free)(arena->m_source_arena, addr, size);
1154 return;
1158 /* insert onto freelist */
1159 rtl_arena_freelist_insert (arena, segment);
1162 RTL_MEMORY_LOCK_RELEASE(&(arena->m_lock));
1164 else if (size > 0)
1166 /* free to quantum cache(s) */
1167 int index = (size >> arena->m_quantum_shift) - 1;
1168 OSL_ASSERT (arena->m_qcache_ptr[index] != 0);
1170 rtl_cache_free (arena->m_qcache_ptr[index], addr);
1175 /* ================================================================= *
1177 * machdep internals.
1179 * ================================================================= */
1181 #if defined(SAL_UNX)
1182 #include <sys/mman.h>
1183 #elif defined(SAL_W32) || defined(SAL_OS2)
1184 #define MAP_FAILED 0
1185 #endif /* SAL_UNX || SAL_W32 */
1187 /** rtl_machdep_alloc()
1189 static void *
1190 SAL_CALL rtl_machdep_alloc (
1191 rtl_arena_type * pArena,
1192 sal_Size * pSize
1195 void * addr;
1196 sal_Size size = (*pSize);
1198 OSL_PRECOND(pArena == gp_machdep_arena, "rtl_machdep_alloc(): invalid argument");
1200 #if defined(SOLARIS) && defined(SPARC)
1201 /* see @ mmap(2) man pages */
1202 size += (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */
1203 if (size > (4 << 20))
1204 size = RTL_MEMORY_P2ROUNDUP(size, (4 << 20));
1205 else if (size > (512 << 10))
1206 size = RTL_MEMORY_P2ROUNDUP(size, (512 << 10));
1207 else
1208 size = RTL_MEMORY_P2ROUNDUP(size, (64 << 10));
1209 size -= (pArena->m_quantum + pArena->m_quantum); /* "red-zone" pages */
1210 #else
1211 /* default allocation granularity */
1212 size = RTL_MEMORY_P2ROUNDUP(size, SAL_MAX(pArena->m_quantum, 64 << 10));
1213 #endif
1215 #if defined(SAL_UNX)
1216 addr = mmap (NULL, (size_t)(size), PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
1217 #elif defined(SAL_W32)
1218 addr = VirtualAlloc (NULL, (SIZE_T)(size), MEM_COMMIT, PAGE_READWRITE);
1219 #elif defined(SAL_OS2)
1221 APIRET rc;
1222 addr = 0;
1223 // Use DosAlloc* to get a 4KB page aligned address.
1224 rc = DosAllocMem( &addr, size, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY);
1225 if (rc) {
1226 fprintf( stderr, "sal3::DosAllocMem failed rc=%d\n", rc);
1227 addr = 0;
1230 #endif /* (SAL_UNX || SAL_W32 || SAL_OS2) */
1232 if (addr != MAP_FAILED)
1234 pArena->m_stats.m_alloc += 1;
1235 pArena->m_stats.m_mem_total += size;
1236 pArena->m_stats.m_mem_alloc += size;
1238 (*pSize) = size;
1239 return (addr);
1241 return (NULL);
1244 /** rtl_machdep_free()
1246 static void
1247 SAL_CALL rtl_machdep_free (
1248 rtl_arena_type * pArena,
1249 void * pAddr,
1250 sal_Size nSize
1253 OSL_PRECOND(pArena == gp_machdep_arena, "rtl_machdep_free(): invalid argument");
1255 pArena->m_stats.m_free += 1;
1256 pArena->m_stats.m_mem_total -= nSize;
1257 pArena->m_stats.m_mem_alloc -= nSize;
1259 #if defined(SAL_UNX)
1260 (void) munmap(pAddr, nSize);
1261 #elif defined(SAL_W32)
1262 (void) VirtualFree ((LPVOID)(pAddr), (SIZE_T)(0), MEM_RELEASE);
1263 #elif defined(SAL_OS2)
1264 (void) DosFreeMem( pAddr);
1265 #endif /* (SAL_UNX || SAL_W32) */
1268 /** rtl_machdep_pagesize()
1270 static sal_Size
1271 rtl_machdep_pagesize (void)
1273 #if defined(SAL_UNX)
1274 #if defined(FREEBSD) || defined(NETBSD)
1275 return ((sal_Size)getpagesize());
1276 #else /* POSIX */
1277 return ((sal_Size)sysconf(_SC_PAGESIZE));
1278 #endif /* xBSD || POSIX */
1279 #elif defined(SAL_W32)
1280 SYSTEM_INFO info;
1281 GetSystemInfo (&info);
1282 return ((sal_Size)(info.dwPageSize));
1283 #elif defined(SAL_OS2)
1284 ULONG ulPageSize;
1285 DosQuerySysInfo(QSV_PAGE_SIZE, QSV_PAGE_SIZE, &ulPageSize, sizeof(ULONG));
1286 return ((sal_Size)ulPageSize);
1287 #endif /* (SAL_UNX || SAL_W32) */
1290 /* ================================================================= *
1292 * arena initialization.
1294 * ================================================================= */
1296 static void
1297 rtl_arena_once_init (void)
1300 /* list of arenas */
1301 RTL_MEMORY_LOCK_INIT(&(g_arena_list.m_lock));
1302 rtl_arena_constructor (&(g_arena_list.m_arena_head));
1305 /* machdep (pseudo) arena */
1306 static rtl_arena_type g_machdep_arena;
1308 OSL_ASSERT(gp_machdep_arena == 0);
1309 rtl_arena_constructor (&g_machdep_arena);
1311 gp_machdep_arena = rtl_arena_activate (
1312 &g_machdep_arena,
1313 "rtl_machdep_arena",
1314 rtl_machdep_pagesize(),
1315 0, /* no quantum caching */
1316 0, 0, 0 /* no source */
1318 OSL_ASSERT(gp_machdep_arena != 0);
1321 /* default arena */
1322 static rtl_arena_type g_default_arena;
1324 OSL_ASSERT(gp_default_arena == 0);
1325 rtl_arena_constructor (&g_default_arena);
1327 gp_default_arena = rtl_arena_activate (
1328 &g_default_arena,
1329 "rtl_default_arena",
1330 rtl_machdep_pagesize(),
1331 0, /* no quantum caching */
1332 gp_machdep_arena, /* source */
1333 rtl_machdep_alloc,
1334 rtl_machdep_free
1336 OSL_ASSERT(gp_default_arena != 0);
1339 /* arena internal arena */
1340 static rtl_arena_type g_arena_arena;
1342 OSL_ASSERT(gp_arena_arena == 0);
1343 rtl_arena_constructor (&g_arena_arena);
1345 gp_arena_arena = rtl_arena_activate (
1346 &g_arena_arena,
1347 "rtl_arena_internal_arena",
1348 64, /* quantum */
1349 0, /* no quantum caching */
1350 gp_default_arena, /* source */
1351 rtl_arena_alloc,
1352 rtl_arena_free
1354 OSL_ASSERT(gp_arena_arena != 0);
1358 static int
1359 rtl_arena_init (void)
1361 static sal_once_type g_once = SAL_ONCE_INIT;
1362 SAL_ONCE(&g_once, rtl_arena_once_init);
1363 return (gp_arena_arena != 0);
1366 /* ================================================================= */
1368 #if defined(__GNUC__)
1369 static void rtl_arena_fini (void) __attribute__((destructor));
1370 #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC)
1371 #pragma fini(rtl_arena_fini)
1372 static void rtl_arena_fini (void);
1373 #endif /* __GNUC__ || __SUNPRO_C */
1375 void
1376 rtl_arena_fini (void)
1378 if (gp_arena_arena != 0)
1380 rtl_arena_type * arena, * head;
1382 RTL_MEMORY_LOCK_ACQUIRE(&(g_arena_list.m_lock));
1383 head = &(g_arena_list.m_arena_head);
1385 for (arena = head->m_arena_next; arena != head; arena = arena->m_arena_next)
1387 OSL_TRACE(
1388 "rtl_arena_fini(\"%s\"): "
1389 "allocs: %"PRIu64", frees: %"PRIu64"; total: %lu, used: %lu",
1390 arena->m_name,
1391 arena->m_stats.m_alloc, arena->m_stats.m_free,
1392 arena->m_stats.m_mem_total, arena->m_stats.m_mem_alloc
1395 RTL_MEMORY_LOCK_RELEASE(&(g_arena_list.m_lock));
1399 /* ================================================================= */