aboutsummaryrefslogtreecommitdiffstats
path: root/mps/code/arena.c
diff options
context:
space:
mode:
Diffstat (limited to 'mps/code/arena.c')
-rw-r--r--mps/code/arena.c649
1 files changed, 649 insertions, 0 deletions
diff --git a/mps/code/arena.c b/mps/code/arena.c
new file mode 100644
index 00000000000..c0bb7888524
--- /dev/null
+++ b/mps/code/arena.c
@@ -0,0 +1,649 @@
1/* impl.c.arena: ARENA ALLOCATION FEATURES
2 *
3 * $HopeName: MMsrc!arena.c(trunk.79) $
4 * Copyright (C) 2001 Harlequin Limited. All rights reserved.
5 *
6 * .sources: design.mps.arena is the main design document. */
7
8#include "tract.h"
9#include "poolmv.h"
10#include "mpm.h"
11
12SRCID(arena, "$HopeName: MMsrc!arena.c(trunk.79) $");
13
14
15/* ArenaControlPool -- get the control pool */
16
17#define ArenaControlPool(arena) MVPool(&(arena)->controlPoolStruct)
18
19
20/* ArenaTrivDescribe -- produce trivial description of an arena */
21
22static Res ArenaTrivDescribe(Arena arena, mps_lib_FILE *stream)
23{
24 if (!CHECKT(Arena, arena)) return ResFAIL;
25 if (stream == NULL) return ResFAIL;
26
27 return WriteF(stream,
28 " No class-specific description available.\n", NULL);
29}
30
31
32/* AbstractArenaClass -- The abstact arena class definition
33 *
34 * .null: Most abstract class methods are set to NULL. See
35 * design.mps.arena.class.abstract.null. */
36
37typedef ArenaClassStruct AbstractArenaClassStruct;
38
39DEFINE_CLASS(AbstractArenaClass, class)
40{
41 INHERIT_CLASS(&class->protocol, ProtocolClass);
42 class->name = "ABSARENA";
43 class->size = 0;
44 class->offset = 0;
45 class->init = NULL;
46 class->finish = NULL;
47 class->reserved = NULL;
48 class->spareCommitExceeded = ArenaNoSpareCommitExceeded;
49 class->extend = ArenaNoExtend;
50 class->alloc = NULL;
51 class->free = NULL;
52 class->chunkInit = NULL;
53 class->chunkFinish = NULL;
54 class->describe = ArenaTrivDescribe;
55 class->sig = ArenaClassSig;
56}
57
58
59/* ArenaClassCheck -- check the consistency of an arena class */
60
61Bool ArenaClassCheck(ArenaClass class)
62{
63 CHECKL(ProtocolClassCheck(&class->protocol));
64 CHECKL(class->name != NULL); /* Should be <=6 char C identifier */
65 CHECKL(class->size >= sizeof(ArenaStruct));
66 /* Offset of generic Pool within class-specific instance cannot be */
67 /* greater than the size of the class-specific portion of the */
68 /* instance. */
69 CHECKL(class->offset <= (size_t)(class->size - sizeof(ArenaStruct)));
70 CHECKL(FUNCHECK(class->init));
71 CHECKL(FUNCHECK(class->finish));
72 CHECKL(FUNCHECK(class->reserved));
73 CHECKL(FUNCHECK(class->spareCommitExceeded));
74 CHECKL(FUNCHECK(class->extend));
75 CHECKL(FUNCHECK(class->alloc));
76 CHECKL(FUNCHECK(class->free));
77 CHECKL(FUNCHECK(class->chunkInit));
78 CHECKL(FUNCHECK(class->chunkFinish));
79 CHECKL(FUNCHECK(class->describe));
80 CHECKS(ArenaClass, class);
81 return TRUE;
82}
83
84
85/* ArenaCheck -- check the arena */
86
87Bool ArenaCheck(Arena arena)
88{
89 CHECKS(Arena, arena);
90 CHECKD(Globals, ArenaGlobals(arena));
91 CHECKD(ArenaClass, arena->class);
92
93 CHECKL(BoolCheck(arena->poolReady));
94 if (arena->poolReady) { /* design.mps.arena.pool.ready */
95 CHECKD(MV, &arena->controlPoolStruct);
96 CHECKD(Reservoir, &arena->reservoirStruct);
97 }
98 /* Can't check that limit>=size because we may call ArenaCheck */
99 /* while the size is being adjusted. */
100
101 CHECKL(arena->committed <= arena->commitLimit);
102 CHECKL(arena->spareCommitted <= arena->committed);
103 CHECKL(arena->spareCommitted <= arena->spareCommitLimit);
104
105 CHECKL(ShiftCheck(arena->zoneShift));
106 CHECKL(AlignCheck(arena->alignment));
107 /* Tract allocation must be platform-aligned. */
108 CHECKL(arena->alignment >= MPS_PF_ALIGN);
109 /* Stripes can't be smaller than pages. */
110 CHECKL(((Size)1 << arena->zoneShift) >= arena->alignment);
111
112 if (arena->lastTract == NULL) {
113 CHECKL(arena->lastTractBase == (Addr)0);
114 } else {
115 CHECKL(TractBase(arena->lastTract) == arena->lastTractBase);
116 }
117
118 if (arena->primary != NULL) {
119 CHECKD(Chunk, arena->primary);
120 }
121 CHECKL(RingCheck(&arena->chunkRing));
122 /* nothing to check for chunkSerial */
123 CHECKD(ChunkCacheEntry, &arena->chunkCache);
124
125 CHECKL(LocusCheck(arena));
126
127 return TRUE;
128}
129
130
131/* ArenaInit -- initialize the generic part of the arena
132 *
133 * .init.caller: Unlike PoolInit, this is called by the class init
134 * methods, not the generic Create. This is because the class is
135 * responsible for allocating the descriptor. */
136
137Res ArenaInit(Arena arena, ArenaClass class)
138{
139 Res res;
140
141 /* We do not check the arena argument, because it's _supposed_ to */
142 /* point to an uninitialized block of memory. */
143 AVERT(ArenaClass, class);
144
145 arena->class = class;
146
147 arena->committed = (Size)0;
148 /* commitLimit may be overridden by init (but probably not */
149 /* as there's not much point) */
150 arena->commitLimit = (Size)-1;
151 arena->spareCommitted = (Size)0;
152 arena->spareCommitLimit = ARENA_INIT_SPARE_COMMIT_LIMIT;
153 /* alignment is usually overridden by init */
154 arena->alignment = 1 << ARENA_ZONESHIFT;
155 /* zoneShift is usually overridden by init */
156 arena->zoneShift = ARENA_ZONESHIFT;
157 arena->poolReady = FALSE; /* design.mps.arena.pool.ready */
158 arena->lastTract = NULL;
159 arena->lastTractBase = NULL;
160
161 arena->primary = NULL;
162 RingInit(&arena->chunkRing);
163 arena->chunkSerial = (Serial)0;
164 ChunkCacheEntryInit(&arena->chunkCache);
165
166 LocusInit(arena);
167
168 res = GlobalsInit(ArenaGlobals(arena));
169 if (res != ResOK)
170 goto failGlobalsInit;
171
172 arena->sig = ArenaSig;
173
174 /* initialize the reservoir, design.mps.reservoir */
175 res = ReservoirInit(&arena->reservoirStruct, arena);
176 if (res != ResOK)
177 goto failReservoirInit;
178
179 AVERT(Arena, arena);
180 return ResOK;
181
182failReservoirInit:
183 GlobalsFinish(ArenaGlobals(arena));
184failGlobalsInit:
185 return res;
186}
187
188
189/* ArenaCreateV -- create the arena and call initializers */
190
191Res ArenaCreateV(Arena *arenaReturn, ArenaClass class, va_list args)
192{
193 Arena arena;
194 Res res;
195
196 AVER(arenaReturn != NULL);
197 AVERT(ArenaClass, class);
198
199 /* Do initialization. This will call ArenaInit (see .init.caller). */
200 res = (*class->init)(&arena, class, args);
201 if (res != ResOK)
202 goto failInit;
203
204 arena->alignment = ChunkPageSize(arena->primary);
205 if (arena->alignment > ((Size)1 << arena->zoneShift)) {
206 res = ResMEMORY; /* size was too small */
207 goto failStripeSize;
208 }
209
210 /* load cache */
211 ChunkEncache(arena, arena->primary);
212
213 res = ControlInit(arena);
214 if (res != ResOK)
215 goto failControlInit;
216
217 res = GlobalsCompleteCreate(ArenaGlobals(arena));
218 if (res != ResOK)
219 goto failGlobalsCompleteCreate;
220
221 AVERT(Arena, arena);
222 *arenaReturn = arena;
223 return ResOK;
224
225failGlobalsCompleteCreate:
226 ControlFinish(arena);
227failControlInit:
228failStripeSize:
229 (*class->finish)(arena);
230failInit:
231 return res;
232}
233
234
235/* ArenaFinish -- finish the generic part of the arena
236 *
237 * .finish.caller: Unlike PoolFinish, this is called by the class finish
238 * methods, not the generic Destroy. This is because the class is
239 * responsible for deallocating the descriptor. */
240
241void ArenaFinish(Arena arena)
242{
243 ReservoirFinish(ArenaReservoir(arena));
244 arena->sig = SigInvalid;
245 GlobalsFinish(ArenaGlobals(arena));
246 LocusFinish(arena);
247 RingFinish(&arena->chunkRing);
248}
249
250
251/* ArenaDestroy -- destroy the arena */
252
253void ArenaDestroy(Arena arena)
254{
255 AVERT(Arena, arena);
256
257 GlobalsPrepareToDestroy(ArenaGlobals(arena));
258
259 /* Empty the reservoir - see impl.c.reserv.reservoir.finish */
260 ReservoirSetLimit(ArenaReservoir(arena), 0);
261
262 arena->poolReady = FALSE;
263 ControlFinish(arena);
264
265 /* Call class-specific finishing. This will call ArenaFinish. */
266 (*arena->class->finish)(arena);
267
268 EventFinish();
269}
270
271
272/* ControlInit -- initialize the control pool */
273
274Res ControlInit(Arena arena)
275{
276 Res res;
277
278 AVERT(Arena, arena);
279 res = PoolInit(&arena->controlPoolStruct.poolStruct,
280 arena, PoolClassMV(),
281 ARENA_CONTROL_EXTENDBY, ARENA_CONTROL_AVGSIZE,
282 ARENA_CONTROL_MAXSIZE);
283 if (res != ResOK)
284 return res;
285 arena->poolReady = TRUE; /* design.mps.arena.pool.ready */
286 return ResOK;
287}
288
289
290/* ControlFinish -- finish the control pool */
291
292void ControlFinish(Arena arena)
293{
294 AVERT(Arena, arena);
295 arena->poolReady = FALSE;
296 PoolFinish(&arena->controlPoolStruct.poolStruct);
297}
298
299
300/* ArenaDescribe -- describe the arena */
301
302Res ArenaDescribe(Arena arena, mps_lib_FILE *stream)
303{
304 Res res;
305
306 if (!CHECKT(Arena, arena)) return ResFAIL;
307 if (stream == NULL) return ResFAIL;
308
309 res = WriteF(stream, "Arena $P {\n", (WriteFP)arena,
310 " class $P (\"$S\")\n",
311 (WriteFP)arena->class, arena->class->name,
312 NULL);
313 if (res != ResOK) return res;
314
315 if (arena->poolReady) {
316 res = WriteF(stream,
317 " controlPool $P\n", (WriteFP)&arena->controlPoolStruct,
318 NULL);
319 if (res != ResOK) return res;
320 }
321
322 res = WriteF(stream,
323 " commitLimit $W\n", (WriteFW)arena->commitLimit,
324 " spareCommitted $W\n", (WriteFW)arena->spareCommitted,
325 " spareCommitLimit $W\n", (WriteFW)arena->spareCommitLimit,
326 " zoneShift $U\n", (WriteFU)arena->zoneShift,
327 " alignment $W\n", (WriteFW)arena->alignment,
328 NULL);
329 if (res != ResOK) return res;
330
331 res = (*arena->class->describe)(arena, stream);
332 if (res != ResOK) return res;
333
334 res = GlobalsDescribe(ArenaGlobals(arena), stream);
335 if (res != ResOK) return res;
336
337 res = WriteF(stream,
338 "} Arena $P ($U)\n", (WriteFP)arena,
339 (WriteFU)arena->serial,
340 NULL);
341 return res;
342}
343
344
345/* ArenaDescribeTracts -- describe all the tracts in the arena */
346
347Res ArenaDescribeTracts(Arena arena, mps_lib_FILE *stream)
348{
349 Res res;
350 Tract tract;
351 Bool b;
352 Addr oldLimit, base, limit;
353 Size size;
354
355 if (!CHECKT(Arena, arena)) return ResFAIL;
356 if (stream == NULL) return ResFAIL;
357
358 b = TractFirst(&tract, arena);
359 oldLimit = TractBase(tract);
360 while (b) {
361 base = TractBase(tract);
362 limit = TractLimit(tract);
363 size = ArenaAlign(arena);
364
365 if (TractBase(tract) > oldLimit) {
366 res = WriteF(stream,
367 "[$P, $P) $W $U ---\n",
368 (WriteFP)oldLimit, (WriteFP)base,
369 (WriteFW)AddrOffset(oldLimit, base),
370 (WriteFU)AddrOffset(oldLimit, base),
371 NULL);
372 if (res != ResOK) return res;
373 }
374
375 res = WriteF(stream,
376 "[$P, $P) $W $U $P ($S)\n",
377 (WriteFP)base, (WriteFP)limit,
378 (WriteFW)size, (WriteFW)size,
379 (WriteFP)TractPool(tract),
380 (WriteFS)(TractPool(tract)->class->name),
381 NULL);
382 if (res != ResOK) return res;
383 b = TractNext(&tract, arena, TractBase(tract));
384 oldLimit = limit;
385 }
386 return ResOK;
387}
388
389
390/* ControlAlloc -- allocate a small block directly from the control pool
391 *
392 * .arena.control-pool: Actually the block will be allocated from the
393 * control pool, which is an MV pool embedded in the arena itself.
394 *
395 * .controlalloc.addr: In implementations where Addr is not compatible
396 * with void* (design.mps.type.addr.use), ControlAlloc must take care of
397 * allocating so that the block can be addressed with a void*. */
398
399Res ControlAlloc(void **baseReturn, Arena arena, size_t size,
400 Bool withReservoirPermit)
401{
402 Addr base;
403 Res res;
404
405 AVERT(Arena, arena);
406 AVER(baseReturn != NULL);
407 AVER(size > 0);
408 AVER(BoolCheck(withReservoirPermit));
409 AVER(arena->poolReady);
410
411 res = PoolAlloc(&base, ArenaControlPool(arena), (Size)size,
412 withReservoirPermit);
413 if (res != ResOK)
414 return res;
415
416 *baseReturn = (void *)base; /* see .controlalloc.addr */
417 return ResOK;
418}
419
420
421/* ControlFree -- free a block allocated using ControlAlloc */
422
423void ControlFree(Arena arena, void* base, size_t size)
424{
425 AVERT(Arena, arena);
426 AVER(base != NULL);
427 AVER(size > 0);
428 AVER(arena->poolReady);
429
430 PoolFree(ArenaControlPool(arena), (Addr)base, (Size)size);
431}
432
433
434/* ArenaAlloc -- allocate some tracts from the arena */
435
436Res ArenaAlloc(Addr *baseReturn, SegPref pref, Size size, Pool pool,
437 Bool withReservoirPermit)
438{
439 Res res;
440 Arena arena;
441 Addr base;
442 Tract baseTract;
443 Reservoir reservoir;
444
445 AVER(baseReturn != NULL);
446 AVERT(SegPref, pref);
447 AVER(size > (Size)0);
448 AVERT(Pool, pool);
449 AVER(BoolCheck(withReservoirPermit));
450
451 arena = PoolArena(pool);
452 AVERT(Arena, arena);
453 AVER(SizeIsAligned(size, arena->alignment));
454 reservoir = ArenaReservoir(arena);
455 AVERT(Reservoir, reservoir);
456
457 res = ReservoirEnsureFull(reservoir);
458 if (res != ResOK) {
459 AVER(ResIsAllocFailure(res));
460 if (!withReservoirPermit)
461 return res;
462 }
463
464 res = (*arena->class->alloc)(&base, &baseTract, pref, size, pool);
465 if (res == ResOK) {
466 goto goodAlloc;
467 } else if (withReservoirPermit) {
468 AVER(ResIsAllocFailure(res));
469 res = ReservoirWithdraw(&base, &baseTract, reservoir, size, pool);
470 if (res == ResOK)
471 goto goodAlloc;
472 }
473 EVENT_PWP(ArenaAllocFail, arena, size, pool);
474 return res;
475
476goodAlloc:
477 /* cache the tract - design.mps.arena.tract.cache */
478 arena->lastTract = baseTract;
479 arena->lastTractBase = base;
480
481 EVENT_PPAWP(ArenaAlloc, arena, baseTract, base, size, pool);
482 *baseReturn = base;
483 return ResOK;
484}
485
486
487/* ArenaFree -- free some tracts to the arena */
488
489void ArenaFree(Addr base, Size size, Pool pool)
490{
491 Arena arena;
492 Addr limit;
493 Reservoir reservoir;
494 Res res;
495
496 AVERT(Pool, pool);
497 AVER(base != NULL);
498 AVER(size > (Size)0);
499 arena = PoolArena(pool);
500 AVERT(Arena, arena);
501 reservoir = ArenaReservoir(arena);
502 AVERT(Reservoir, reservoir);
503 AVER(AddrIsAligned(base, arena->alignment));
504 AVER(SizeIsAligned(size, arena->alignment));
505
506 /* uncache the tract if in range - design.mps.arena.tract.uncache */
507 limit = AddrAdd(base, size);
508 if ((arena->lastTractBase >= base) && (arena->lastTractBase < limit)) {
509 arena->lastTract = NULL;
510 arena->lastTractBase = (Addr)0;
511 }
512
513 res = ReservoirEnsureFull(reservoir);
514 if (res == ResOK) {
515 (*arena->class->free)(base, size, pool);
516 } else {
517 AVER(ResIsAllocFailure(res));
518 ReservoirDeposit(reservoir, base, size);
519 }
520
521 EVENT_PAW(ArenaFree, arena, base, size);
522 return;
523}
524
525
526Size ArenaReserved(Arena arena)
527{
528 AVERT(Arena, arena);
529 return (*arena->class->reserved)(arena);
530}
531
532Size ArenaCommitted(Arena arena)
533{
534 AVERT(Arena, arena);
535 return arena->committed;
536}
537
538Size ArenaSpareCommitted(Arena arena)
539{
540 AVERT(Arena, arena);
541 return arena->spareCommitted;
542}
543
544Size ArenaSpareCommitLimit(Arena arena)
545{
546 AVERT(Arena, arena);
547 return arena->spareCommitLimit;
548}
549
550void ArenaSetSpareCommitLimit(Arena arena, Size limit)
551{
552 AVERT(Arena, arena);
553 /* Can't check limit, as all possible values are allowed. */
554
555 arena->spareCommitLimit = limit;
556 if (arena->spareCommitLimit < arena->spareCommitted) {
557 arena->class->spareCommitExceeded(arena);
558 }
559
560 EVENT_PW(SpareCommitLimitSet, arena, limit);
561 return;
562}
563
564/* Used by arenas which don't use spare committed memory */
565void ArenaNoSpareCommitExceeded(Arena arena)
566{
567 AVERT(Arena, arena);
568 return;
569}
570
571
572Size ArenaCommitLimit(Arena arena)
573{
574 AVERT(Arena, arena);
575 return arena->commitLimit;
576}
577
578Res ArenaSetCommitLimit(Arena arena, Size limit)
579{
580 Size committed;
581 Res res;
582
583 AVERT(Arena, arena);
584 AVER(ArenaCommitted(arena) <= arena->commitLimit);
585
586 committed = ArenaCommitted(arena);
587 if (limit < committed) {
588 /* Attempt to set the limit below current committed */
589 if (limit >= committed - arena->spareCommitted) {
590 /* could set the limit by flushing any spare committed memory */
591 arena->class->spareCommitExceeded(arena);
592 AVER(limit >= ArenaCommitted(arena));
593 arena->commitLimit = limit;
594 res = ResOK;
595 } else {
596 res = ResFAIL;
597 }
598 } else {
599 arena->commitLimit = limit;
600 res = ResOK;
601 }
602 EVENT_PWU(CommitLimitSet, arena, limit, (res == ResOK));
603 return res;
604}
605
606
607/* ArenaAvail -- return available memory in the arena */
608
609Size ArenaAvail(Arena arena)
610{
611 Size sSwap;
612
613 sSwap = ArenaReserved(arena);
614 if (sSwap > arena->commitLimit) sSwap = arena->commitLimit;
615 /* @@@@ sSwap should take actual paging file size into account */
616 return sSwap - arena->committed + arena->spareCommitted;
617}
618
619
620/* ArenaExtend -- Add a new chunk in the arena */
621
622Res ArenaExtend(Arena arena, Addr base, Size size)
623{
624 Res res;
625
626 AVERT(Arena, arena);
627 AVER(base != (Addr)0);
628 AVER(size > 0);
629
630 res = (*arena->class->extend)(arena, base, size);
631 if (res != ResOK)
632 return res;
633
634 EVENT_PAW(ArenaExtend, arena, base, size);
635 return ResOK;
636}
637
638
639/* ArenaNoExtend -- fail to extend the arena by a chunk */
640
641Res ArenaNoExtend(Arena arena, Addr base, Size size)
642{
643 AVERT(Arena, arena);
644 AVER(base != (Addr)0);
645 AVER(size > (Size)0);
646
647 NOTREACHED;
648 return ResUNIMPL;
649}