Compile fixes.
[SquirrelJME.git] / nanocoat / lib / base / alloc.c
blob3a8b85285e94f58a684b51c3074836a4eac08619
1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
3 // SquirrelJME
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 // -------------------------------------------------------------------------*/
10 #include <string.h>
11 #include <stdio.h>
13 /* Include Valgrind if it is available? */
14 #if defined(SJME_CONFIG_HAS_VALGRIND)
15 #include <valgrind.h>
16 #include <memcheck.h>
17 #endif
19 #include "sjme/alloc.h"
20 #include "sjme/debug.h"
21 #include "sjme/atomic.h"
22 #include "sjme/multithread.h"
23 #include "sjme/dylib.h"
25 /** The minimum size permitted for allocation pools. */
26 #define SJME_ALLOC_MIN_SIZE (((SJME_SIZEOF_ALLOC_POOL(0) + \
27 (SJME_SIZEOF_ALLOC_LINK(0) * 3)) | 0x1FF))
29 /** The minimum size for splits. */
30 #define SJME_ALLOC_SPLIT_MIN_SIZE 64
32 /** The front guard value. */
33 #define SJME_ALLOC_GUARD_FRONT INT32_C(0x53716B21)
35 /** The back guard value. */
36 #define SJME_ALLOC_GUARD_BACK INT32_C(0x6C65783F)
38 #if defined(SJME_CONFIG_DEBUG)
39 /**
40 * Prints information on a given link and returns.
42 * @param pool The pool this is in.
43 * @param atLink The link to print info for.
44 * @param trigger The trigger for the failure.
45 * @return Always @c SJME_JNI_TRUE .
46 * @since 2023/12/29
48 static sjme_inline sjme_jboolean sjme_alloc_corruptFail(
49 volatile sjme_alloc_pool* pool,
50 volatile sjme_alloc_link* atLink,
51 const char* trigger)
53 sjme_message("Corrupted Link %p: %s", atLink, trigger);
55 /* Ignore if null. */
56 if (atLink == NULL)
57 return SJME_JNI_TRUE;
59 /* Dump everything about the link. */
60 sjme_message("link->guardFront: %08x", atLink->guardFront);
61 sjme_message("link->pool: %p (should be %p)", atLink->pool, pool);
62 sjme_message("link->prev: %p", atLink->prev);
63 sjme_message("link->next: %p", atLink->next);
64 if (atLink->space == SJME_ALLOC_POOL_SPACE_USED)
65 sjme_message("link->space: USED");
66 else if (atLink->space == SJME_ALLOC_POOL_SPACE_FREE)
67 sjme_message("link->space: FREE");
68 else if (atLink->space == SJME_NUM_ALLOC_POOL_SPACE)
69 sjme_message("link->space: NUM");
70 else
71 sjme_message("link->space: %d", (int)atLink->space);
72 sjme_message("link->weak: %p", atLink->weak);
73 sjme_message("link->freePrev: %p", atLink->freePrev);
74 sjme_message("link->freeNext: %p", atLink->freeNext);
75 sjme_message("link->allocSize: %d", (int)atLink->allocSize);
76 sjme_message("link->blockSize: %d", (int)atLink->blockSize);
77 sjme_message("link->guardBack: %08x", atLink->guardBack);
79 /* Abort. */
80 if (sjme_debug_handlers != NULL && sjme_debug_handlers->abort != NULL)
81 sjme_debug_handlers->abort();
83 /* Always indicate failure here. */
84 return SJME_JNI_TRUE;
86 #else
87 /**
88 * Prints information on a given link and returns.
90 * @param pool The pool this is in.
91 * @param atLink The link to print info for.
92 * @param trigger The trigger for the failure.
93 * @return Always @c SJME_JNI_TRUE .
94 * @since 2023/12/29
96 #define sjme_alloc_corruptFail(pool, atLink, trigger) SJME_JNI_TRUE
97 #endif
99 static sjme_inline sjme_jboolean sjme_alloc_checkCorruptionRange(
100 sjme_alloc_pool* pool, uintptr_t poolStart, uintptr_t poolEnd,
101 sjme_alloc_link* atLink)
103 uintptr_t check;
105 /* Ignore null pointers. */
106 if (atLink == NULL)
107 return SJME_JNI_FALSE;
109 /* Nominal address of the check pointer. */
110 check = (uintptr_t)atLink;
112 /* Must be in range! */
113 if (check < poolStart || check >= poolEnd)
114 return sjme_alloc_corruptFail(pool, atLink,
115 "Out of range link");
117 /* Does not appear corrupt. */
118 return SJME_JNI_FALSE;
122 * Checks the integrity of the memory pool.
124 * @param pool The pool to check in.
125 * @param atLink The link of the pool.
126 * @return If there is corruption or not.
127 * @since 2023/12/29
129 static sjme_jboolean sjme_noOptimize sjme_alloc_checkCorruption(
130 volatile sjme_alloc_pool* pool,
131 volatile sjme_alloc_link* atLink)
133 uintptr_t poolStart, poolEnd;
135 if (pool == NULL)
136 return SJME_JNI_TRUE;
138 /* If no link is specified, ignore. */
139 if (atLink == NULL)
140 return SJME_JNI_FALSE;
142 /* Check front and back guards. */
143 if (atLink->guardFront != SJME_ALLOC_GUARD_FRONT)
144 return sjme_alloc_corruptFail(pool, atLink,
145 "Wrong front guard");
146 if (atLink->guardBack != SJME_ALLOC_GUARD_BACK)
147 return sjme_alloc_corruptFail(pool, atLink,
148 "Wrong back guard");
150 /* Link is in the wrong pool. */
151 if (atLink->pool != pool)
152 return sjme_alloc_corruptFail(pool, atLink,
153 "Wrong pool");
155 /* Allocation size larger than block? */
156 if (atLink->allocSize > atLink->blockSize)
157 return sjme_alloc_corruptFail(pool, atLink,
158 "Allocation size larger than block.");
160 /* Next link is in the wrong location? */
161 if (atLink->next != NULL && (uintptr_t)atLink->next !=
162 (uintptr_t)&atLink->block[atLink->blockSize])
163 return sjme_alloc_corruptFail(pool, atLink,
164 "Next not at block end");
166 /* Is front/end link? */
167 if (atLink == pool->frontLink || atLink == pool->backLink)
169 /* Link space incorrect? */
170 if (atLink->space != SJME_NUM_ALLOC_POOL_SPACE)
171 return sjme_alloc_corruptFail(pool, atLink,
172 "Front/Back link not in correct space");
174 /* Size is not zero? */
175 if (atLink->blockSize != 0 || atLink->allocSize != 0)
176 return sjme_alloc_corruptFail(pool, atLink,
177 "Front/back link sizes non-zero");
179 /* Does not appear corrupt. */
180 return SJME_JNI_FALSE;
183 /* Invalid block size? */
184 if (atLink->blockSize <= 0)
185 return sjme_alloc_corruptFail(pool, atLink,
186 "Zero or negative block size");
188 /* Used for checking the integrity of pointers. */
189 poolStart = (uintptr_t)pool;
190 poolEnd = (uintptr_t)&pool->block[pool->size];
192 /* Free link only. */
193 if (atLink->space == SJME_ALLOC_POOL_SPACE_FREE)
195 /* Check free links. */
196 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
197 atLink->freePrev))
198 return sjme_alloc_corruptFail(pool, atLink,
199 "Corrupt freePrev");
200 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
201 atLink->freeNext))
202 return sjme_alloc_corruptFail(pool, atLink,
203 "Corrupt freeNext");
206 /* Used link only. */
207 else if (atLink->space == SJME_ALLOC_POOL_SPACE_USED)
209 /* Zero or negative size. */
210 if (atLink->allocSize <= 0)
211 return sjme_alloc_corruptFail(pool, atLink,
212 "Zero/negative used allocSize");
214 /* Cannot have any free or previous links. */
215 if (atLink->freePrev != NULL || atLink->freeNext != NULL)
216 return sjme_alloc_corruptFail(pool, atLink,
217 "Used has free links");
220 /* Link space incorrect? */
221 else
222 return sjme_alloc_corruptFail(pool, atLink,
223 "Incorrect space");
225 /* Check common next links. */
226 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
227 atLink->prev))
228 return sjme_alloc_corruptFail(pool, atLink,
229 "Corrupt prev");
230 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
231 atLink->next))
232 return sjme_alloc_corruptFail(pool, atLink,
233 "Corrupt next");
235 /* Does not appear corrupt. */
236 return SJME_JNI_FALSE;
239 static sjme_errorCode sjme_alloc_getLinkOptional(
240 sjme_attrInNotNull sjme_pointer addr,
241 sjme_attrOutNotNull sjme_alloc_link** outLink,
242 sjme_attrInValue sjme_jboolean checkCorruption)
244 sjme_alloc_link* link;
246 if (addr == NULL || outLink == NULL)
247 return SJME_ERROR_NULL_ARGUMENTS;
249 /* Just need to do some reversing math. */
250 link = (sjme_alloc_link*)(((uintptr_t)addr) -
251 offsetof(sjme_alloc_link, block));
253 /* Check the integrity of the link. */
254 if (checkCorruption)
255 sjme_alloc_checkCorruption(link->pool, link);
257 /* Success! */
258 *outLink = link;
259 return SJME_ERROR_NONE;
262 sjme_errorCode sjme_noOptimize sjme_alloc_poolInitMalloc(
263 sjme_attrOutNotNull sjme_alloc_pool** outPool,
264 sjme_attrInPositive sjme_jint size)
266 sjme_pointer result;
267 sjme_jint useSize;
269 /* Make sure the size is not wonky. */
270 useSize = SJME_SIZEOF_ALLOC_POOL(size);
271 if (outPool == NULL || size <= SJME_ALLOC_MIN_SIZE || useSize <= 0 ||
272 size > useSize)
273 return SJME_ERROR_INVALID_ARGUMENT;
275 /* Attempt allocation. */
276 result = malloc(useSize);
277 if (!result)
278 return sjme_error_outOfMemory(NULL, useSize);
280 /* Use static pool initializer to set up structures. */
281 return sjme_alloc_poolInitStatic(outPool, result, useSize);
284 sjme_errorCode sjme_noOptimize sjme_alloc_poolInitStatic(
285 sjme_attrOutNotNull sjme_alloc_pool** outPool,
286 sjme_attrInNotNull sjme_pointer baseAddr,
287 sjme_attrInPositive sjme_jint size)
289 sjme_alloc_pool* pool;
290 sjme_alloc_link* frontLink;
291 sjme_alloc_link* midLink;
292 sjme_alloc_link* backLink;
293 sjme_alloc_link* specialParent;
295 if (outPool == NULL || baseAddr == NULL)
296 return SJME_ERROR_NULL_ARGUMENTS;
298 if (size <= SJME_ALLOC_MIN_SIZE)
299 return SJME_ERROR_INVALID_ARGUMENT;
301 /* Initialize memory to nothing. */
302 memset(baseAddr, 0, size);
304 /* Setup initial pool structure. */
305 pool = baseAddr;
306 pool->size = (size & (~7)) - SJME_SIZEOF_ALLOC_POOL(0);
308 /* Setup front link. */
309 frontLink = (sjme_pointer)&pool->block[0];
310 pool->frontLink = frontLink;
312 /* Setup back link. */
313 backLink = (sjme_pointer)&pool->block[pool->size -
314 SJME_SIZEOF_ALLOC_LINK(0)];
315 pool->backLink = backLink;
317 /* Setup middle link, which is between the two. */
318 midLink = (sjme_pointer)&frontLink->block[0];
319 midLink->prev = frontLink;
320 frontLink->next = midLink;
321 midLink->next = backLink;
322 backLink->prev = midLink;
324 /* Determine size of the middle link, which is free space. */
325 midLink->blockSize = (sjme_jint)((uintptr_t)backLink -
326 (uintptr_t)&midLink->block[0]);
328 /* The mid-link is considered free. */
329 midLink->space = SJME_ALLOC_POOL_SPACE_FREE;
331 /* The front and back links are in the "invalid" space. */
332 frontLink->space = SJME_NUM_ALLOC_POOL_SPACE;
333 backLink->space = SJME_NUM_ALLOC_POOL_SPACE;
335 /* Determine size that can and cannot be used. */
336 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved =
337 SJME_SIZEOF_ALLOC_LINK(0);
338 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable = midLink->blockSize;
340 /* Link in the first and last actual blocks for the free chain. */
341 pool->freeFirstLink = frontLink;
342 frontLink->freeNext = midLink;
343 midLink->freePrev = frontLink;
344 pool->freeLastLink = backLink;
345 backLink->freePrev = midLink;
346 midLink->freeNext = backLink;
348 /* Guards for all links. */
349 frontLink->guardFront = SJME_ALLOC_GUARD_FRONT;
350 frontLink->guardBack = SJME_ALLOC_GUARD_BACK;
351 midLink->guardFront = SJME_ALLOC_GUARD_FRONT;
352 midLink->guardBack = SJME_ALLOC_GUARD_BACK;
353 backLink->guardFront = SJME_ALLOC_GUARD_FRONT;
354 backLink->guardBack = SJME_ALLOC_GUARD_BACK;
356 /* Link in pools. */
357 frontLink->pool = pool;
358 midLink->pool = pool;
359 backLink->pool = pool;
361 #if defined(SJME_CONFIG_DEBUG)
362 /* Debug source line init blocks. */
363 pool->frontLink->debugFile = "<FRONT LINK>";
364 pool->frontLink->debugLine = 1;
365 pool->frontLink->debugFunction = "<FRONT LINK>";
367 pool->backLink->debugFile = "<BACK LINK>";
368 pool->backLink->debugLine = 1;
369 pool->backLink->debugFunction = "<BACK LINK>";
370 #endif
372 #if defined(SJME_CONFIG_HAS_VALGRIND)
373 /* Reserve front side in Valgrind. */
374 VALGRIND_MAKE_MEM_NOACCESS(baseAddr,
375 ((uintptr_t)&midLink->block[0] - (uintptr_t)baseAddr));
377 /* Reserve back side in Valgrind. */
378 VALGRIND_MAKE_MEM_NOACCESS(backLink,
379 (SJME_SIZEOF_ALLOC_LINK(0)));
380 #endif
382 /* If this is a valid link then we are allocating a nested pool. */
383 #if 0
384 specialParent = NULL;
385 if (!sjme_error_is(sjme_alloc_getLinkOptional(baseAddr,
386 &specialParent, SJME_JNI_FALSE)))
387 specialParent->flags |= SJME_ALLOC_LINK_FLAG_NESTED_POOL;
388 #endif
390 /* Use the pool. */
391 *outPool = pool;
392 return SJME_ERROR_NONE;
395 sjme_errorCode sjme_alloc_poolDestroy(
396 sjme_attrOutNotNull sjme_alloc_pool* inPool)
398 if (inPool == NULL)
399 return SJME_ERROR_NULL_ARGUMENTS;
401 return sjme_error_notImplemented(0);
404 sjme_errorCode sjme_alloc_poolSpaceTotalSize(
405 sjme_attrInNotNull const sjme_alloc_pool* pool,
406 sjme_attrOutNullable sjme_jint* outTotal,
407 sjme_attrOutNullable sjme_jint* outReserved,
408 sjme_attrOutNullable sjme_jint* outUsable)
410 sjme_jint total, i;
411 sjme_jint reserved;
412 sjme_jint usable;
414 if (pool == NULL)
415 return SJME_ERROR_NULL_ARGUMENTS;
417 if (outTotal == NULL && outReserved == NULL && outUsable == NULL)
418 return SJME_ERROR_NULL_ARGUMENTS;
420 /* Run through and tally values for each space. */
421 reserved = 0;
422 usable = 0;
423 for (i = 0; i < SJME_NUM_ALLOC_POOL_SPACE; i++)
425 reserved += pool->space[i].reserved;
426 usable += pool->space[i].usable;
429 /* Total space is both. */
430 total = reserved + usable;
432 /* Store output values. */
433 if (outTotal != NULL)
434 *outTotal = total;
435 if (outReserved != NULL)
436 *outReserved = reserved;
437 if (outUsable != NULL)
438 *outUsable = usable;
440 /* Success! */
441 return SJME_ERROR_NONE;
444 sjme_errorCode sjme_noOptimize SJME_DEBUG_IDENTIFIER(sjme_alloc)(
445 sjme_attrInNotNull volatile sjme_alloc_pool* pool,
446 sjme_attrInPositiveNonZero sjme_jint size,
447 sjme_attrOutNotNull sjme_pointer* outAddr
448 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
450 sjme_errorCode error;
451 sjme_alloc_link* scanLink;
452 sjme_alloc_link* rightLink;
453 sjme_jint splitMinSize, roundSize;
454 sjme_jboolean splitBlock;
455 sjme_alloc_pool* nextPool;
456 volatile sjme_alloc_link* nextFree;
458 if (pool == NULL || size <= 0 || outAddr == NULL)
459 return SJME_ERROR_NULL_ARGUMENTS;
461 #if defined(SJME_CONFIG_DEBUG)
462 if ((size * 8) == SJME_CONFIG_HAS_POINTER)
463 sjme_message("Alloc of single pointer in %s (%s:%d).",
464 func, file, line);
465 #endif
467 /* Determine the size this will actually take up, which includes the */
468 /* link to be created following this. */
469 roundSize = (((size & 7) != 0) ? ((size | 7) + 1) : size);
470 splitMinSize = roundSize +
471 (sjme_jint)SJME_SIZEOF_ALLOC_LINK(SJME_ALLOC_SPLIT_MIN_SIZE) +
472 (sjme_jint)SJME_SIZEOF_ALLOC_LINK(0);
473 if (size > splitMinSize || splitMinSize < 0)
474 return SJME_ERROR_INVALID_ARGUMENT;
476 /* Take ownership of lock. */
477 if (sjme_error_is(error = sjme_thread_spinLockGrab(
478 &pool->spinLock)))
479 return sjme_error_default(error);
481 /* Emit barrier. */
482 sjme_thread_barrier();
484 /* Find the first free link that this fits in. */
485 scanLink = NULL;
486 splitBlock = SJME_JNI_FALSE;
487 for (scanLink = pool->freeFirstLink;
488 scanLink != NULL; scanLink = scanLink->freeNext)
490 /* Has memory been corrupted? */
491 nextFree = scanLink->freeNext;
492 if (sjme_alloc_checkCorruption(pool, scanLink) ||
493 (nextFree != NULL &&
494 sjme_alloc_checkCorruption(pool, nextFree)))
496 error = SJME_ERROR_MEMORY_CORRUPTION;
497 goto fail_corrupt;
500 /* Block is in the "invalid" space, skip it. */
501 if (scanLink->space == SJME_NUM_ALLOC_POOL_SPACE)
502 continue;
504 /* Block fits perfectly here, without needing a split? */
505 if (scanLink->blockSize == roundSize)
506 break;
508 /* Block fits here when split, try to not split ridiculously small. */
509 if (scanLink->blockSize >= splitMinSize)
511 splitBlock = SJME_JNI_TRUE;
512 break;
516 /* Out of memory. */
517 if (scanLink == NULL)
519 /* If there is an adjacent pool, if allocation fails then we shall */
520 /* try the next pool, this means multiple pools can work together */
521 /* accordingly. */
522 nextPool = pool->nextPool;
523 if (nextPool != NULL)
525 /* Release ownership of lock. */
526 if (sjme_error_is(error = sjme_thread_spinLockRelease(
527 &pool->spinLock, NULL)))
528 return sjme_error_default(error);
530 #if defined(SJME_CONFIG_DEBUG)
531 return sjme_allocR(nextPool, size, outAddr,
532 file, line, func);
533 #else
534 return sjme_alloc(pool->nextPool, size, outAddr);
535 #endif
538 /* Otherwise fail! */
539 error = sjme_error_outOfMemory(pool, size);
540 goto fail_noMemory;
543 #if 0
544 /* Debug. */
545 sjme_message("Found link at %p: %d bytes, we need %d with split %d.",
546 scanLink, (int)scanLink->blockSize, (int)roundSize, (int)splitBlock);
547 #endif
549 /* Does this block need to be split? */
550 if (splitBlock)
552 /* Check for link corruption on the adjacent links. */
553 if (sjme_alloc_checkCorruption(pool, scanLink->next) ||
554 sjme_alloc_checkCorruption(pool, scanLink->prev) ||
555 sjme_alloc_checkCorruption(pool, scanLink->freeNext) ||
556 sjme_alloc_checkCorruption(pool, scanLink->freePrev))
558 error = SJME_ERROR_MEMORY_CORRUPTION;
559 goto fail_corrupt;
562 /* Make it so this block can actually fit in here. */
563 rightLink = (sjme_alloc_link*)&scanLink->block[roundSize];
565 /* Initialize block to remove any old data. */
566 memset(rightLink, 0, sizeof(*rightLink));
568 /* Guards for link. */
569 rightLink->guardFront = SJME_ALLOC_GUARD_FRONT;
570 rightLink->guardBack = SJME_ALLOC_GUARD_BACK;
572 /* Set the right link's pool accordingly. */
573 rightLink->pool = pool;
575 /* Make sure this block is marked as free. */
576 rightLink->space = SJME_ALLOC_POOL_SPACE_FREE;
578 /* Set size of the right link. */
579 rightLink->blockSize =
580 (sjme_jint)((intptr_t)&scanLink->block[scanLink->blockSize] -
581 (intptr_t)&rightLink->block[0]);
582 rightLink->allocSize = rightLink->blockSize;
584 /* Link in physical links. */
585 rightLink->next = scanLink->next;
586 rightLink->next->prev = rightLink;
587 scanLink->next = rightLink;
588 rightLink->prev = scanLink;
590 /* Link in free links. */
591 rightLink->freeNext = scanLink->freeNext;
592 rightLink->freeNext->freePrev = rightLink;
593 scanLink->freeNext = rightLink;
594 rightLink->freePrev = scanLink;
596 /* Set size of the left block. */
597 scanLink->blockSize =
598 (sjme_jint)((intptr_t)rightLink - (intptr_t)&scanLink->block[0]);
599 scanLink->allocSize = scanLink->blockSize;
601 /* Adjust reserved and usable space. */
602 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved +=
603 SJME_SIZEOF_ALLOC_LINK(0);
604 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable -=
605 SJME_SIZEOF_ALLOC_LINK(0);
607 /* Make sure we did not cause corruption. */
608 if (sjme_alloc_checkCorruption(pool, scanLink) ||
609 sjme_alloc_checkCorruption(pool, rightLink))
611 error = SJME_ERROR_MEMORY_CORRUPTION;
612 goto fail_corrupt;
616 /* Setup block information. */
617 scanLink->space = SJME_ALLOC_POOL_SPACE_USED;
619 /* Unlink from free links. */
620 if (scanLink->freeNext != NULL)
621 scanLink->freeNext->freePrev = scanLink->freePrev;
622 if (scanLink->freePrev != NULL)
623 scanLink->freePrev->freeNext = scanLink->freeNext;
624 scanLink->freePrev = NULL;
625 scanLink->freeNext = NULL;
627 /* Use our given allocation size. */
628 scanLink->allocSize = size;
630 /* Adjust space that can actually be used for data. */
631 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable -= scanLink->blockSize;
632 pool->space[SJME_ALLOC_POOL_SPACE_USED].usable += scanLink->blockSize;
634 /* Since this block is claimed, the reserved space moves over. */
635 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved -=
636 SJME_SIZEOF_ALLOC_LINK(0);
637 pool->space[SJME_ALLOC_POOL_SPACE_USED].reserved +=
638 SJME_SIZEOF_ALLOC_LINK(0);
640 #if defined(SJME_CONFIG_DEBUG)
641 /* Set debug info. */
642 scanLink->debugFile = file;
643 scanLink->debugLine = line;
644 scanLink->debugFunction = func;
645 #endif
647 /* Make sure we did not cause corruption. */
648 if (sjme_alloc_checkCorruption(pool, scanLink) ||
649 sjme_alloc_checkCorruption(pool, scanLink->prev) ||
650 sjme_alloc_checkCorruption(pool, scanLink->next))
652 error = SJME_ERROR_MEMORY_CORRUPTION;
653 goto fail_corrupt;
656 /* Emit barrier. */
657 sjme_thread_barrier();
659 /* Release ownership of lock. */
660 if (sjme_error_is(error = sjme_thread_spinLockRelease(
661 &pool->spinLock, NULL)))
662 return sjme_error_default(error);
664 /* Use the given link. */
665 *outAddr = &scanLink->block[0];
666 return SJME_ERROR_NONE;
668 fail_corrupt:
669 fail_noMemory:
670 /* Release ownership of lock before we leave. */
671 if (sjme_error_is(sjme_thread_spinLockRelease(
672 &pool->spinLock, NULL)))
673 return sjme_error_default(error);
675 return sjme_error_default(error);
678 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_copy)(
679 sjme_attrInNotNull volatile sjme_alloc_pool* pool,
680 sjme_attrInPositiveNonZero sjme_jint size,
681 sjme_attrOutNotNull sjme_pointer* outAddr,
682 sjme_attrInNotNull sjme_pointer inAddr
683 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
685 sjme_errorCode error;
686 sjme_pointer dest;
688 if (pool == NULL || outAddr == NULL || inAddr == NULL)
689 return SJME_ERROR_NULL_ARGUMENTS;
691 /* Allocate new copy first. */
692 dest = NULL;
693 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc)(
694 pool, size, &dest
695 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) || dest == NULL)
696 return sjme_error_default(error);
698 /* Copy over. */
699 memmove(dest, inAddr, size);
700 *outAddr = dest;
702 /* Success! */
703 return SJME_ERROR_NONE;
706 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_copyWeak)(
707 sjme_attrInNotNull volatile sjme_alloc_pool* pool,
708 sjme_attrInPositiveNonZero sjme_jint size,
709 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
710 sjme_attrInNullable sjme_pointer inEnqueueData,
711 sjme_attrOutNotNull sjme_pointer* outAddr,
712 sjme_attrInNotNull sjme_pointer inAddr,
713 sjme_attrOutNullable sjme_alloc_weak* outWeak
714 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
716 sjme_errorCode error;
717 sjme_pointer dest;
719 if (pool == NULL || outAddr == NULL || inAddr == NULL)
720 return SJME_ERROR_NULL_ARGUMENTS;
722 /* Allocate new copy first. */
723 dest = NULL;
724 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew)(
725 pool, size, inEnqueue, inEnqueueData, &dest, outWeak
726 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) || dest == NULL)
727 return sjme_error_default(error);
729 /* Copy over. */
730 memmove(dest, inAddr, size);
731 *outAddr = dest;
733 /* Success! */
734 return SJME_ERROR_NONE;
737 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_format)(
738 sjme_attrInNotNull sjme_alloc_pool* inPool,
739 sjme_attrOutNotNull sjme_lpstr* outString,
740 SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL SJME_DEBUG_ONLY_COMMA
741 sjme_attrInNotNull sjme_attrFormatArg const char* format,
742 ...)
744 #define BUF_SIZE 512
745 char buf[BUF_SIZE];
746 va_list arg;
747 int len;
749 if (inPool == NULL || outString == NULL || format == NULL)
750 return SJME_ERROR_NULL_ARGUMENTS;
752 /* Start variable arguments. */
753 va_start(arg, format);
755 /* Format string to the buffer. */
756 memset(buf, 0, sizeof(buf));
757 vsnprintf(buf, BUF_SIZE - 1, format, arg);
759 /* Force to end with a NUL. */
760 buf[BUF_SIZE - 1] = 0;
762 /* End them. */
763 va_end(arg);
765 /* Calculate length of string for copying. */
766 len = strlen(buf);
768 /* Copy it. */
769 return SJME_DEBUG_IDENTIFIER(sjme_alloc_copy)(inPool, len + 1,
770 (sjme_pointer*)outString, buf
771 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY);
772 #undef BUF_SIZE
775 static sjme_errorCode sjme_alloc_mergeFree(sjme_alloc_link* link)
777 sjme_alloc_pool* pool;
778 sjme_alloc_link* right;
779 sjme_alloc_link* oldRightFreeNext;
780 sjme_alloc_link* rightRight;
781 sjme_alloc_link* checkLeft;
782 sjme_jint addedSize;
784 if (link == NULL)
785 return SJME_ERROR_NULL_ARGUMENTS;
787 /* Need pool for all operations. */
788 pool = link->pool;
790 /* If the previous block is free, pivot to there. */
791 checkLeft = link->prev;
792 if (checkLeft->space == SJME_ALLOC_POOL_SPACE_FREE)
793 return sjme_alloc_mergeFree(checkLeft);
795 /* Is the block on the right a candidate for merge? */
796 right = link->next;
797 if (right->space != SJME_ALLOC_POOL_SPACE_FREE)
798 return SJME_ERROR_NONE;
800 /* We need the block after to relink. */
801 rightRight = right->next;
803 /* Disconnect in the middle. */
804 link->next = rightRight;
805 rightRight->prev = link;
807 /* Remove from the free chain. */
808 oldRightFreeNext = right->freeNext;
809 right->freePrev->freeNext = right->freeNext;
810 oldRightFreeNext->freePrev = right->freePrev;
812 /* Reclaim the right link data area. */
813 addedSize = right->blockSize + SJME_SIZEOF_ALLOC_LINK(0);
814 link->blockSize += addedSize;
816 /* Update pool sizes. */
817 pool->space[SJME_ALLOC_POOL_SPACE_FREE].usable += addedSize;
818 pool->space[SJME_ALLOC_POOL_SPACE_FREE].reserved -=
819 SJME_SIZEOF_ALLOC_LINK(0);
821 /* Synchronize allocation size. */
822 link->allocSize = link->blockSize;
824 /* Wipe next side block to remove any stale data. */
825 memset(right, 0, sizeof(*right));
827 /* Should not have corrupted the block. */
828 if (sjme_alloc_checkCorruption(pool, link) ||
829 sjme_alloc_checkCorruption(pool, link->prev) ||
830 sjme_alloc_checkCorruption(pool, link->next) ||
831 sjme_alloc_checkCorruption(pool, link->freePrev) ||
832 sjme_alloc_checkCorruption(pool, link->freeNext))
833 return SJME_ERROR_MEMORY_CORRUPTION;
835 /* We merged a block, so check again. */
836 return sjme_alloc_mergeFree(link);
839 sjme_errorCode sjme_noOptimize sjme_alloc_free(
840 sjme_attrInNotNull sjme_pointer addr)
842 sjme_alloc_link* link;
843 volatile sjme_alloc_pool* pool;
844 sjme_errorCode error;
845 sjme_alloc_weak weak;
847 if (addr == NULL)
848 return SJME_ERROR_NULL_ARGUMENTS;
850 /* Emit barrier. */
851 sjme_thread_barrier();
853 /* Get the link. */
854 link = NULL;
855 if (sjme_error_is(error = sjme_alloc_getLink(addr, &link)))
856 return sjme_error_default(error);
858 /* Get the pool we are in. */
859 pool = link->pool;
861 /* Take ownership of lock. */
862 if (sjme_error_is(error = sjme_thread_spinLockGrab(
863 &pool->spinLock)))
864 return sjme_error_default(error);
866 /* Check the integrity of the block before we free it. */
867 pool = link->pool;
868 if (sjme_alloc_checkCorruption(pool, link))
870 error = SJME_ERROR_MEMORY_CORRUPTION;
871 goto fail_corrupt;
874 /* If there is a weak reference, clear it. */
875 weak = link->weak;
876 if (weak != NULL)
878 /* Call enqueue handler. */
879 if (weak->enqueue != NULL)
880 if (sjme_error_is(error = weak->enqueue(weak,
881 weak->enqueueData, SJME_JNI_TRUE)))
883 /* Keeping a weak reference is not considered an error */
884 /* although at this point it has no effect. */
885 if (error != SJME_ERROR_ENQUEUE_KEEP_WEAK)
886 goto fail_weakEnqueueCall;
889 /* Remove everything. */
890 link->weak = NULL;
891 weak->link = NULL;
892 weak->pointer = NULL;
894 /* Was this never referenced ever? */
895 if (sjme_atomic_sjme_jint_get(&weak->count) <= 0)
896 if (sjme_error_is(error = sjme_alloc_free(weak)))
897 goto fail_weakFree;
900 /* Mark block as free. */
901 link->space = SJME_ALLOC_POOL_SPACE_FREE;
903 /* Clear flags, if any. */
904 link->flags = 0;
906 /* Clear block memory so stale memory is not around. */
907 memset(&link->block[0], 0, link->blockSize);
909 /* Restore allocation size to block size. */
910 link->allocSize = link->blockSize;
912 #if defined(SJME_CONFIG_DEBUG)
913 /* Remove debug information. */
914 link->debugFile = NULL;
915 link->debugLine = 0;
916 link->debugFunction = NULL;
917 #endif
919 /* Link into free chain. */
920 link->freeNext = pool->freeFirstLink->freeNext;
921 pool->freeFirstLink->freeNext = link;
922 link->freeNext->freePrev = link;
923 link->freePrev = pool->freeFirstLink;
925 /* Merge together free blocks. */
926 if (sjme_error_is(error = sjme_alloc_mergeFree(link)))
927 goto fail_merge;
929 /* Emit barrier. */
930 sjme_thread_barrier();
932 /* Release ownership of lock. */
933 if (sjme_error_is(error = sjme_thread_spinLockRelease(
934 &pool->spinLock, NULL)))
935 return sjme_error_default(error);
937 /* Success! */
938 return SJME_ERROR_NONE;
940 /* Release ownership of lock. */
941 fail_weakFree:
942 fail_corrupt:
943 fail_weakEnqueueCall:
944 fail_merge:
945 if (sjme_error_is(sjme_thread_spinLockRelease(
946 &pool->spinLock, NULL)))
947 return sjme_error_default(error);
949 return sjme_error_default(error);
952 sjme_errorCode sjme_alloc_getLink(
953 sjme_attrInNotNull sjme_pointer addr,
954 sjme_attrOutNotNull sjme_alloc_link** outLink)
956 return sjme_alloc_getLinkOptional(addr, outLink,
957 SJME_JNI_TRUE);
960 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_realloc)(
961 sjme_attrInOutNotNull sjme_pointer* inOutAddr,
962 sjme_attrInPositive sjme_jint newSize
963 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
965 sjme_alloc_link* link;
966 sjme_pointer result;
967 sjme_pointer source;
968 sjme_jint limit;
969 sjme_errorCode error;
971 if (inOutAddr == NULL || *inOutAddr == NULL)
972 return SJME_ERROR_NULL_ARGUMENTS;
974 if (newSize < 0)
975 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
977 /* Emit barrier. */
978 sjme_thread_barrier();
980 /* Alias for free. */
981 source = *inOutAddr;
982 if (newSize == 0)
984 /* Just do a normal free of it since zero was requested. */
985 if (sjme_error_is(error = sjme_alloc_free(source)))
986 return sjme_error_default(error);
988 /* Clear pointer. */
989 *inOutAddr = NULL;
991 /* Success! */
992 return SJME_ERROR_NULL_ARGUMENTS;
995 /* Recover the link. */
996 link = NULL;
997 if (sjme_error_is(error = sjme_alloc_getLink(source,
998 &link)) || link == NULL)
999 return sjme_error_default(error);
1001 /* If there is a weak reference, then we cannot touch this. */
1002 if (link->weak != NULL)
1003 return SJME_ERROR_WEAK_REFERENCE_ATTACHED;
1005 /* Pointless operation. */
1006 if (newSize == link->allocSize)
1007 return SJME_ERROR_NONE;
1009 /* There are some padding bytes we can consume. */
1010 else if (newSize > link->allocSize && newSize < link->blockSize)
1012 /* Just set the new allocation size. */
1013 link->allocSize = newSize;
1015 /* Emit barrier. */
1016 sjme_thread_barrier();
1018 /* Success! */
1019 return SJME_ERROR_NONE;
1022 /* No space to grow or shrink, move it. */
1023 else
1025 /* How much do we actually want to copy? */
1026 if (newSize < link->allocSize)
1027 limit = newSize;
1028 else
1029 limit = link->allocSize;
1031 /* Debug. */
1032 sjme_message("Realloc copy %d -> %d (%d)",
1033 link->allocSize, newSize, limit);
1035 /* Allocate new block. */
1036 result = NULL;
1037 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc)(
1038 link->pool, newSize, &result
1039 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) ||
1040 result == NULL)
1041 return sjme_error_defaultOr(error,
1042 sjme_error_outOfMemory(link->pool, limit));
1044 /* Copy all the data over. */
1045 memmove(result, source, limit);
1047 /* Free the old block. */
1048 if (sjme_error_is(error = sjme_alloc_free(source)))
1049 return sjme_error_default(error);
1051 /* Emit barrier. */
1052 sjme_thread_barrier();
1054 /* Success! */
1055 *inOutAddr = result;
1056 return SJME_ERROR_NONE;
1060 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_strdup)(
1061 sjme_attrInNotNull sjme_alloc_pool* inPool,
1062 sjme_attrOutNotNull sjme_lpstr* outString,
1063 sjme_attrInNotNull sjme_lpcstr stringToCopy
1064 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1066 sjme_jint charLen;
1068 if (inPool == NULL || outString == NULL || stringToCopy == NULL)
1069 return SJME_ERROR_NULL_ARGUMENTS;
1071 /* Use standard string length, include NUL. */
1072 charLen = strlen(stringToCopy) + 1;
1074 /* Then just forward to copy. */
1075 #if defined(SJME_CONFIG_DEBUG)
1076 return sjme_alloc_copyR(inPool, charLen,
1077 (sjme_pointer*)outString, (sjme_pointer)stringToCopy,
1078 file, line, func);
1079 #else
1080 return sjme_alloc_copy(inPool, charLen,
1081 (sjme_pointer*)outString, (sjme_pointer)stringToCopy);
1082 #endif
1085 sjme_errorCode sjme_noOptimize SJME_DEBUG_IDENTIFIER(sjme_alloc_weakDelete)(
1086 sjme_attrInOutNotNull sjme_alloc_weak* inOutWeak
1087 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1089 sjme_errorCode error;
1090 sjme_alloc_weak weak;
1091 sjme_jint count;
1092 sjme_jboolean keepWeak;
1093 sjme_alloc_link* link;
1094 sjme_pointer block;
1096 if (inOutWeak == NULL)
1097 return SJME_ERROR_NULL_ARGUMENTS;
1099 /* Operate on this weak. */
1100 weak = *inOutWeak;
1102 /* Already free? */
1103 if (weak == NULL)
1104 return SJME_ERROR_NONE;
1106 /* Emit barrier. */
1107 sjme_thread_barrier();
1109 /* Get the current count. */
1110 count = sjme_atomic_sjme_jint_get(&weak->count);
1112 /* Debug. */
1113 #if defined(SJME_CONFIG_DEBUG)
1114 sjme_messageR(file, line, func, SJME_JNI_FALSE,
1115 "Weak ref %p (%p) count down to %d.",
1116 weak->pointer, weak, count - 1);
1117 #endif
1119 /* If zero is reached, it is eligible for free. */
1120 /* Provided, the data is still there. */
1121 link = weak->link;
1122 block = weak->pointer;
1123 if (count <= 1 && link != NULL && block != NULL)
1125 /* Call enqueue handler if it exists. */
1126 keepWeak = SJME_JNI_FALSE;
1127 if (weak->enqueue != NULL)
1128 if (sjme_error_is(error = weak->enqueue(weak,
1129 weak->enqueueData, SJME_JNI_FALSE)))
1131 /* Only fail if we are not keeping it. */
1132 keepWeak = (error == SJME_ERROR_ENQUEUE_KEEP_WEAK);
1133 if (!keepWeak)
1134 return sjme_error_default(error);
1137 /* Clear weak count to zero. */
1138 sjme_atomic_sjme_jint_set(&weak->count, 0);
1140 /* Unlink weak reference from block. */
1141 /* So that enqueue is not called multiple times. */
1142 link->weak = NULL;
1143 weak->link = NULL;
1144 weak->pointer = NULL;
1146 /* Free the block we point to. */
1147 if (sjme_error_is(error = sjme_alloc_free(block)))
1148 return sjme_error_default(error);
1150 /* Delete the weak reference as well? */
1151 if (!keepWeak)
1153 /* Inform our pointer that it is gone. */
1154 *inOutWeak = NULL;
1156 /* Free the weak reference itself. */
1157 if (sjme_error_is(error = sjme_alloc_free(weak)))
1158 return sjme_error_default(error);
1162 /* Otherwise, just count it down. */
1163 else if (count > 1)
1164 sjme_atomic_sjme_jint_set(&weak->count, count - 1);
1166 /* Emit barrier. */
1167 sjme_thread_barrier();
1169 /* Success! */
1170 return SJME_ERROR_NONE;
1173 sjme_errorCode sjme_alloc_weakGetPointer(
1174 sjme_attrInNotNull sjme_alloc_weak inWeak,
1175 sjme_attrOutNotNull sjme_pointer* outPointer)
1177 if (inWeak == NULL || outPointer == NULL)
1178 return SJME_ERROR_NULL_ARGUMENTS;
1180 /* Emit barrier. */
1181 sjme_thread_barrier();
1183 if (inWeak->link == NULL || inWeak->pointer == NULL)
1184 *outPointer = NULL;
1185 else
1186 *outPointer = inWeak->pointer;
1188 /* Emit barrier. */
1189 sjme_thread_barrier();
1191 /* Success! */
1192 return SJME_ERROR_NONE;
1195 static sjme_errorCode sjme_noOptimize sjme_alloc_weakRefInternal(
1196 sjme_attrInNotNull sjme_pointer addr,
1197 sjme_attrOutNullable sjme_alloc_weak* outWeak,
1198 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1199 sjme_attrInNullable sjme_pointer inEnqueueData
1200 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1202 sjme_errorCode error;
1203 sjme_alloc_link* link;
1204 sjme_alloc_weak result;
1205 sjme_jint was;
1207 if (addr == NULL ||
1208 (inEnqueue == NULL && inEnqueueData != NULL))
1209 return SJME_ERROR_NULL_ARGUMENTS;
1211 /* Emit barrier. */
1212 sjme_thread_barrier();
1214 /* Recover the link. */
1215 link = NULL;
1216 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1217 &link)) || link == NULL)
1218 return sjme_error_default(error);
1220 /* Is there already a weak reference? */
1221 result = link->weak;
1222 if (result != NULL)
1224 /* Enqueue can be set, but not overwritten. */
1225 if (inEnqueue != NULL)
1227 /* Set it? */
1228 if (result->enqueue == NULL)
1230 result->enqueue = inEnqueue;
1231 result->enqueueData = inEnqueueData;
1234 /* Must be the same function. */
1235 else if (result->enqueue != inEnqueue ||
1236 result->enqueueData != inEnqueueData)
1237 return SJME_ERROR_ENQUEUE_ALREADY_SET;
1240 /* Count up. */
1241 was = sjme_atomic_sjme_jint_getAdd(&result->count, 1);
1243 /* Emit barrier. */
1244 sjme_thread_barrier();
1246 /* Debug. */
1247 #if defined(SJME_CONFIG_DEBUG)
1248 sjme_messageR(file, line, func, SJME_JNI_FALSE,
1249 "Weak ref %p (%p) count up to %d.",
1250 result->pointer, result, was + 1);
1251 #endif
1253 /* Use it. */
1254 if (outWeak != NULL)
1255 *outWeak = result;
1256 return SJME_ERROR_NONE;
1259 /* We need to allocate the link. */
1260 if (sjme_error_is(error = sjme_alloc(link->pool, sizeof(*result),
1261 (sjme_pointer*)&result)))
1262 return sjme_error_default(error);
1264 /* Setup link information. */
1265 sjme_atomic_sjme_jint_set(&result->valid,
1266 SJME_ALLOC_WEAK_VALID);
1267 result->link = link;
1268 result->pointer = addr;
1269 result->enqueue = inEnqueue;
1270 result->enqueueData = inEnqueueData;
1271 sjme_atomic_sjme_jint_set(&result->count, 0);
1273 /* Join link back to this. */
1274 link->weak = result;
1276 /* Debug. */
1277 #if defined(SJME_CONFIG_DEBUG)
1278 sjme_messageR(file, line, func, SJME_JNI_FALSE,
1279 "Weak ref new %p (%p).",
1280 result->pointer, result);
1281 #endif
1283 /* Emit barrier. */
1284 sjme_thread_barrier();
1286 /* Success! */
1287 if (outWeak != NULL)
1288 *outWeak = result;
1289 return SJME_ERROR_NONE;
1292 sjme_errorCode sjme_noOptimize SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew)(
1293 sjme_attrInNotNull volatile sjme_alloc_pool* inPool,
1294 sjme_attrInPositiveNonZero sjme_jint size,
1295 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1296 sjme_attrInNullable sjme_pointer inEnqueueData,
1297 sjme_attrOutNotNull sjme_pointer* outAddr,
1298 sjme_attrOutNullable sjme_alloc_weak* outWeak
1299 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1301 sjme_pointer resultPtr;
1302 sjme_alloc_weak resultWeak;
1303 sjme_errorCode error;
1305 if (inPool == NULL || outAddr == NULL ||
1306 (inEnqueueData != NULL && inEnqueue == NULL))
1307 return SJME_ERROR_NULL_ARGUMENTS;
1309 /* Take ownership of lock. */
1310 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1311 &inPool->spinLock)))
1312 return sjme_error_default(error);
1314 /* Emit barrier. */
1315 sjme_thread_barrier();
1317 /* Attempt block allocation first. */
1318 resultPtr = NULL;
1319 #if defined(SJME_CONFIG_DEBUG)
1320 if (sjme_error_is(error = sjme_allocR(inPool, size,
1321 (sjme_pointer*)&resultPtr, file, line, func)) ||
1322 resultPtr == NULL)
1323 #else
1324 if (sjme_error_is(error = sjme_alloc(inPool, size,
1325 (sjme_pointer*)&resultPtr)) ||
1326 resultPtr == NULL)
1327 #endif
1328 goto fail_allocBlock;
1330 /* Then create the weak reference. */
1331 resultWeak = NULL;
1332 if (sjme_error_is(error = sjme_alloc_weakRefInternal(resultPtr,
1333 &resultWeak, inEnqueue, inEnqueueData
1334 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) ||
1335 resultWeak == NULL)
1336 goto fail_allocWeak;
1338 /* Emit barrier. */
1339 sjme_thread_barrier();
1341 /* Release ownership of lock. */
1342 if (sjme_error_is(error = sjme_thread_spinLockRelease(
1343 &inPool->spinLock, NULL)))
1344 return sjme_error_default(error);
1346 /* Success! */
1347 *outAddr = resultPtr;
1348 if (outWeak != NULL)
1349 *outWeak = resultWeak;
1350 return SJME_ERROR_NONE;
1352 fail_allocWeak:
1353 fail_allocBlock:
1354 if (resultPtr != NULL)
1355 sjme_alloc_free(resultPtr);
1357 /* Release ownership of lock. */
1358 if (sjme_error_is(sjme_thread_spinLockRelease(
1359 &inPool->spinLock, NULL)))
1360 return sjme_error_default(error);
1362 return sjme_error_default(error);
1365 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_weakRefE)(
1366 sjme_attrInNotNull sjme_pointer addr,
1367 sjme_attrOutNullable sjme_alloc_weak* outWeak,
1368 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1369 sjme_attrInNullable sjme_pointer inEnqueueData
1370 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1372 volatile sjme_alloc_pool* pool;
1373 sjme_errorCode error;
1374 sjme_alloc_link* link;
1376 if (addr == NULL ||
1377 (inEnqueue == NULL && inEnqueueData != NULL))
1378 return SJME_ERROR_NULL_ARGUMENTS;
1380 /* Emit barrier. */
1381 sjme_thread_barrier();
1383 /* Recover the link. */
1384 link = NULL;
1385 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1386 &link)) || link == NULL)
1387 return sjme_error_default(error);
1389 /* No weak reference here? */
1390 if (link->weak == NULL)
1391 return SJME_ERROR_NOT_WEAK_REFERENCE;
1393 /* Take ownership of lock. */
1394 pool = link->pool;
1395 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1396 &pool->spinLock)))
1397 return sjme_error_default(error);
1399 /* Forward. */
1400 error = sjme_alloc_weakRefInternal(addr, outWeak, inEnqueue,
1401 inEnqueueData SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY);
1403 /* Release ownership of lock. */
1404 if (sjme_error_is(sjme_thread_spinLockRelease(
1405 &pool->spinLock, NULL)))
1406 return sjme_error_default(error);
1408 /* Failed? */
1409 return error;
1412 sjme_errorCode sjme_alloc_weakRefGet(
1413 sjme_attrInNotNull sjme_pointer addr,
1414 sjme_attrOutNullable sjme_alloc_weak* outWeak)
1416 volatile sjme_alloc_pool* pool;
1417 sjme_errorCode error;
1418 sjme_alloc_link* link;
1419 sjme_alloc_weak weak;
1421 if (addr == NULL || outWeak == NULL)
1422 return SJME_ERROR_NULL_ARGUMENTS;
1424 /* Emit barrier. */
1425 sjme_thread_barrier();
1427 /* Recover the link. */
1428 link = NULL;
1429 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1430 &link)) || link == NULL)
1431 return sjme_error_default(error);
1433 /* Take ownership of lock. */
1434 pool = link->pool;
1435 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1436 &pool->spinLock)))
1437 return sjme_error_default(error);
1439 /* No weak reference here? */
1440 weak = link->weak;
1441 if (weak == NULL)
1442 error = SJME_ERROR_NOT_WEAK_REFERENCE;
1443 else
1444 error = SJME_ERROR_NONE;
1446 /* Release ownership of lock. */
1447 if (sjme_error_is(sjme_thread_spinLockRelease(
1448 &pool->spinLock, NULL)))
1449 return sjme_error_default(error);
1451 /* Did it fail? */
1452 if (sjme_error_is(error))
1453 return sjme_error_default(error);
1455 /* Give it! */
1456 *outWeak = weak;
1457 return SJME_ERROR_NONE;
1460 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_weakUnRef)(
1461 sjme_attrInNotNull sjme_pointer* addr
1462 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1464 sjme_errorCode error;
1465 sjme_alloc_weak weak;
1466 sjme_pointer sub;
1468 if (addr == NULL)
1469 return SJME_ERROR_NULL_ARGUMENTS;
1471 /* Already free? */
1472 sub = *addr;
1473 if (sub == NULL)
1474 return SJME_ERROR_NONE;
1476 /* Obtain weak reference. */
1477 weak = NULL;
1478 if (sjme_error_is(error = sjme_alloc_weakRefGet(sub,
1479 &weak)) || weak == NULL)
1480 return sjme_error_default(error);
1482 /* Delete it. */
1483 #if defined(SJME_CONFIG_DEBUG)
1484 if (sjme_error_is(error = sjme_alloc_weakDeleteR(&weak,
1485 file, line, func)))
1486 #else
1487 if (sjme_error_is(error = sjme_alloc_weakDelete(&weak)))
1488 #endif
1489 return sjme_error_default(error);
1491 /* Clear pointer if this was wiped. */
1492 if (weak == NULL)
1493 *addr = NULL;
1495 /* Success! */
1496 return SJME_ERROR_NONE;
1499 #if defined(SJME_CONFIG_DEBUG)
1501 sjme_errorCode sjme_alloc_poolDump(
1502 sjme_attrInNotNull sjme_alloc_pool* inPool)
1504 sjme_alloc_link* rover;
1506 if (inPool == NULL)
1507 return SJME_ERROR_NULL_ARGUMENTS;
1509 /* Dump information on every link. */
1510 for (rover = inPool->frontLink; rover != NULL; rover = rover->next)
1512 sjme_messageR(NULL, -1, NULL,
1513 SJME_JNI_TRUE,
1514 "Link %08p: %s %dB in %s (%s:%d)",
1515 rover,
1516 (rover->space == SJME_ALLOC_POOL_SPACE_USED ? "USED" : "FREE"),
1517 rover->blockSize,
1518 rover->debugFunction,
1519 rover->debugFile,
1520 rover->debugLine);
1523 return SJME_ERROR_NONE;
1526 #endif