1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
13 /* Include Valgrind if it is available? */
14 #if defined(SJME_CONFIG_HAS_VALGRIND)
19 #include "sjme/alloc.h"
20 #include "sjme/debug.h"
21 #include "sjme/atomic.h"
22 #include "sjme/multithread.h"
24 /** The minimum size permitted for allocation pools. */
25 #define SJME_ALLOC_MIN_SIZE (((SJME_SIZEOF_ALLOC_POOL(0) + \
26 (SJME_SIZEOF_ALLOC_LINK(0) * 3)) | 0x1FF))
28 /** The minimum size for splits. */
29 #define SJME_ALLOC_SPLIT_MIN_SIZE 64
31 /** The front guard value. */
32 #define SJME_ALLOC_GUARD_FRONT INT32_C(0x53716B21)
34 /** The back guard value. */
35 #define SJME_ALLOC_GUARD_BACK INT32_C(0x6C65783F)
37 #if defined(SJME_CONFIG_DEBUG)
39 * Prints information on a given link and returns.
41 * @param pool The pool this is in.
42 * @param atLink The link to print info for.
43 * @param trigger The trigger for the failure.
44 * @return Always @c SJME_JNI_TRUE .
47 static sjme_inline sjme_jboolean
sjme_alloc_corruptFail(
48 volatile sjme_alloc_pool
* pool
,
49 volatile sjme_alloc_link
* atLink
,
52 sjme_message("Corrupted Link %p: %s", atLink
, trigger
);
58 /* Dump everything about the link. */
59 sjme_message("link->guardFront: %08x", atLink
->guardFront
);
60 sjme_message("link->pool: %p (should be %p)", atLink
->pool
, pool
);
61 sjme_message("link->prev: %p", atLink
->prev
);
62 sjme_message("link->next: %p", atLink
->next
);
63 if (atLink
->space
== SJME_ALLOC_POOL_SPACE_USED
)
64 sjme_message("link->space: USED");
65 else if (atLink
->space
== SJME_ALLOC_POOL_SPACE_FREE
)
66 sjme_message("link->space: FREE");
67 else if (atLink
->space
== SJME_NUM_ALLOC_POOL_SPACE
)
68 sjme_message("link->space: NUM");
70 sjme_message("link->space: %d", (int)atLink
->space
);
71 sjme_message("link->weak: %p", atLink
->weak
);
72 sjme_message("link->freePrev: %p", atLink
->freePrev
);
73 sjme_message("link->freeNext: %p", atLink
->freeNext
);
74 sjme_message("link->allocSize: %d", (int)atLink
->allocSize
);
75 sjme_message("link->blockSize: %d", (int)atLink
->blockSize
);
76 sjme_message("link->guardBack: %08x", atLink
->guardBack
);
79 if (sjme_debug_handlers
!= NULL
&& sjme_debug_handlers
->abort
!= NULL
)
80 sjme_debug_handlers
->abort();
82 /* Always indicate failure here. */
87 * Prints information on a given link and returns.
89 * @param pool The pool this is in.
90 * @param atLink The link to print info for.
91 * @param trigger The trigger for the failure.
92 * @return Always @c SJME_JNI_TRUE .
95 #define sjme_alloc_corruptFail(pool, atLink, trigger) SJME_JNI_TRUE
98 static sjme_inline sjme_jboolean
sjme_alloc_checkCorruptionRange(
99 sjme_alloc_pool
* pool
, uintptr_t poolStart
, uintptr_t poolEnd
,
100 sjme_alloc_link
* atLink
)
104 /* Ignore null pointers. */
106 return SJME_JNI_FALSE
;
108 /* Nominal address of the check pointer. */
109 check
= (uintptr_t)atLink
;
111 /* Must be in range! */
112 if (check
< poolStart
|| check
>= poolEnd
)
113 return sjme_alloc_corruptFail(pool
, atLink
,
114 "Out of range link");
116 /* Does not appear corrupt. */
117 return SJME_JNI_FALSE
;
121 * Checks the integrity of the memory pool.
123 * @param pool The pool to check in.
124 * @param atLink The link of the pool.
125 * @return If there is corruption or not.
128 static sjme_jboolean sjme_noOptimize
sjme_alloc_checkCorruption(
129 volatile sjme_alloc_pool
* pool
,
130 volatile sjme_alloc_link
* atLink
)
132 uintptr_t poolStart
, poolEnd
;
135 return SJME_JNI_TRUE
;
137 /* If no link is specified, ignore. */
139 return SJME_JNI_FALSE
;
141 /* Check front and back guards. */
142 if (atLink
->guardFront
!= SJME_ALLOC_GUARD_FRONT
)
143 return sjme_alloc_corruptFail(pool
, atLink
,
144 "Wrong front guard");
145 if (atLink
->guardBack
!= SJME_ALLOC_GUARD_BACK
)
146 return sjme_alloc_corruptFail(pool
, atLink
,
149 /* Link is in the wrong pool. */
150 if (atLink
->pool
!= pool
)
151 return sjme_alloc_corruptFail(pool
, atLink
,
154 /* Allocation size larger than block? */
155 if (atLink
->allocSize
> atLink
->blockSize
)
156 return sjme_alloc_corruptFail(pool
, atLink
,
157 "Allocation size larger than block.");
159 /* Next link is in the wrong location? */
160 if (atLink
->next
!= NULL
&& (uintptr_t)atLink
->next
!=
161 (uintptr_t)&atLink
->block
[atLink
->blockSize
])
162 return sjme_alloc_corruptFail(pool
, atLink
,
163 "Next not at block end");
165 /* Is front/end link? */
166 if (atLink
== pool
->frontLink
|| atLink
== pool
->backLink
)
168 /* Link space incorrect? */
169 if (atLink
->space
!= SJME_NUM_ALLOC_POOL_SPACE
)
170 return sjme_alloc_corruptFail(pool
, atLink
,
171 "Front/Back link not in correct space");
173 /* Size is not zero? */
174 if (atLink
->blockSize
!= 0 || atLink
->allocSize
!= 0)
175 return sjme_alloc_corruptFail(pool
, atLink
,
176 "Front/back link sizes non-zero");
178 /* Does not appear corrupt. */
179 return SJME_JNI_FALSE
;
182 /* Invalid block size? */
183 if (atLink
->blockSize
<= 0)
184 return sjme_alloc_corruptFail(pool
, atLink
,
185 "Zero or negative block size");
187 /* Used for checking the integrity of pointers. */
188 poolStart
= (uintptr_t)pool
;
189 poolEnd
= (uintptr_t)&pool
->block
[pool
->size
];
191 /* Free link only. */
192 if (atLink
->space
== SJME_ALLOC_POOL_SPACE_FREE
)
194 /* Check free links. */
195 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
197 return sjme_alloc_corruptFail(pool
, atLink
,
199 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
201 return sjme_alloc_corruptFail(pool
, atLink
,
205 /* Used link only. */
206 else if (atLink
->space
== SJME_ALLOC_POOL_SPACE_USED
)
208 /* Zero or negative size. */
209 if (atLink
->allocSize
<= 0)
210 return sjme_alloc_corruptFail(pool
, atLink
,
211 "Zero/negative used allocSize");
213 /* Cannot have any free or previous links. */
214 if (atLink
->freePrev
!= NULL
|| atLink
->freeNext
!= NULL
)
215 return sjme_alloc_corruptFail(pool
, atLink
,
216 "Used has free links");
219 /* Link space incorrect? */
221 return sjme_alloc_corruptFail(pool
, atLink
,
224 /* Check common next links. */
225 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
227 return sjme_alloc_corruptFail(pool
, atLink
,
229 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
231 return sjme_alloc_corruptFail(pool
, atLink
,
234 /* Does not appear corrupt. */
235 return SJME_JNI_FALSE
;
238 static sjme_errorCode
sjme_alloc_getLinkOptional(
239 sjme_attrInNotNull sjme_pointer addr
,
240 sjme_attrOutNotNull sjme_alloc_link
** outLink
,
241 sjme_attrInValue sjme_jboolean checkCorruption
)
243 sjme_alloc_link
* link
;
245 if (addr
== NULL
|| outLink
== NULL
)
246 return SJME_ERROR_NULL_ARGUMENTS
;
248 /* Just need to do some reversing math. */
249 link
= (sjme_alloc_link
*)(((uintptr_t)addr
) -
250 offsetof(sjme_alloc_link
, block
));
252 /* Check the integrity of the link. */
254 sjme_alloc_checkCorruption(link
->pool
, link
);
258 return SJME_ERROR_NONE
;
261 sjme_errorCode sjme_noOptimize
sjme_alloc_poolInitMalloc(
262 sjme_attrOutNotNull sjme_alloc_pool
** outPool
,
263 sjme_attrInPositive sjme_jint size
)
268 /* Make sure the size is not wonky. */
269 useSize
= SJME_SIZEOF_ALLOC_POOL(size
);
270 if (outPool
== NULL
|| size
<= SJME_ALLOC_MIN_SIZE
|| useSize
<= 0 ||
272 return SJME_ERROR_INVALID_ARGUMENT
;
274 /* Attempt allocation. */
275 result
= malloc(useSize
);
277 return sjme_error_outOfMemory(NULL
, useSize
);
279 /* Use static pool initializer to set up structures. */
280 return sjme_alloc_poolInitStatic(outPool
, result
, useSize
);
283 sjme_errorCode sjme_noOptimize
sjme_alloc_poolInitStatic(
284 sjme_attrOutNotNull sjme_alloc_pool
** outPool
,
285 sjme_attrInNotNull sjme_pointer baseAddr
,
286 sjme_attrInPositive sjme_jint size
)
288 sjme_alloc_pool
* pool
;
289 sjme_alloc_link
* frontLink
;
290 sjme_alloc_link
* midLink
;
291 sjme_alloc_link
* backLink
;
292 sjme_alloc_link
* specialParent
;
294 if (outPool
== NULL
|| baseAddr
== NULL
)
295 return SJME_ERROR_NULL_ARGUMENTS
;
297 if (size
<= SJME_ALLOC_MIN_SIZE
)
298 return SJME_ERROR_INVALID_ARGUMENT
;
300 /* Initialize memory to nothing. */
301 memset(baseAddr
, 0, size
);
303 /* Setup initial pool structure. */
305 pool
->size
= (size
& (~7)) - SJME_SIZEOF_ALLOC_POOL(0);
307 /* Setup front link. */
308 frontLink
= (sjme_pointer
)&pool
->block
[0];
309 pool
->frontLink
= frontLink
;
311 /* Setup back link. */
312 backLink
= (sjme_pointer
)&pool
->block
[pool
->size
-
313 SJME_SIZEOF_ALLOC_LINK(0)];
314 pool
->backLink
= backLink
;
316 /* Setup middle link, which is between the two. */
317 midLink
= (sjme_pointer
)&frontLink
->block
[0];
318 midLink
->prev
= frontLink
;
319 frontLink
->next
= midLink
;
320 midLink
->next
= backLink
;
321 backLink
->prev
= midLink
;
323 /* Determine size of the middle link, which is free space. */
324 midLink
->blockSize
= (sjme_jint
)((uintptr_t)backLink
-
325 (uintptr_t)&midLink
->block
[0]);
327 /* The mid-link is considered free. */
328 midLink
->space
= SJME_ALLOC_POOL_SPACE_FREE
;
330 /* The front and back links are in the "invalid" space. */
331 frontLink
->space
= SJME_NUM_ALLOC_POOL_SPACE
;
332 backLink
->space
= SJME_NUM_ALLOC_POOL_SPACE
;
334 /* Determine size that can and cannot be used. */
335 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
=
336 SJME_SIZEOF_ALLOC_LINK(0);
337 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
= midLink
->blockSize
;
339 /* Link in the first and last actual blocks for the free chain. */
340 pool
->freeFirstLink
= frontLink
;
341 frontLink
->freeNext
= midLink
;
342 midLink
->freePrev
= frontLink
;
343 pool
->freeLastLink
= backLink
;
344 backLink
->freePrev
= midLink
;
345 midLink
->freeNext
= backLink
;
347 /* Guards for all links. */
348 frontLink
->guardFront
= SJME_ALLOC_GUARD_FRONT
;
349 frontLink
->guardBack
= SJME_ALLOC_GUARD_BACK
;
350 midLink
->guardFront
= SJME_ALLOC_GUARD_FRONT
;
351 midLink
->guardBack
= SJME_ALLOC_GUARD_BACK
;
352 backLink
->guardFront
= SJME_ALLOC_GUARD_FRONT
;
353 backLink
->guardBack
= SJME_ALLOC_GUARD_BACK
;
356 frontLink
->pool
= pool
;
357 midLink
->pool
= pool
;
358 backLink
->pool
= pool
;
360 #if defined(SJME_CONFIG_DEBUG)
361 /* Debug source line init blocks. */
362 pool
->frontLink
->debugFile
= "<FRONT LINK>";
363 pool
->frontLink
->debugLine
= 1;
364 pool
->frontLink
->debugFunction
= "<FRONT LINK>";
366 pool
->backLink
->debugFile
= "<BACK LINK>";
367 pool
->backLink
->debugLine
= 1;
368 pool
->backLink
->debugFunction
= "<BACK LINK>";
371 #if defined(SJME_CONFIG_HAS_VALGRIND)
372 /* Reserve front side in Valgrind. */
373 VALGRIND_MAKE_MEM_NOACCESS(baseAddr
,
374 ((uintptr_t)&midLink
->block
[0] - (uintptr_t)baseAddr
));
376 /* Reserve back side in Valgrind. */
377 VALGRIND_MAKE_MEM_NOACCESS(backLink
,
378 (SJME_SIZEOF_ALLOC_LINK(0)));
381 /* If this is a valid link then we are allocating a nested pool. */
383 specialParent
= NULL
;
384 if (!sjme_error_is(sjme_alloc_getLinkOptional(baseAddr
,
385 &specialParent
, SJME_JNI_FALSE
)))
386 specialParent
->flags
|= SJME_ALLOC_LINK_FLAG_NESTED_POOL
;
391 return SJME_ERROR_NONE
;
394 sjme_errorCode
sjme_alloc_poolSpaceTotalSize(
395 sjme_attrInNotNull
const sjme_alloc_pool
* pool
,
396 sjme_attrOutNullable sjme_jint
* outTotal
,
397 sjme_attrOutNullable sjme_jint
* outReserved
,
398 sjme_attrOutNullable sjme_jint
* outUsable
)
405 return SJME_ERROR_NULL_ARGUMENTS
;
407 if (outTotal
== NULL
&& outReserved
== NULL
&& outUsable
== NULL
)
408 return SJME_ERROR_NULL_ARGUMENTS
;
410 /* Run through and tally values for each space. */
413 for (i
= 0; i
< SJME_NUM_ALLOC_POOL_SPACE
; i
++)
415 reserved
+= pool
->space
[i
].reserved
;
416 usable
+= pool
->space
[i
].usable
;
419 /* Total space is both. */
420 total
= reserved
+ usable
;
422 /* Store output values. */
423 if (outTotal
!= NULL
)
425 if (outReserved
!= NULL
)
426 *outReserved
= reserved
;
427 if (outUsable
!= NULL
)
431 return SJME_ERROR_NONE
;
434 sjme_errorCode sjme_noOptimize
SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
435 sjme_attrInNotNull
volatile sjme_alloc_pool
* pool
,
436 sjme_attrInPositiveNonZero sjme_jint size
,
437 sjme_attrOutNotNull sjme_pointer
* outAddr
438 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
440 sjme_errorCode error
;
441 sjme_alloc_link
* scanLink
;
442 sjme_alloc_link
* rightLink
;
443 sjme_jint splitMinSize
, roundSize
;
444 sjme_jboolean splitBlock
;
445 sjme_alloc_pool
* nextPool
;
446 volatile sjme_alloc_link
* nextFree
;
448 if (pool
== NULL
|| size
<= 0 || outAddr
== NULL
)
449 return SJME_ERROR_NULL_ARGUMENTS
;
451 #if defined(SJME_CONFIG_DEBUG)
452 if ((size
* 8) == SJME_CONFIG_HAS_POINTER
)
453 sjme_message("Alloc of single pointer in %s (%s:%d).",
457 /* Determine the size this will actually take up, which includes the */
458 /* link to be created following this. */
459 roundSize
= (((size
& 7) != 0) ? ((size
| 7) + 1) : size
);
460 splitMinSize
= roundSize
+
461 (sjme_jint
)SJME_SIZEOF_ALLOC_LINK(SJME_ALLOC_SPLIT_MIN_SIZE
) +
462 (sjme_jint
)SJME_SIZEOF_ALLOC_LINK(0);
463 if (size
> splitMinSize
|| splitMinSize
< 0)
464 return SJME_ERROR_INVALID_ARGUMENT
;
466 /* Take ownership of lock. */
467 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
469 return sjme_error_default(error
);
472 sjme_thread_barrier();
474 /* Find the first free link that this fits in. */
476 splitBlock
= SJME_JNI_FALSE
;
477 for (scanLink
= pool
->freeFirstLink
;
478 scanLink
!= NULL
; scanLink
= scanLink
->freeNext
)
480 /* Has memory been corrupted? */
481 nextFree
= scanLink
->freeNext
;
482 if (sjme_alloc_checkCorruption(pool
, scanLink
) ||
484 sjme_alloc_checkCorruption(pool
, nextFree
)))
486 error
= SJME_ERROR_MEMORY_CORRUPTION
;
490 /* Block is in the "invalid" space, skip it. */
491 if (scanLink
->space
== SJME_NUM_ALLOC_POOL_SPACE
)
494 /* Block fits perfectly here, without needing a split? */
495 if (scanLink
->blockSize
== roundSize
)
498 /* Block fits here when split, try to not split ridiculously small. */
499 if (scanLink
->blockSize
>= splitMinSize
)
501 splitBlock
= SJME_JNI_TRUE
;
507 if (scanLink
== NULL
)
509 /* If there is an adjacent pool, if allocation fails then we shall */
510 /* try the next pool, this means multiple pools can work together */
512 nextPool
= pool
->nextPool
;
513 if (nextPool
!= NULL
)
515 /* Release ownership of lock. */
516 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
517 &pool
->spinLock
, NULL
)))
518 return sjme_error_default(error
);
520 #if defined(SJME_CONFIG_DEBUG)
521 return sjme_allocR(nextPool
, size
, outAddr
,
524 return sjme_alloc(pool
->nextPool
, size
, outAddr
);
528 /* Otherwise fail! */
529 error
= sjme_error_outOfMemory(pool
, size
);
535 sjme_message("Found link at %p: %d bytes, we need %d with split %d.",
536 scanLink
, (int)scanLink
->blockSize
, (int)roundSize
, (int)splitBlock
);
539 /* Does this block need to be split? */
542 /* Check for link corruption on the adjacent links. */
543 if (sjme_alloc_checkCorruption(pool
, scanLink
->next
) ||
544 sjme_alloc_checkCorruption(pool
, scanLink
->prev
) ||
545 sjme_alloc_checkCorruption(pool
, scanLink
->freeNext
) ||
546 sjme_alloc_checkCorruption(pool
, scanLink
->freePrev
))
548 error
= SJME_ERROR_MEMORY_CORRUPTION
;
552 /* Make it so this block can actually fit in here. */
553 rightLink
= (sjme_alloc_link
*)&scanLink
->block
[roundSize
];
555 /* Initialize block to remove any old data. */
556 memset(rightLink
, 0, sizeof(*rightLink
));
558 /* Guards for link. */
559 rightLink
->guardFront
= SJME_ALLOC_GUARD_FRONT
;
560 rightLink
->guardBack
= SJME_ALLOC_GUARD_BACK
;
562 /* Set the right link's pool accordingly. */
563 rightLink
->pool
= pool
;
565 /* Make sure this block is marked as free. */
566 rightLink
->space
= SJME_ALLOC_POOL_SPACE_FREE
;
568 /* Set size of the right link. */
569 rightLink
->blockSize
=
570 (sjme_jint
)((intptr_t)&scanLink
->block
[scanLink
->blockSize
] -
571 (intptr_t)&rightLink
->block
[0]);
572 rightLink
->allocSize
= rightLink
->blockSize
;
574 /* Link in physical links. */
575 rightLink
->next
= scanLink
->next
;
576 rightLink
->next
->prev
= rightLink
;
577 scanLink
->next
= rightLink
;
578 rightLink
->prev
= scanLink
;
580 /* Link in free links. */
581 rightLink
->freeNext
= scanLink
->freeNext
;
582 rightLink
->freeNext
->freePrev
= rightLink
;
583 scanLink
->freeNext
= rightLink
;
584 rightLink
->freePrev
= scanLink
;
586 /* Set size of the left block. */
587 scanLink
->blockSize
=
588 (sjme_jint
)((intptr_t)rightLink
- (intptr_t)&scanLink
->block
[0]);
589 scanLink
->allocSize
= scanLink
->blockSize
;
591 /* Adjust reserved and usable space. */
592 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
+=
593 SJME_SIZEOF_ALLOC_LINK(0);
594 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
-=
595 SJME_SIZEOF_ALLOC_LINK(0);
597 /* Make sure we did not cause corruption. */
598 if (sjme_alloc_checkCorruption(pool
, scanLink
) ||
599 sjme_alloc_checkCorruption(pool
, rightLink
))
601 error
= SJME_ERROR_MEMORY_CORRUPTION
;
606 /* Setup block information. */
607 scanLink
->space
= SJME_ALLOC_POOL_SPACE_USED
;
609 /* Unlink from free links. */
610 if (scanLink
->freeNext
!= NULL
)
611 scanLink
->freeNext
->freePrev
= scanLink
->freePrev
;
612 if (scanLink
->freePrev
!= NULL
)
613 scanLink
->freePrev
->freeNext
= scanLink
->freeNext
;
614 scanLink
->freePrev
= NULL
;
615 scanLink
->freeNext
= NULL
;
617 /* Use our given allocation size. */
618 scanLink
->allocSize
= size
;
620 /* Adjust space that can actually be used for data. */
621 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
-= scanLink
->blockSize
;
622 pool
->space
[SJME_ALLOC_POOL_SPACE_USED
].usable
+= scanLink
->blockSize
;
624 /* Since this block is claimed, the reserved space moves over. */
625 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
-=
626 SJME_SIZEOF_ALLOC_LINK(0);
627 pool
->space
[SJME_ALLOC_POOL_SPACE_USED
].reserved
+=
628 SJME_SIZEOF_ALLOC_LINK(0);
630 #if defined(SJME_CONFIG_DEBUG)
631 /* Set debug info. */
632 scanLink
->debugFile
= file
;
633 scanLink
->debugLine
= line
;
634 scanLink
->debugFunction
= func
;
637 /* Make sure we did not cause corruption. */
638 if (sjme_alloc_checkCorruption(pool
, scanLink
) ||
639 sjme_alloc_checkCorruption(pool
, scanLink
->prev
) ||
640 sjme_alloc_checkCorruption(pool
, scanLink
->next
))
642 error
= SJME_ERROR_MEMORY_CORRUPTION
;
647 sjme_thread_barrier();
649 /* Release ownership of lock. */
650 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
651 &pool
->spinLock
, NULL
)))
652 return sjme_error_default(error
);
654 /* Use the given link. */
655 *outAddr
= &scanLink
->block
[0];
656 return SJME_ERROR_NONE
;
660 /* Release ownership of lock before we leave. */
661 if (sjme_error_is(sjme_thread_spinLockRelease(
662 &pool
->spinLock
, NULL
)))
663 return sjme_error_default(error
);
665 return sjme_error_default(error
);
668 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_copy
)(
669 sjme_attrInNotNull
volatile sjme_alloc_pool
* pool
,
670 sjme_attrInPositiveNonZero sjme_jint size
,
671 sjme_attrOutNotNull sjme_pointer
* outAddr
,
672 sjme_attrInNotNull sjme_pointer inAddr
673 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
675 sjme_errorCode error
;
678 if (pool
== NULL
|| outAddr
== NULL
|| inAddr
== NULL
)
679 return SJME_ERROR_NULL_ARGUMENTS
;
681 /* Allocate new copy first. */
683 if (sjme_error_is(error
= SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
685 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) || dest
== NULL
)
686 return sjme_error_default(error
);
689 memmove(dest
, inAddr
, size
);
693 return SJME_ERROR_NONE
;
696 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_format
)(
697 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
698 sjme_attrOutNotNull sjme_lpstr
* outString
,
699 SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL SJME_DEBUG_ONLY_COMMA
700 sjme_attrInNotNull sjme_attrFormatArg
const char* format
,
708 if (inPool
== NULL
|| outString
== NULL
|| format
== NULL
)
709 return SJME_ERROR_NULL_ARGUMENTS
;
711 /* Start variable arguments. */
712 va_start(arg
, format
);
714 /* Format string to the buffer. */
715 memset(buf
, 0, sizeof(buf
));
716 vsnprintf(buf
, BUF_SIZE
- 1, format
, arg
);
718 /* Force to end with a NUL. */
719 buf
[BUF_SIZE
- 1] = 0;
724 /* Calculate length of string for copying. */
728 return SJME_DEBUG_IDENTIFIER(sjme_alloc_copy
)(inPool
, len
+ 1,
729 (sjme_pointer
*)outString
, buf
730 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
);
734 static sjme_errorCode
sjme_alloc_mergeFree(sjme_alloc_link
* link
)
736 sjme_alloc_pool
* pool
;
737 sjme_alloc_link
* right
;
738 sjme_alloc_link
* oldRightFreeNext
;
739 sjme_alloc_link
* rightRight
;
740 sjme_alloc_link
* checkLeft
;
744 return SJME_ERROR_NULL_ARGUMENTS
;
746 /* Need pool for all operations. */
749 /* If the previous block is free, pivot to there. */
750 checkLeft
= link
->prev
;
751 if (checkLeft
->space
== SJME_ALLOC_POOL_SPACE_FREE
)
752 return sjme_alloc_mergeFree(checkLeft
);
754 /* Is the block on the right a candidate for merge? */
756 if (right
->space
!= SJME_ALLOC_POOL_SPACE_FREE
)
757 return SJME_ERROR_NONE
;
759 /* We need the block after to relink. */
760 rightRight
= right
->next
;
762 /* Disconnect in the middle. */
763 link
->next
= rightRight
;
764 rightRight
->prev
= link
;
766 /* Remove from the free chain. */
767 oldRightFreeNext
= right
->freeNext
;
768 right
->freePrev
->freeNext
= right
->freeNext
;
769 oldRightFreeNext
->freePrev
= right
->freePrev
;
771 /* Reclaim the right link data area. */
772 addedSize
= right
->blockSize
+ SJME_SIZEOF_ALLOC_LINK(0);
773 link
->blockSize
+= addedSize
;
775 /* Update pool sizes. */
776 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
+= addedSize
;
777 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
-=
778 SJME_SIZEOF_ALLOC_LINK(0);
780 /* Synchronize allocation size. */
781 link
->allocSize
= link
->blockSize
;
783 /* Wipe next side block to remove any stale data. */
784 memset(right
, 0, sizeof(*right
));
786 /* Should not have corrupted the block. */
787 if (sjme_alloc_checkCorruption(pool
, link
) ||
788 sjme_alloc_checkCorruption(pool
, link
->prev
) ||
789 sjme_alloc_checkCorruption(pool
, link
->next
) ||
790 sjme_alloc_checkCorruption(pool
, link
->freePrev
) ||
791 sjme_alloc_checkCorruption(pool
, link
->freeNext
))
792 return SJME_ERROR_MEMORY_CORRUPTION
;
794 /* We merged a block, so check again. */
795 return sjme_alloc_mergeFree(link
);
798 sjme_errorCode sjme_noOptimize
sjme_alloc_free(
799 sjme_attrInNotNull sjme_pointer addr
)
801 sjme_alloc_link
* link
;
802 volatile sjme_alloc_pool
* pool
;
803 sjme_errorCode error
;
804 sjme_alloc_weak weak
;
807 return SJME_ERROR_NULL_ARGUMENTS
;
810 sjme_thread_barrier();
814 if (sjme_error_is(error
= sjme_alloc_getLink(addr
, &link
)))
815 return sjme_error_default(error
);
817 /* Get the pool we are in. */
820 /* Take ownership of lock. */
821 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
823 return sjme_error_default(error
);
825 /* Check the integrity of the block before we free it. */
827 if (sjme_alloc_checkCorruption(pool
, link
))
829 error
= SJME_ERROR_MEMORY_CORRUPTION
;
833 /* If there is a weak reference, clear it. */
837 /* Call enqueue handler. */
838 if (weak
->enqueue
!= NULL
)
839 if (sjme_error_is(error
= weak
->enqueue(weak
,
840 weak
->enqueueData
, SJME_JNI_TRUE
)))
842 /* Keeping a weak reference is not considered an error */
843 /* although at this point it has no effect. */
844 if (error
!= SJME_ERROR_ENQUEUE_KEEP_WEAK
)
845 goto fail_weakEnqueueCall
;
848 /* Remove everything. */
851 weak
->pointer
= NULL
;
854 /* Mark block as free. */
855 link
->space
= SJME_ALLOC_POOL_SPACE_FREE
;
857 /* Clear flags, if any. */
860 /* Clear block memory so stale memory is not around. */
861 memset(&link
->block
[0], 0, link
->blockSize
);
863 /* Restore allocation size to block size. */
864 link
->allocSize
= link
->blockSize
;
866 #if defined(SJME_CONFIG_DEBUG)
867 /* Remove debug information. */
868 link
->debugFile
= NULL
;
870 link
->debugFunction
= NULL
;
873 /* Link into free chain. */
874 link
->freeNext
= pool
->freeFirstLink
->freeNext
;
875 pool
->freeFirstLink
->freeNext
= link
;
876 link
->freeNext
->freePrev
= link
;
877 link
->freePrev
= pool
->freeFirstLink
;
879 /* Merge together free blocks. */
880 if (sjme_error_is(error
= sjme_alloc_mergeFree(link
)))
884 sjme_thread_barrier();
886 /* Release ownership of lock. */
887 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
888 &pool
->spinLock
, NULL
)))
889 return sjme_error_default(error
);
892 return SJME_ERROR_NONE
;
894 /* Release ownership of lock. */
896 fail_weakEnqueueCall
:
898 if (sjme_error_is(sjme_thread_spinLockRelease(
899 &pool
->spinLock
, NULL
)))
900 return sjme_error_default(error
);
902 return sjme_error_default(error
);
905 sjme_errorCode
sjme_alloc_getLink(
906 sjme_attrInNotNull sjme_pointer addr
,
907 sjme_attrOutNotNull sjme_alloc_link
** outLink
)
909 return sjme_alloc_getLinkOptional(addr
, outLink
,
913 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_realloc
)(
914 sjme_attrInOutNotNull sjme_pointer
* inOutAddr
,
915 sjme_attrInPositive sjme_jint newSize
916 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
918 sjme_alloc_link
* link
;
922 sjme_errorCode error
;
924 if (inOutAddr
== NULL
|| *inOutAddr
== NULL
)
925 return SJME_ERROR_NULL_ARGUMENTS
;
928 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
931 sjme_thread_barrier();
933 /* Alias for free. */
937 /* Just do a normal free of it since zero was requested. */
938 if (sjme_error_is(error
= sjme_alloc_free(source
)))
939 return sjme_error_default(error
);
945 return SJME_ERROR_NULL_ARGUMENTS
;
948 /* Recover the link. */
950 if (sjme_error_is(error
= sjme_alloc_getLink(source
,
951 &link
)) || link
== NULL
)
952 return sjme_error_default(error
);
954 /* If there is a weak reference, then we cannot touch this. */
955 if (link
->weak
!= NULL
)
956 return SJME_ERROR_WEAK_REFERENCE_ATTACHED
;
958 /* Pointless operation. */
959 if (newSize
== link
->allocSize
)
960 return SJME_ERROR_NONE
;
962 /* There are some padding bytes we can consume. */
963 else if (newSize
> link
->allocSize
&& newSize
< link
->blockSize
)
965 /* Just set the new allocation size. */
966 link
->allocSize
= newSize
;
969 sjme_thread_barrier();
972 return SJME_ERROR_NONE
;
975 /* No space to grow or shrink, move it. */
978 /* How much do we actually want to copy? */
979 if (newSize
< link
->allocSize
)
982 limit
= link
->allocSize
;
985 sjme_message("Realloc copy %d -> %d (%d)",
986 link
->allocSize
, newSize
, limit
);
988 /* Allocate new block. */
990 if (sjme_error_is(error
= SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
991 link
->pool
, newSize
, &result
992 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) ||
994 return sjme_error_defaultOr(error
,
995 sjme_error_outOfMemory(link
->pool
, limit
));
997 /* Copy all the data over. */
998 memmove(result
, source
, limit
);
1000 /* Free the old block. */
1001 if (sjme_error_is(error
= sjme_alloc_free(source
)))
1002 return sjme_error_default(error
);
1005 sjme_thread_barrier();
1008 *inOutAddr
= result
;
1009 return SJME_ERROR_NONE
;
1013 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_strdup
)(
1014 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
1015 sjme_attrOutNotNull sjme_lpcstr
* outString
,
1016 sjme_attrInNotNull sjme_lpcstr stringToCopy
1017 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
1021 if (inPool
== NULL
|| outString
== NULL
|| stringToCopy
== NULL
)
1022 return SJME_ERROR_NULL_ARGUMENTS
;
1024 /* Use standard string length, include NUL. */
1025 charLen
= strlen(stringToCopy
) + 1;
1027 /* Then just forward to copy. */
1028 #if defined(SJME_CONFIG_DEBUG)
1029 return sjme_alloc_copyR(inPool
, charLen
,
1030 (sjme_pointer
*)outString
, (sjme_pointer
)stringToCopy
,
1033 return sjme_alloc_copy(inPool
, charLen
,
1034 (sjme_pointer
*)outString
, (sjme_pointer
)stringToCopy
);
1038 sjme_errorCode sjme_noOptimize
sjme_alloc_weakDelete(
1039 sjme_attrInOutNotNull sjme_alloc_weak
* inOutWeak
)
1041 sjme_errorCode error
;
1042 sjme_alloc_weak weak
;
1044 sjme_jboolean keepWeak
;
1045 sjme_alloc_link
* link
;
1048 if (inOutWeak
== NULL
)
1049 return SJME_ERROR_NULL_ARGUMENTS
;
1051 /* Operate on this weak. */
1056 return SJME_ERROR_NONE
;
1059 sjme_thread_barrier();
1061 /* Get the current count. */
1062 count
= sjme_atomic_sjme_jint_get(&weak
->count
);
1064 /* If zero is reached, it is eligible for free. */
1065 /* Provided, the data is still there. */
1067 block
= weak
->pointer
;
1068 if (count
<= 1 && link
!= NULL
&& block
!= NULL
)
1070 /* Call enqueue handler if it exists. */
1071 keepWeak
= SJME_JNI_FALSE
;
1072 if (weak
->enqueue
!= NULL
)
1073 if (sjme_error_is(error
= weak
->enqueue(weak
,
1074 weak
->enqueueData
, SJME_JNI_FALSE
)))
1076 /* Only fail if we are not keeping it. */
1077 keepWeak
= (error
== SJME_ERROR_ENQUEUE_KEEP_WEAK
);
1079 return sjme_error_default(error
);
1082 /* Clear weak count to zero. */
1083 sjme_atomic_sjme_jint_set(&weak
->count
, 0);
1085 /* Unlink weak reference from block. */
1086 /* So that enqueue is not called multiple times. */
1089 weak
->pointer
= NULL
;
1091 /* Free the block we point to. */
1092 if (sjme_error_is(error
= sjme_alloc_free(block
)))
1093 return sjme_error_default(error
);
1095 /* Delete the weak reference as well? */
1098 /* Inform our pointer that it is gone. */
1101 /* Free the weak reference itself. */
1102 if (sjme_error_is(error
= sjme_alloc_free(weak
)))
1103 return sjme_error_default(error
);
1107 /* Otherwise, just count it down. */
1109 sjme_atomic_sjme_jint_set(&weak
->count
, count
- 1);
1112 sjme_thread_barrier();
1115 return SJME_ERROR_NONE
;
1118 sjme_errorCode
sjme_alloc_weakGetPointer(
1119 sjme_attrInNotNull sjme_alloc_weak inWeak
,
1120 sjme_attrOutNotNull sjme_pointer
* outPointer
)
1122 if (inWeak
== NULL
|| outPointer
== NULL
)
1123 return SJME_ERROR_NULL_ARGUMENTS
;
1126 sjme_thread_barrier();
1128 if (inWeak
->link
== NULL
|| inWeak
->pointer
== NULL
)
1131 *outPointer
= inWeak
->pointer
;
1134 sjme_thread_barrier();
1137 return SJME_ERROR_NONE
;
1140 static sjme_errorCode sjme_noOptimize
sjme_alloc_weakRefInternal(
1141 sjme_attrInNotNull sjme_pointer addr
,
1142 sjme_attrOutNotNull sjme_alloc_weak
* outWeak
,
1143 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
1144 sjme_attrInNullable sjme_pointer inEnqueueData
)
1146 sjme_errorCode error
;
1147 sjme_alloc_link
* link
;
1148 sjme_alloc_weak result
;
1150 if (addr
== NULL
|| outWeak
== NULL
||
1151 (inEnqueue
== NULL
&& inEnqueueData
!= NULL
))
1152 return SJME_ERROR_NULL_ARGUMENTS
;
1155 sjme_thread_barrier();
1157 /* Recover the link. */
1159 if (sjme_error_is(error
= sjme_alloc_getLink(addr
,
1160 &link
)) || link
== NULL
)
1161 return sjme_error_default(error
);
1163 /* Is there already a weak reference? */
1164 result
= link
->weak
;
1167 /* Enqueue can be set, but not overwritten. */
1168 if (inEnqueue
!= NULL
)
1171 if (result
->enqueue
== NULL
)
1173 result
->enqueue
= inEnqueue
;
1174 result
->enqueueData
= inEnqueueData
;
1177 /* Must be the same function. */
1178 else if (result
->enqueue
!= inEnqueue
||
1179 result
->enqueueData
!= inEnqueueData
)
1180 return SJME_ERROR_ENQUEUE_ALREADY_SET
;
1184 sjme_atomic_sjme_jint_getAdd(&result
->count
, 1);
1187 sjme_thread_barrier();
1191 return SJME_ERROR_NONE
;
1194 /* We need to allocate the link. */
1195 if (sjme_error_is(error
= sjme_alloc(link
->pool
, sizeof(*result
),
1196 (sjme_pointer
*)&result
)))
1197 return sjme_error_default(error
);
1199 /* Setup link information. */
1200 sjme_atomic_sjme_jint_set(&result
->valid
,
1201 SJME_ALLOC_WEAK_VALID
);
1202 result
->link
= link
;
1203 result
->pointer
= addr
;
1204 result
->enqueue
= inEnqueue
;
1205 result
->enqueueData
= inEnqueueData
;
1206 sjme_atomic_sjme_jint_set(&result
->count
, 1);
1208 /* Join link back to this. */
1209 link
->weak
= result
;
1212 sjme_thread_barrier();
1216 return SJME_ERROR_NONE
;
1219 sjme_errorCode sjme_noOptimize
SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew
)(
1220 sjme_attrInNotNull
volatile sjme_alloc_pool
* inPool
,
1221 sjme_attrInPositiveNonZero sjme_jint size
,
1222 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
1223 sjme_attrInNullable sjme_pointer inEnqueueData
,
1224 sjme_attrOutNotNull sjme_pointer
* outAddr
,
1225 sjme_attrOutNotNull sjme_alloc_weak
* outWeak
1226 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
1228 sjme_pointer resultPtr
;
1229 sjme_alloc_weak resultWeak
;
1230 sjme_errorCode error
;
1232 if (inPool
== NULL
|| outAddr
== NULL
||
1233 (inEnqueueData
!= NULL
&& inEnqueue
== NULL
))
1234 return SJME_ERROR_NULL_ARGUMENTS
;
1236 /* Take ownership of lock. */
1237 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
1238 &inPool
->spinLock
)))
1239 return sjme_error_default(error
);
1242 sjme_thread_barrier();
1244 /* Attempt block allocation first. */
1246 #if defined(SJME_CONFIG_DEBUG)
1247 if (sjme_error_is(error
= sjme_allocR(inPool
, size
,
1248 (sjme_pointer
*)&resultPtr
, file
, line
, func
)) ||
1251 if (sjme_error_is(error
= sjme_alloc(inPool
, size
,
1252 (sjme_pointer
*)&resultPtr
)) ||
1255 goto fail_allocBlock
;
1257 /* Then create the weak reference. */
1259 if (sjme_error_is(error
= sjme_alloc_weakRefInternal(resultPtr
,
1260 &resultWeak
, inEnqueue
, inEnqueueData
)) || resultWeak
== NULL
)
1261 goto fail_allocWeak
;
1264 sjme_thread_barrier();
1266 /* Release ownership of lock. */
1267 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
1268 &inPool
->spinLock
, NULL
)))
1269 return sjme_error_default(error
);
1272 *outAddr
= resultPtr
;
1273 if (outWeak
!= NULL
)
1274 *outWeak
= resultWeak
;
1275 return SJME_ERROR_NONE
;
1279 if (resultPtr
!= NULL
)
1280 sjme_alloc_free(resultPtr
);
1282 /* Release ownership of lock. */
1283 if (sjme_error_is(sjme_thread_spinLockRelease(
1284 &inPool
->spinLock
, NULL
)))
1285 return sjme_error_default(error
);
1287 return sjme_error_default(error
);
1290 sjme_errorCode
sjme_alloc_weakRef(
1291 sjme_attrInNotNull sjme_pointer addr
,
1292 sjme_attrOutNotNull sjme_alloc_weak
* outWeak
,
1293 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
1294 sjme_attrInNullable sjme_pointer inEnqueueData
)
1296 volatile sjme_alloc_pool
* pool
;
1297 sjme_errorCode error
;
1298 sjme_alloc_link
* link
;
1300 if (addr
== NULL
|| outWeak
== NULL
||
1301 (inEnqueue
== NULL
&& inEnqueueData
!= NULL
))
1302 return SJME_ERROR_NULL_ARGUMENTS
;
1305 sjme_thread_barrier();
1307 /* Recover the link. */
1309 if (sjme_error_is(error
= sjme_alloc_getLink(addr
,
1310 &link
)) || link
== NULL
)
1311 return sjme_error_default(error
);
1313 /* No weak reference here? */
1314 if (link
->weak
== NULL
)
1315 return SJME_ERROR_NOT_WEAK_REFERENCE
;
1317 /* Take ownership of lock. */
1319 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
1321 return sjme_error_default(error
);
1324 error
= sjme_alloc_weakRefInternal(addr
, outWeak
, inEnqueue
,
1327 /* Release ownership of lock. */
1328 if (sjme_error_is(sjme_thread_spinLockRelease(
1329 &pool
->spinLock
, NULL
)))
1330 return sjme_error_default(error
);
1335 #if defined(SJME_CONFIG_DEBUG)
1337 sjme_errorCode
sjme_alloc_poolDump(
1338 sjme_attrInNotNull sjme_alloc_pool
* inPool
)
1340 sjme_alloc_link
* rover
;
1343 return SJME_ERROR_NULL_ARGUMENTS
;
1345 /* Dump information on every link. */
1346 for (rover
= inPool
->frontLink
; rover
!= NULL
; rover
= rover
->next
)
1348 sjme_messageR(NULL
, -1, NULL
,
1350 "Link %08p: %s %dB in %s (%s:%d)",
1352 (rover
->space
== SJME_ALLOC_POOL_SPACE_USED
? "USED" : "FREE"),
1354 rover
->debugFunction
,
1359 return SJME_ERROR_NONE
;