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_OUT_OF_MEMORY
;
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_poolDestroy(
395 sjme_attrOutNotNull sjme_alloc_pool
* inPool
)
398 return SJME_ERROR_NULL_ARGUMENTS
;
400 return sjme_error_notImplemented(0);
403 sjme_errorCode
sjme_alloc_poolSpaceTotalSize(
404 sjme_attrInNotNull
const sjme_alloc_pool
* pool
,
405 sjme_attrOutNullable sjme_jint
* outTotal
,
406 sjme_attrOutNullable sjme_jint
* outReserved
,
407 sjme_attrOutNullable sjme_jint
* outUsable
)
414 return SJME_ERROR_NULL_ARGUMENTS
;
416 if (outTotal
== NULL
&& outReserved
== NULL
&& outUsable
== NULL
)
417 return SJME_ERROR_NULL_ARGUMENTS
;
419 /* Run through and tally values for each space. */
422 for (i
= 0; i
< SJME_NUM_ALLOC_POOL_SPACE
; i
++)
424 reserved
+= pool
->space
[i
].reserved
;
425 usable
+= pool
->space
[i
].usable
;
428 /* Total space is both. */
429 total
= reserved
+ usable
;
431 /* Store output values. */
432 if (outTotal
!= NULL
)
434 if (outReserved
!= NULL
)
435 *outReserved
= reserved
;
436 if (outUsable
!= NULL
)
440 return SJME_ERROR_NONE
;
443 sjme_errorCode sjme_noOptimize
SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
444 sjme_attrInNotNull
volatile sjme_alloc_pool
* pool
,
445 sjme_attrInPositiveNonZero sjme_jint size
,
446 sjme_attrOutNotNull sjme_pointer
* outAddr
447 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
449 sjme_errorCode error
;
450 sjme_alloc_link
* scanLink
;
451 sjme_alloc_link
* rightLink
;
452 sjme_jint splitMinSize
, roundSize
;
453 sjme_jboolean splitBlock
;
454 sjme_alloc_pool
* nextPool
;
455 volatile sjme_alloc_link
* nextFree
;
457 if (pool
== NULL
|| size
<= 0 || outAddr
== NULL
)
458 return SJME_ERROR_NULL_ARGUMENTS
;
460 #if defined(SJME_CONFIG_DEBUG)
461 if ((size
* 8) == SJME_CONFIG_HAS_POINTER
)
462 sjme_message("Alloc of single pointer in %s (%s:%d).",
466 /* Determine the size this will actually take up, which includes the */
467 /* link to be created following this. */
468 roundSize
= (((size
& 7) != 0) ? ((size
| 7) + 1) : size
);
469 splitMinSize
= roundSize
+
470 (sjme_jint
)SJME_SIZEOF_ALLOC_LINK(SJME_ALLOC_SPLIT_MIN_SIZE
) +
471 (sjme_jint
)SJME_SIZEOF_ALLOC_LINK(0);
472 if (size
> splitMinSize
|| splitMinSize
< 0)
473 return SJME_ERROR_INVALID_ARGUMENT
;
475 /* Take ownership of lock. */
476 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
478 return sjme_error_default(error
);
481 sjme_thread_barrier();
483 /* Find the first free link that this fits in. */
485 splitBlock
= SJME_JNI_FALSE
;
486 for (scanLink
= pool
->freeFirstLink
;
487 scanLink
!= NULL
; scanLink
= scanLink
->freeNext
)
489 /* Has memory been corrupted? */
490 nextFree
= scanLink
->freeNext
;
491 if (sjme_alloc_checkCorruption(pool
, scanLink
) ||
493 sjme_alloc_checkCorruption(pool
, nextFree
)))
495 error
= SJME_ERROR_MEMORY_CORRUPTION
;
499 /* Block is in the "invalid" space, skip it. */
500 if (scanLink
->space
== SJME_NUM_ALLOC_POOL_SPACE
)
503 /* Block fits perfectly here, without needing a split? */
504 if (scanLink
->blockSize
== roundSize
)
507 /* Block fits here when split, try to not split ridiculously small. */
508 if (scanLink
->blockSize
>= splitMinSize
)
510 splitBlock
= SJME_JNI_TRUE
;
516 if (scanLink
== NULL
)
518 /* If there is an adjacent pool, if allocation fails then we shall */
519 /* try the next pool, this means multiple pools can work together */
521 nextPool
= pool
->nextPool
;
522 if (nextPool
!= NULL
)
524 /* Release ownership of lock. */
525 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
526 &pool
->spinLock
, NULL
)))
527 return sjme_error_default(error
);
529 #if defined(SJME_CONFIG_DEBUG)
530 return sjme_allocR(nextPool
, size
, outAddr
,
533 return sjme_alloc(pool
->nextPool
, size
, outAddr
);
537 /* Otherwise fail! */
538 error
= SJME_ERROR_OUT_OF_MEMORY
;
544 sjme_message("Found link at %p: %d bytes, we need %d with split %d.",
545 scanLink
, (int)scanLink
->blockSize
, (int)roundSize
, (int)splitBlock
);
548 /* Does this block need to be split? */
551 /* Check for link corruption on the adjacent links. */
552 if (sjme_alloc_checkCorruption(pool
, scanLink
->next
) ||
553 sjme_alloc_checkCorruption(pool
, scanLink
->prev
) ||
554 sjme_alloc_checkCorruption(pool
, scanLink
->freeNext
) ||
555 sjme_alloc_checkCorruption(pool
, scanLink
->freePrev
))
557 error
= SJME_ERROR_MEMORY_CORRUPTION
;
561 /* Make it so this block can actually fit in here. */
562 rightLink
= (sjme_alloc_link
*)&scanLink
->block
[roundSize
];
564 /* Initialize block to remove any old data. */
565 memset(rightLink
, 0, sizeof(*rightLink
));
567 /* Guards for link. */
568 rightLink
->guardFront
= SJME_ALLOC_GUARD_FRONT
;
569 rightLink
->guardBack
= SJME_ALLOC_GUARD_BACK
;
571 /* Set the right link's pool accordingly. */
572 rightLink
->pool
= pool
;
574 /* Make sure this block is marked as free. */
575 rightLink
->space
= SJME_ALLOC_POOL_SPACE_FREE
;
577 /* Set size of the right link. */
578 rightLink
->blockSize
=
579 (sjme_jint
)((intptr_t)&scanLink
->block
[scanLink
->blockSize
] -
580 (intptr_t)&rightLink
->block
[0]);
581 rightLink
->allocSize
= rightLink
->blockSize
;
583 /* Link in physical links. */
584 rightLink
->next
= scanLink
->next
;
585 rightLink
->next
->prev
= rightLink
;
586 scanLink
->next
= rightLink
;
587 rightLink
->prev
= scanLink
;
589 /* Link in free links. */
590 rightLink
->freeNext
= scanLink
->freeNext
;
591 rightLink
->freeNext
->freePrev
= rightLink
;
592 scanLink
->freeNext
= rightLink
;
593 rightLink
->freePrev
= scanLink
;
595 /* Set size of the left block. */
596 scanLink
->blockSize
=
597 (sjme_jint
)((intptr_t)rightLink
- (intptr_t)&scanLink
->block
[0]);
598 scanLink
->allocSize
= scanLink
->blockSize
;
600 /* Adjust reserved and usable space. */
601 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
+=
602 SJME_SIZEOF_ALLOC_LINK(0);
603 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
-=
604 SJME_SIZEOF_ALLOC_LINK(0);
606 /* Make sure we did not cause corruption. */
607 if (sjme_alloc_checkCorruption(pool
, scanLink
) ||
608 sjme_alloc_checkCorruption(pool
, rightLink
))
610 error
= SJME_ERROR_MEMORY_CORRUPTION
;
615 /* Setup block information. */
616 scanLink
->space
= SJME_ALLOC_POOL_SPACE_USED
;
618 /* Unlink from free links. */
619 if (scanLink
->freeNext
!= NULL
)
620 scanLink
->freeNext
->freePrev
= scanLink
->freePrev
;
621 if (scanLink
->freePrev
!= NULL
)
622 scanLink
->freePrev
->freeNext
= scanLink
->freeNext
;
623 scanLink
->freePrev
= NULL
;
624 scanLink
->freeNext
= NULL
;
626 /* Use our given allocation size. */
627 scanLink
->allocSize
= size
;
629 /* Adjust space that can actually be used for data. */
630 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
-= scanLink
->blockSize
;
631 pool
->space
[SJME_ALLOC_POOL_SPACE_USED
].usable
+= scanLink
->blockSize
;
633 /* Since this block is claimed, the reserved space moves over. */
634 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
-=
635 SJME_SIZEOF_ALLOC_LINK(0);
636 pool
->space
[SJME_ALLOC_POOL_SPACE_USED
].reserved
+=
637 SJME_SIZEOF_ALLOC_LINK(0);
639 #if defined(SJME_CONFIG_DEBUG)
640 /* Set debug info. */
641 scanLink
->debugFile
= file
;
642 scanLink
->debugLine
= line
;
643 scanLink
->debugFunction
= func
;
646 /* Make sure we did not cause corruption. */
647 if (sjme_alloc_checkCorruption(pool
, scanLink
) ||
648 sjme_alloc_checkCorruption(pool
, scanLink
->prev
) ||
649 sjme_alloc_checkCorruption(pool
, scanLink
->next
))
651 error
= SJME_ERROR_MEMORY_CORRUPTION
;
656 sjme_thread_barrier();
658 /* Release ownership of lock. */
659 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
660 &pool
->spinLock
, NULL
)))
661 return sjme_error_default(error
);
663 /* Use the given link. */
664 *outAddr
= &scanLink
->block
[0];
665 return SJME_ERROR_NONE
;
669 /* Release ownership of lock before we leave. */
670 if (sjme_error_is(sjme_thread_spinLockRelease(
671 &pool
->spinLock
, NULL
)))
672 return sjme_error_default(error
);
674 return sjme_error_default(error
);
677 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_copy
)(
678 sjme_attrInNotNull
volatile sjme_alloc_pool
* pool
,
679 sjme_attrInPositiveNonZero sjme_jint size
,
680 sjme_attrOutNotNull sjme_pointer
* outAddr
,
681 sjme_attrInNotNull sjme_pointer inAddr
682 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
684 sjme_errorCode error
;
687 if (pool
== NULL
|| outAddr
== NULL
|| inAddr
== NULL
)
688 return SJME_ERROR_NULL_ARGUMENTS
;
690 /* Allocate new copy first. */
692 if (sjme_error_is(error
= SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
694 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) || dest
== NULL
)
695 return sjme_error_default(error
);
698 memmove(dest
, inAddr
, size
);
702 return SJME_ERROR_NONE
;
705 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_copyWeak
)(
706 sjme_attrInNotNull
volatile sjme_alloc_pool
* pool
,
707 sjme_attrInPositiveNonZero sjme_jint size
,
708 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
709 sjme_attrInNullable sjme_pointer inEnqueueData
,
710 sjme_attrOutNotNull sjme_pointer
* outAddr
,
711 sjme_attrInNotNull sjme_pointer inAddr
,
712 sjme_attrOutNullable sjme_alloc_weak
* outWeak
713 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
715 sjme_errorCode error
;
718 if (pool
== NULL
|| outAddr
== NULL
|| inAddr
== NULL
)
719 return SJME_ERROR_NULL_ARGUMENTS
;
721 /* Allocate new copy first. */
723 if (sjme_error_is(error
= SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew
)(
724 pool
, size
, inEnqueue
, inEnqueueData
, &dest
, outWeak
725 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) || dest
== NULL
)
726 return sjme_error_default(error
);
729 memmove(dest
, inAddr
, size
);
733 return SJME_ERROR_NONE
;
736 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_format
)(
737 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
738 sjme_attrOutNotNull sjme_lpstr
* outString
,
739 SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL SJME_DEBUG_ONLY_COMMA
740 sjme_attrInNotNull sjme_attrFormatArg
const char* format
,
748 if (inPool
== NULL
|| outString
== NULL
|| format
== NULL
)
749 return SJME_ERROR_NULL_ARGUMENTS
;
751 /* Start variable arguments. */
752 va_start(arg
, format
);
754 /* Format string to the buffer. */
755 memset(buf
, 0, sizeof(buf
));
756 vsnprintf(buf
, BUF_SIZE
- 1, format
, arg
);
758 /* Force to end with a NUL. */
759 buf
[BUF_SIZE
- 1] = 0;
764 /* Calculate length of string for copying. */
768 return SJME_DEBUG_IDENTIFIER(sjme_alloc_copy
)(inPool
, len
+ 1,
769 (sjme_pointer
*)outString
, buf
770 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
);
774 static sjme_errorCode
sjme_alloc_mergeFree(sjme_alloc_link
* link
)
776 sjme_alloc_pool
* pool
;
777 sjme_alloc_link
* right
;
778 sjme_alloc_link
* oldRightFreeNext
;
779 sjme_alloc_link
* rightRight
;
780 sjme_alloc_link
* checkLeft
;
784 return SJME_ERROR_NULL_ARGUMENTS
;
786 /* Need pool for all operations. */
789 /* If the previous block is free, pivot to there. */
790 checkLeft
= link
->prev
;
791 if (checkLeft
->space
== SJME_ALLOC_POOL_SPACE_FREE
)
792 return sjme_alloc_mergeFree(checkLeft
);
794 /* Is the block on the right a candidate for merge? */
796 if (right
->space
!= SJME_ALLOC_POOL_SPACE_FREE
)
797 return SJME_ERROR_NONE
;
799 /* We need the block after to relink. */
800 rightRight
= right
->next
;
802 /* Disconnect in the middle. */
803 link
->next
= rightRight
;
804 rightRight
->prev
= link
;
806 /* Remove from the free chain. */
807 oldRightFreeNext
= right
->freeNext
;
808 right
->freePrev
->freeNext
= right
->freeNext
;
809 oldRightFreeNext
->freePrev
= right
->freePrev
;
811 /* Reclaim the right link data area. */
812 addedSize
= right
->blockSize
+ SJME_SIZEOF_ALLOC_LINK(0);
813 link
->blockSize
+= addedSize
;
815 /* Update pool sizes. */
816 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].usable
+= addedSize
;
817 pool
->space
[SJME_ALLOC_POOL_SPACE_FREE
].reserved
-=
818 SJME_SIZEOF_ALLOC_LINK(0);
820 /* Synchronize allocation size. */
821 link
->allocSize
= link
->blockSize
;
823 /* Wipe next side block to remove any stale data. */
824 memset(right
, 0, sizeof(*right
));
826 /* Should not have corrupted the block. */
827 if (sjme_alloc_checkCorruption(pool
, link
) ||
828 sjme_alloc_checkCorruption(pool
, link
->prev
) ||
829 sjme_alloc_checkCorruption(pool
, link
->next
) ||
830 sjme_alloc_checkCorruption(pool
, link
->freePrev
) ||
831 sjme_alloc_checkCorruption(pool
, link
->freeNext
))
832 return SJME_ERROR_MEMORY_CORRUPTION
;
834 /* We merged a block, so check again. */
835 return sjme_alloc_mergeFree(link
);
838 sjme_errorCode sjme_noOptimize
sjme_alloc_free(
839 sjme_attrInNotNull sjme_pointer addr
)
841 sjme_alloc_link
* link
;
842 volatile sjme_alloc_pool
* pool
;
843 sjme_errorCode error
;
844 sjme_alloc_weak weak
;
847 return SJME_ERROR_NULL_ARGUMENTS
;
850 sjme_thread_barrier();
854 if (sjme_error_is(error
= sjme_alloc_getLink(addr
, &link
)))
855 return sjme_error_default(error
);
857 /* Get the pool we are in. */
860 /* Take ownership of lock. */
861 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
863 return sjme_error_default(error
);
865 /* Check the integrity of the block before we free it. */
867 if (sjme_alloc_checkCorruption(pool
, link
))
869 error
= SJME_ERROR_MEMORY_CORRUPTION
;
873 /* If there is a weak reference, clear it. */
877 /* Call enqueue handler. */
878 if (weak
->enqueue
!= NULL
)
879 if (sjme_error_is(error
= weak
->enqueue(weak
,
880 weak
->enqueueData
, SJME_JNI_TRUE
)))
882 /* Keeping a weak reference is not considered an error */
883 /* although at this point it has no effect. */
884 if (error
!= SJME_ERROR_ENQUEUE_KEEP_WEAK
)
885 goto fail_weakEnqueueCall
;
888 /* Remove everything. */
891 weak
->pointer
= NULL
;
894 /* Mark block as free. */
895 link
->space
= SJME_ALLOC_POOL_SPACE_FREE
;
897 /* Clear flags, if any. */
900 /* Clear block memory so stale memory is not around. */
901 memset(&link
->block
[0], 0, link
->blockSize
);
903 /* Restore allocation size to block size. */
904 link
->allocSize
= link
->blockSize
;
906 #if defined(SJME_CONFIG_DEBUG)
907 /* Remove debug information. */
908 link
->debugFile
= NULL
;
910 link
->debugFunction
= NULL
;
913 /* Link into free chain. */
914 link
->freeNext
= pool
->freeFirstLink
->freeNext
;
915 pool
->freeFirstLink
->freeNext
= link
;
916 link
->freeNext
->freePrev
= link
;
917 link
->freePrev
= pool
->freeFirstLink
;
919 /* Merge together free blocks. */
920 if (sjme_error_is(error
= sjme_alloc_mergeFree(link
)))
924 sjme_thread_barrier();
926 /* Release ownership of lock. */
927 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
928 &pool
->spinLock
, NULL
)))
929 return sjme_error_default(error
);
932 return SJME_ERROR_NONE
;
934 /* Release ownership of lock. */
936 fail_weakEnqueueCall
:
938 if (sjme_error_is(sjme_thread_spinLockRelease(
939 &pool
->spinLock
, NULL
)))
940 return sjme_error_default(error
);
942 return sjme_error_default(error
);
945 sjme_errorCode
sjme_alloc_getLink(
946 sjme_attrInNotNull sjme_pointer addr
,
947 sjme_attrOutNotNull sjme_alloc_link
** outLink
)
949 return sjme_alloc_getLinkOptional(addr
, outLink
,
953 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_realloc
)(
954 sjme_attrInOutNotNull sjme_pointer
* inOutAddr
,
955 sjme_attrInPositive sjme_jint newSize
956 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
958 sjme_alloc_link
* link
;
962 sjme_errorCode error
;
964 if (inOutAddr
== NULL
|| *inOutAddr
== NULL
)
965 return SJME_ERROR_NULL_ARGUMENTS
;
968 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
971 sjme_thread_barrier();
973 /* Alias for free. */
977 /* Just do a normal free of it since zero was requested. */
978 if (sjme_error_is(error
= sjme_alloc_free(source
)))
979 return sjme_error_default(error
);
985 return SJME_ERROR_NULL_ARGUMENTS
;
988 /* Recover the link. */
990 if (sjme_error_is(error
= sjme_alloc_getLink(source
,
991 &link
)) || link
== NULL
)
992 return sjme_error_default(error
);
994 /* If there is a weak reference, then we cannot touch this. */
995 if (link
->weak
!= NULL
)
996 return SJME_ERROR_WEAK_REFERENCE_ATTACHED
;
998 /* Pointless operation. */
999 if (newSize
== link
->allocSize
)
1000 return SJME_ERROR_NONE
;
1002 /* There are some padding bytes we can consume. */
1003 else if (newSize
> link
->allocSize
&& newSize
< link
->blockSize
)
1005 /* Just set the new allocation size. */
1006 link
->allocSize
= newSize
;
1009 sjme_thread_barrier();
1012 return SJME_ERROR_NONE
;
1015 /* No space to grow or shrink, move it. */
1018 /* How much do we actually want to copy? */
1019 if (newSize
< link
->allocSize
)
1022 limit
= link
->allocSize
;
1025 sjme_message("Realloc copy %d -> %d (%d)",
1026 link
->allocSize
, newSize
, limit
);
1028 /* Allocate new block. */
1030 if (sjme_error_is(error
= SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
1031 link
->pool
, newSize
, &result
1032 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) ||
1034 return sjme_error_defaultOr(error
,
1035 SJME_ERROR_OUT_OF_MEMORY
);
1037 /* Copy all the data over. */
1038 memmove(result
, source
, limit
);
1040 /* Free the old block. */
1041 if (sjme_error_is(error
= sjme_alloc_free(source
)))
1042 return sjme_error_default(error
);
1045 sjme_thread_barrier();
1048 *inOutAddr
= result
;
1049 return SJME_ERROR_NONE
;
1053 sjme_errorCode
SJME_DEBUG_IDENTIFIER(sjme_alloc_strdup
)(
1054 sjme_attrInNotNull sjme_alloc_pool
* inPool
,
1055 sjme_attrOutNotNull sjme_lpcstr
* outString
,
1056 sjme_attrInNotNull sjme_lpcstr stringToCopy
1057 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
1061 if (inPool
== NULL
|| outString
== NULL
|| stringToCopy
== NULL
)
1062 return SJME_ERROR_NULL_ARGUMENTS
;
1064 /* Use standard string length, include NUL. */
1065 charLen
= strlen(stringToCopy
) + 1;
1067 /* Then just forward to copy. */
1068 #if defined(SJME_CONFIG_DEBUG)
1069 return sjme_alloc_copyR(inPool
, charLen
,
1070 outString
, stringToCopy
,
1073 return sjme_alloc_copy(inPool
, charLen
,
1074 outString
, stringToCopy
);
1078 sjme_errorCode sjme_noOptimize
sjme_alloc_weakDelete(
1079 sjme_attrInOutNotNull sjme_alloc_weak
* inOutWeak
)
1081 sjme_errorCode error
;
1082 sjme_alloc_weak weak
;
1084 sjme_jboolean keepWeak
;
1085 sjme_alloc_link
* link
;
1088 if (inOutWeak
== NULL
)
1089 return SJME_ERROR_NULL_ARGUMENTS
;
1091 /* Operate on this weak. */
1096 return SJME_ERROR_NONE
;
1099 sjme_thread_barrier();
1101 /* Get the current count. */
1102 count
= sjme_atomic_sjme_jint_get(&weak
->count
);
1104 /* If zero is reached, it is eligible for free. */
1105 /* Provided, the data is still there. */
1107 block
= weak
->pointer
;
1108 if (count
<= 1 && link
!= NULL
&& block
!= NULL
)
1110 /* Call enqueue handler if it exists. */
1111 keepWeak
= SJME_JNI_FALSE
;
1112 if (weak
->enqueue
!= NULL
)
1113 if (sjme_error_is(error
= weak
->enqueue(weak
,
1114 weak
->enqueueData
, SJME_JNI_FALSE
)))
1116 /* Only fail if we are not keeping it. */
1117 keepWeak
= (error
== SJME_ERROR_ENQUEUE_KEEP_WEAK
);
1119 return sjme_error_default(error
);
1122 /* Clear weak count to zero. */
1123 sjme_atomic_sjme_jint_set(&weak
->count
, 0);
1125 /* Unlink weak reference from block. */
1126 /* So that enqueue is not called multiple times. */
1129 weak
->pointer
= NULL
;
1131 /* Free the block we point to. */
1132 if (sjme_error_is(error
= sjme_alloc_free(block
)))
1133 return sjme_error_default(error
);
1135 /* Delete the weak reference as well? */
1138 /* Inform our pointer that it is gone. */
1141 /* Free the weak reference itself. */
1142 if (sjme_error_is(error
= sjme_alloc_free(weak
)))
1143 return sjme_error_default(error
);
1147 /* Otherwise, just count it down. */
1149 sjme_atomic_sjme_jint_set(&weak
->count
, count
- 1);
1152 sjme_thread_barrier();
1155 return SJME_ERROR_NONE
;
1158 sjme_errorCode
sjme_alloc_weakGetPointer(
1159 sjme_attrInNotNull sjme_alloc_weak inWeak
,
1160 sjme_attrOutNotNull sjme_pointer
* outPointer
)
1162 if (inWeak
== NULL
|| outPointer
== NULL
)
1163 return SJME_ERROR_NULL_ARGUMENTS
;
1166 sjme_thread_barrier();
1168 if (inWeak
->link
== NULL
|| inWeak
->pointer
== NULL
)
1171 *outPointer
= inWeak
->pointer
;
1174 sjme_thread_barrier();
1177 return SJME_ERROR_NONE
;
1180 static sjme_errorCode sjme_noOptimize
sjme_alloc_weakRefInternal(
1181 sjme_attrInNotNull sjme_pointer addr
,
1182 sjme_attrOutNullable sjme_alloc_weak
* outWeak
,
1183 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
1184 sjme_attrInNullable sjme_pointer inEnqueueData
)
1186 sjme_errorCode error
;
1187 sjme_alloc_link
* link
;
1188 sjme_alloc_weak result
;
1190 if (addr
== NULL
|| outWeak
== NULL
||
1191 (inEnqueue
== NULL
&& inEnqueueData
!= NULL
))
1192 return SJME_ERROR_NULL_ARGUMENTS
;
1195 sjme_thread_barrier();
1197 /* Recover the link. */
1199 if (sjme_error_is(error
= sjme_alloc_getLink(addr
,
1200 &link
)) || link
== NULL
)
1201 return sjme_error_default(error
);
1203 /* Is there already a weak reference? */
1204 result
= link
->weak
;
1207 /* Enqueue can be set, but not overwritten. */
1208 if (inEnqueue
!= NULL
)
1211 if (result
->enqueue
== NULL
)
1213 result
->enqueue
= inEnqueue
;
1214 result
->enqueueData
= inEnqueueData
;
1217 /* Must be the same function. */
1218 else if (result
->enqueue
!= inEnqueue
||
1219 result
->enqueueData
!= inEnqueueData
)
1220 return SJME_ERROR_ENQUEUE_ALREADY_SET
;
1224 sjme_atomic_sjme_jint_getAdd(&result
->count
, 1);
1227 sjme_thread_barrier();
1231 return SJME_ERROR_NONE
;
1234 /* We need to allocate the link. */
1235 if (sjme_error_is(error
= sjme_alloc(link
->pool
, sizeof(*result
),
1237 return sjme_error_default(error
);
1239 /* Setup link information. */
1240 sjme_atomic_sjme_jint_set(&result
->valid
,
1241 SJME_ALLOC_WEAK_VALID
);
1242 result
->link
= link
;
1243 result
->pointer
= addr
;
1244 result
->enqueue
= inEnqueue
;
1245 result
->enqueueData
= inEnqueueData
;
1246 sjme_atomic_sjme_jint_set(&result
->count
, 1);
1248 /* Join link back to this. */
1249 link
->weak
= result
;
1252 sjme_thread_barrier();
1256 return SJME_ERROR_NONE
;
1259 sjme_errorCode sjme_noOptimize
SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew
)(
1260 sjme_attrInNotNull
volatile sjme_alloc_pool
* inPool
,
1261 sjme_attrInPositiveNonZero sjme_jint size
,
1262 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
1263 sjme_attrInNullable sjme_pointer inEnqueueData
,
1264 sjme_attrOutNotNull sjme_pointer
* outAddr
,
1265 sjme_attrOutNullable sjme_alloc_weak
* outWeak
1266 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL
)
1268 sjme_pointer resultPtr
;
1269 sjme_alloc_weak resultWeak
;
1270 sjme_errorCode error
;
1272 if (inPool
== NULL
|| outAddr
== NULL
||
1273 (inEnqueueData
!= NULL
&& inEnqueue
== NULL
))
1274 return SJME_ERROR_NULL_ARGUMENTS
;
1276 /* Take ownership of lock. */
1277 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
1278 &inPool
->spinLock
)))
1279 return sjme_error_default(error
);
1282 sjme_thread_barrier();
1284 /* Attempt block allocation first. */
1286 #if defined(SJME_CONFIG_DEBUG)
1287 if (sjme_error_is(error
= sjme_allocR(inPool
, size
,
1288 &resultPtr
, file
, line
, func
)) ||
1291 if (sjme_error_is(error
= sjme_alloc(inPool
, size
,
1295 goto fail_allocBlock
;
1297 /* Then create the weak reference. */
1299 if (sjme_error_is(error
= sjme_alloc_weakRefInternal(resultPtr
,
1300 &resultWeak
, inEnqueue
, inEnqueueData
)) || resultWeak
== NULL
)
1301 goto fail_allocWeak
;
1304 sjme_thread_barrier();
1306 /* Release ownership of lock. */
1307 if (sjme_error_is(error
= sjme_thread_spinLockRelease(
1308 &inPool
->spinLock
, NULL
)))
1309 return sjme_error_default(error
);
1312 *outAddr
= resultPtr
;
1313 if (outWeak
!= NULL
)
1314 *outWeak
= resultWeak
;
1315 return SJME_ERROR_NONE
;
1319 if (resultPtr
!= NULL
)
1320 sjme_alloc_free(resultPtr
);
1322 /* Release ownership of lock. */
1323 if (sjme_error_is(sjme_thread_spinLockRelease(
1324 &inPool
->spinLock
, NULL
)))
1325 return sjme_error_default(error
);
1327 return sjme_error_default(error
);
1330 sjme_errorCode
sjme_alloc_weakRef(
1331 sjme_attrInNotNull sjme_pointer addr
,
1332 sjme_attrOutNullable sjme_alloc_weak
* outWeak
,
1333 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue
,
1334 sjme_attrInNullable sjme_pointer inEnqueueData
)
1336 volatile sjme_alloc_pool
* pool
;
1337 sjme_errorCode error
;
1338 sjme_alloc_link
* link
;
1340 if (addr
== NULL
|| outWeak
== NULL
||
1341 (inEnqueue
== NULL
&& inEnqueueData
!= NULL
))
1342 return SJME_ERROR_NULL_ARGUMENTS
;
1345 sjme_thread_barrier();
1347 /* Recover the link. */
1349 if (sjme_error_is(error
= sjme_alloc_getLink(addr
,
1350 &link
)) || link
== NULL
)
1351 return sjme_error_default(error
);
1353 /* No weak reference here? */
1354 if (link
->weak
== NULL
)
1355 return SJME_ERROR_NOT_WEAK_REFERENCE
;
1357 /* Take ownership of lock. */
1359 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
1361 return sjme_error_default(error
);
1364 error
= sjme_alloc_weakRefInternal(addr
, outWeak
, inEnqueue
,
1367 /* Release ownership of lock. */
1368 if (sjme_error_is(sjme_thread_spinLockRelease(
1369 &pool
->spinLock
, NULL
)))
1370 return sjme_error_default(error
);