Commit | Line | Data |
---|---|---|
7f918cf1 CE |
1 | #define GC_SMALLEST_PAGESIZE 4096 |
2 | /* #define DEBUG_MREMAP 1 */ | |
3 | ||
4 | /* Used to allocate at the head and tail of an existing allocation */ | |
5 | static void *GC_extendHead (void *base, size_t length); | |
6 | static void *GC_extendTail (void *base, size_t length); | |
7 | ||
8 | void *GC_mremap (void *base, size_t oldLength, size_t newLength) { | |
9 | static void* cacheAddress = 0; | |
10 | static size_t cacheOldLength = 0; | |
11 | static size_t cacheNewLength = 0; | |
12 | static size_t cacheTailSize; | |
13 | static size_t cacheHeadSize; | |
14 | ||
15 | void* tail; | |
16 | void* head; | |
17 | void* alloc; | |
18 | size_t growth, bsLow, bsHigh, bsTry; | |
19 | ||
20 | #ifdef DEBUG_MREMAP | |
21 | fprintf(stderr, "remap(%08X, %d, %d)\n", (size_t)base, oldLength, newLength); | |
22 | fflush(stderr); | |
23 | #endif | |
24 | ||
25 | if (newLength == oldLength) | |
26 | return base; | |
27 | if (newLength < oldLength) { | |
28 | GC_release((char*)base + newLength, oldLength-newLength); | |
29 | return base; | |
30 | } | |
31 | ||
32 | growth = newLength-oldLength; | |
33 | ||
34 | if (cacheAddress == base && | |
35 | cacheOldLength == oldLength && | |
36 | cacheNewLength > newLength) /* cache only during backoff */ | |
37 | goto GC_mremap_cached; | |
38 | ||
39 | /* Start probing for the available tail length */ | |
40 | bsLow = 0; | |
41 | bsHigh = (growth+GC_SMALLEST_PAGESIZE-1)/GC_SMALLEST_PAGESIZE; | |
42 | /* Round bsHigh to a power of two -> allocation works with all page sizes */ | |
43 | for (bsTry = 1; bsTry <= bsHigh; bsTry += bsTry) { } | |
44 | bsHigh = bsTry; | |
45 | while (bsHigh - bsLow > 1) { | |
46 | bsTry = (bsHigh + bsLow)/2; | |
47 | tail = (char*)base + oldLength; | |
48 | alloc = GC_extendTail(tail, bsTry*GC_SMALLEST_PAGESIZE); | |
49 | ||
50 | if (tail == alloc) { | |
51 | GC_release(alloc, bsTry*GC_SMALLEST_PAGESIZE); | |
52 | bsLow = bsTry; | |
53 | } else { | |
54 | if (alloc != (void*)-1) | |
55 | GC_release(alloc, bsTry*GC_SMALLEST_PAGESIZE); | |
56 | bsHigh = bsTry; | |
57 | } | |
58 | } | |
59 | cacheTailSize = bsLow*GC_SMALLEST_PAGESIZE; | |
60 | ||
61 | /* Start probing for the available head length */ | |
62 | bsLow = 0; | |
63 | bsHigh = (growth+GC_SMALLEST_PAGESIZE-1)/GC_SMALLEST_PAGESIZE; | |
64 | /* Round bsHigh to a power of two -> allocation works with all page sizes */ | |
65 | for (bsTry = 1; bsTry <= bsHigh; bsTry += bsTry) { } | |
66 | bsHigh = bsTry; | |
67 | while (bsHigh - bsLow > 1) { | |
68 | bsTry = (bsHigh + bsLow)/2; | |
69 | head = (char*)base - bsTry*GC_SMALLEST_PAGESIZE; | |
70 | alloc = GC_extendHead(head, bsTry*GC_SMALLEST_PAGESIZE); | |
71 | ||
72 | if (head == alloc) { | |
73 | GC_release(alloc, bsTry*GC_SMALLEST_PAGESIZE); | |
74 | bsLow = bsTry; | |
75 | } else { | |
76 | if (alloc != (void*)-1) | |
77 | GC_release(alloc, bsTry*GC_SMALLEST_PAGESIZE); | |
78 | bsHigh = bsTry; | |
79 | } | |
80 | } | |
81 | cacheHeadSize = bsLow*GC_SMALLEST_PAGESIZE; | |
82 | ||
83 | #ifdef DEBUG_MREMAP | |
84 | fprintf(stderr, "Expansion detects: %10d/%10d/%10d = %10d\n", | |
85 | cacheHeadSize, oldLength, cacheTailSize, | |
86 | cacheHeadSize+ oldLength+ cacheTailSize); | |
87 | fflush(stderr); | |
88 | #endif | |
89 | ||
90 | cacheAddress = base; | |
91 | cacheOldLength = oldLength; | |
92 | ||
93 | GC_mremap_cached: | |
94 | cacheNewLength = newLength; | |
95 | ||
96 | /* Is there enough free space? */ | |
97 | if (cacheTailSize + cacheHeadSize < growth) { | |
98 | /* No, there's not. Try to move it instead. */ | |
99 | alloc = GC_mmapAnon(0, newLength); | |
100 | if (alloc != (void*)-1) { | |
101 | memcpy(alloc, base, oldLength); | |
102 | GC_release(base, oldLength); | |
103 | return alloc; | |
104 | } | |
105 | /* Failed even to move it */ | |
106 | return (void*)-1; | |
107 | } | |
108 | ||
109 | #ifdef DEBUG_MREMAP | |
110 | fprintf(stderr, "Expansion attempts %d bytes\n", newLength); | |
111 | fflush(stderr); | |
112 | #endif | |
113 | ||
114 | if (growth <= cacheTailSize) { | |
115 | tail = (char*)base + oldLength; | |
116 | alloc = GC_extendTail(tail, growth); | |
117 | if (alloc != tail) { | |
118 | /* This shouldn't happen; we tested for the memory! */ | |
119 | if (alloc != (void*)-1) | |
120 | GC_release(alloc, growth); | |
121 | return (void*)-1; | |
122 | } | |
123 | return base; | |
124 | } | |
125 | ||
126 | if (cacheTailSize > 0) { | |
127 | tail = (char*)base + oldLength; | |
128 | alloc = GC_extendTail(tail, cacheTailSize); | |
129 | if (alloc != tail) { | |
130 | /* This shouldn't happen; we tested for the memory! */ | |
131 | if (alloc != (void*)-1) | |
132 | GC_release(alloc, cacheTailSize); | |
133 | return (void*)-1; | |
134 | } | |
135 | } else { | |
136 | tail = 0; /* quell warning */ | |
137 | } | |
138 | ||
139 | head = (char*)base - (growth-cacheTailSize); | |
140 | alloc = GC_extendHead(head, growth-cacheTailSize); | |
141 | if (alloc != head) { | |
142 | /* This shouldn't happen; we tested for the memory! */ | |
143 | if (alloc != (void*)-1) | |
144 | GC_release(alloc, growth-cacheTailSize); | |
145 | if (cacheTailSize > 0) | |
146 | GC_release(tail, cacheTailSize); | |
147 | return (void*)-1; | |
148 | } | |
149 | ||
150 | memmove(head, base, oldLength); | |
151 | return head; | |
152 | } |