Allow weakRef to be NULL, it will just not be passed to the caller.
[SquirrelJME.git] / nanocoat / lib / base / alloc.c
blob28c99c45b51870b465fb21038d1030668485f65a
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_OUT_OF_MEMORY;
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_OUT_OF_MEMORY;
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;
895 /* Mark block as free. */
896 link->space = SJME_ALLOC_POOL_SPACE_FREE;
898 /* Clear flags, if any. */
899 link->flags = 0;
901 /* Clear block memory so stale memory is not around. */
902 memset(&link->block[0], 0, link->blockSize);
904 /* Restore allocation size to block size. */
905 link->allocSize = link->blockSize;
907 #if defined(SJME_CONFIG_DEBUG)
908 /* Remove debug information. */
909 link->debugFile = NULL;
910 link->debugLine = 0;
911 link->debugFunction = NULL;
912 #endif
914 /* Link into free chain. */
915 link->freeNext = pool->freeFirstLink->freeNext;
916 pool->freeFirstLink->freeNext = link;
917 link->freeNext->freePrev = link;
918 link->freePrev = pool->freeFirstLink;
920 /* Merge together free blocks. */
921 if (sjme_error_is(error = sjme_alloc_mergeFree(link)))
922 goto fail_merge;
924 /* Emit barrier. */
925 sjme_thread_barrier();
927 /* Release ownership of lock. */
928 if (sjme_error_is(error = sjme_thread_spinLockRelease(
929 &pool->spinLock, NULL)))
930 return sjme_error_default(error);
932 /* Success! */
933 return SJME_ERROR_NONE;
935 /* Release ownership of lock. */
936 fail_corrupt:
937 fail_weakEnqueueCall:
938 fail_merge:
939 if (sjme_error_is(sjme_thread_spinLockRelease(
940 &pool->spinLock, NULL)))
941 return sjme_error_default(error);
943 return sjme_error_default(error);
946 sjme_errorCode sjme_alloc_getLink(
947 sjme_attrInNotNull sjme_pointer addr,
948 sjme_attrOutNotNull sjme_alloc_link** outLink)
950 return sjme_alloc_getLinkOptional(addr, outLink,
951 SJME_JNI_TRUE);
954 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_realloc)(
955 sjme_attrInOutNotNull sjme_pointer* inOutAddr,
956 sjme_attrInPositive sjme_jint newSize
957 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
959 sjme_alloc_link* link;
960 sjme_pointer result;
961 sjme_pointer source;
962 sjme_jint limit;
963 sjme_errorCode error;
965 if (inOutAddr == NULL || *inOutAddr == NULL)
966 return SJME_ERROR_NULL_ARGUMENTS;
968 if (newSize < 0)
969 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
971 /* Emit barrier. */
972 sjme_thread_barrier();
974 /* Alias for free. */
975 source = *inOutAddr;
976 if (newSize == 0)
978 /* Just do a normal free of it since zero was requested. */
979 if (sjme_error_is(error = sjme_alloc_free(source)))
980 return sjme_error_default(error);
982 /* Clear pointer. */
983 *inOutAddr = NULL;
985 /* Success! */
986 return SJME_ERROR_NULL_ARGUMENTS;
989 /* Recover the link. */
990 link = NULL;
991 if (sjme_error_is(error = sjme_alloc_getLink(source,
992 &link)) || link == NULL)
993 return sjme_error_default(error);
995 /* If there is a weak reference, then we cannot touch this. */
996 if (link->weak != NULL)
997 return SJME_ERROR_WEAK_REFERENCE_ATTACHED;
999 /* Pointless operation. */
1000 if (newSize == link->allocSize)
1001 return SJME_ERROR_NONE;
1003 /* There are some padding bytes we can consume. */
1004 else if (newSize > link->allocSize && newSize < link->blockSize)
1006 /* Just set the new allocation size. */
1007 link->allocSize = newSize;
1009 /* Emit barrier. */
1010 sjme_thread_barrier();
1012 /* Success! */
1013 return SJME_ERROR_NONE;
1016 /* No space to grow or shrink, move it. */
1017 else
1019 /* How much do we actually want to copy? */
1020 if (newSize < link->allocSize)
1021 limit = newSize;
1022 else
1023 limit = link->allocSize;
1025 /* Debug. */
1026 sjme_message("Realloc copy %d -> %d (%d)",
1027 link->allocSize, newSize, limit);
1029 /* Allocate new block. */
1030 result = NULL;
1031 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc)(
1032 link->pool, newSize, &result
1033 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) ||
1034 result == NULL)
1035 return sjme_error_defaultOr(error,
1036 SJME_ERROR_OUT_OF_MEMORY);
1038 /* Copy all the data over. */
1039 memmove(result, source, limit);
1041 /* Free the old block. */
1042 if (sjme_error_is(error = sjme_alloc_free(source)))
1043 return sjme_error_default(error);
1045 /* Emit barrier. */
1046 sjme_thread_barrier();
1048 /* Success! */
1049 *inOutAddr = result;
1050 return SJME_ERROR_NONE;
1054 sjme_errorCode SJME_DEBUG_IDENTIFIER(sjme_alloc_strdup)(
1055 sjme_attrInNotNull sjme_alloc_pool* inPool,
1056 sjme_attrOutNotNull sjme_lpcstr* outString,
1057 sjme_attrInNotNull sjme_lpcstr stringToCopy
1058 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1060 sjme_jint charLen;
1062 if (inPool == NULL || outString == NULL || stringToCopy == NULL)
1063 return SJME_ERROR_NULL_ARGUMENTS;
1065 /* Use standard string length, include NUL. */
1066 charLen = strlen(stringToCopy) + 1;
1068 /* Then just forward to copy. */
1069 #if defined(SJME_CONFIG_DEBUG)
1070 return sjme_alloc_copyR(inPool, charLen,
1071 (void**)outString, (void*)stringToCopy,
1072 file, line, func);
1073 #else
1074 return sjme_alloc_copy(inPool, charLen,
1075 outString, stringToCopy);
1076 #endif
1079 sjme_errorCode sjme_noOptimize sjme_alloc_weakDelete(
1080 sjme_attrInOutNotNull sjme_alloc_weak* inOutWeak)
1082 sjme_errorCode error;
1083 sjme_alloc_weak weak;
1084 sjme_jint count;
1085 sjme_jboolean keepWeak;
1086 sjme_alloc_link* link;
1087 sjme_pointer block;
1089 if (inOutWeak == NULL)
1090 return SJME_ERROR_NULL_ARGUMENTS;
1092 /* Operate on this weak. */
1093 weak = *inOutWeak;
1095 /* Already free? */
1096 if (weak == NULL)
1097 return SJME_ERROR_NONE;
1099 /* Emit barrier. */
1100 sjme_thread_barrier();
1102 /* Get the current count. */
1103 count = sjme_atomic_sjme_jint_get(&weak->count);
1105 /* If zero is reached, it is eligible for free. */
1106 /* Provided, the data is still there. */
1107 link = weak->link;
1108 block = weak->pointer;
1109 if (count <= 1 && link != NULL && block != NULL)
1111 /* Call enqueue handler if it exists. */
1112 keepWeak = SJME_JNI_FALSE;
1113 if (weak->enqueue != NULL)
1114 if (sjme_error_is(error = weak->enqueue(weak,
1115 weak->enqueueData, SJME_JNI_FALSE)))
1117 /* Only fail if we are not keeping it. */
1118 keepWeak = (error == SJME_ERROR_ENQUEUE_KEEP_WEAK);
1119 if (!keepWeak)
1120 return sjme_error_default(error);
1123 /* Clear weak count to zero. */
1124 sjme_atomic_sjme_jint_set(&weak->count, 0);
1126 /* Unlink weak reference from block. */
1127 /* So that enqueue is not called multiple times. */
1128 link->weak = NULL;
1129 weak->link = NULL;
1130 weak->pointer = NULL;
1132 /* Free the block we point to. */
1133 if (sjme_error_is(error = sjme_alloc_free(block)))
1134 return sjme_error_default(error);
1136 /* Delete the weak reference as well? */
1137 if (!keepWeak)
1139 /* Inform our pointer that it is gone. */
1140 *inOutWeak = NULL;
1142 /* Free the weak reference itself. */
1143 if (sjme_error_is(error = sjme_alloc_free(weak)))
1144 return sjme_error_default(error);
1148 /* Otherwise, just count it down. */
1149 else if (count > 1)
1150 sjme_atomic_sjme_jint_set(&weak->count, count - 1);
1152 /* Emit barrier. */
1153 sjme_thread_barrier();
1155 /* Success! */
1156 return SJME_ERROR_NONE;
1159 sjme_errorCode sjme_alloc_weakGetPointer(
1160 sjme_attrInNotNull sjme_alloc_weak inWeak,
1161 sjme_attrOutNotNull sjme_pointer* outPointer)
1163 if (inWeak == NULL || outPointer == NULL)
1164 return SJME_ERROR_NULL_ARGUMENTS;
1166 /* Emit barrier. */
1167 sjme_thread_barrier();
1169 if (inWeak->link == NULL || inWeak->pointer == NULL)
1170 *outPointer = NULL;
1171 else
1172 *outPointer = inWeak->pointer;
1174 /* Emit barrier. */
1175 sjme_thread_barrier();
1177 /* Success! */
1178 return SJME_ERROR_NONE;
1181 static sjme_errorCode sjme_noOptimize sjme_alloc_weakRefInternal(
1182 sjme_attrInNotNull sjme_pointer addr,
1183 sjme_attrOutNullable sjme_alloc_weak* outWeak,
1184 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1185 sjme_attrInNullable sjme_pointer inEnqueueData)
1187 sjme_errorCode error;
1188 sjme_alloc_link* link;
1189 sjme_alloc_weak result;
1190 sjme_jint was;
1192 if (addr == NULL ||
1193 (inEnqueue == NULL && inEnqueueData != NULL))
1194 return SJME_ERROR_NULL_ARGUMENTS;
1196 /* Emit barrier. */
1197 sjme_thread_barrier();
1199 /* Recover the link. */
1200 link = NULL;
1201 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1202 &link)) || link == NULL)
1203 return sjme_error_default(error);
1205 /* Is there already a weak reference? */
1206 result = link->weak;
1207 if (result != NULL)
1209 /* Enqueue can be set, but not overwritten. */
1210 if (inEnqueue != NULL)
1212 /* Set it? */
1213 if (result->enqueue == NULL)
1215 result->enqueue = inEnqueue;
1216 result->enqueueData = inEnqueueData;
1219 /* Must be the same function. */
1220 else if (result->enqueue != inEnqueue ||
1221 result->enqueueData != inEnqueueData)
1222 return SJME_ERROR_ENQUEUE_ALREADY_SET;
1225 /* Count up. */
1226 was = sjme_atomic_sjme_jint_getAdd(&result->count, 1);
1228 /* Emit barrier. */
1229 sjme_thread_barrier();
1231 /* Debug. */
1232 #if defined(SJME_CONFIG_DEBUG)
1233 sjme_message("Weak ref %p (%p) count up to %d.",
1234 result->pointer, result, was + 1);
1235 #endif
1237 /* Use it. */
1238 if (outWeak != NULL)
1239 *outWeak = result;
1240 return SJME_ERROR_NONE;
1243 /* We need to allocate the link. */
1244 if (sjme_error_is(error = sjme_alloc(link->pool, sizeof(*result),
1245 (void**)&result)))
1246 return sjme_error_default(error);
1248 /* Setup link information. */
1249 sjme_atomic_sjme_jint_set(&result->valid,
1250 SJME_ALLOC_WEAK_VALID);
1251 result->link = link;
1252 result->pointer = addr;
1253 result->enqueue = inEnqueue;
1254 result->enqueueData = inEnqueueData;
1255 sjme_atomic_sjme_jint_set(&result->count, 1);
1257 /* Join link back to this. */
1258 link->weak = result;
1260 /* Emit barrier. */
1261 sjme_thread_barrier();
1263 /* Success! */
1264 if (outWeak != NULL)
1265 *outWeak = result;
1266 return SJME_ERROR_NONE;
1269 sjme_errorCode sjme_noOptimize SJME_DEBUG_IDENTIFIER(sjme_alloc_weakNew)(
1270 sjme_attrInNotNull volatile sjme_alloc_pool* inPool,
1271 sjme_attrInPositiveNonZero sjme_jint size,
1272 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1273 sjme_attrInNullable sjme_pointer inEnqueueData,
1274 sjme_attrOutNotNull sjme_pointer* outAddr,
1275 sjme_attrOutNullable sjme_alloc_weak* outWeak
1276 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_DECL_FILE_LINE_FUNC_OPTIONAL)
1278 sjme_pointer resultPtr;
1279 sjme_alloc_weak resultWeak;
1280 sjme_errorCode error;
1282 if (inPool == NULL || outAddr == NULL ||
1283 (inEnqueueData != NULL && inEnqueue == NULL))
1284 return SJME_ERROR_NULL_ARGUMENTS;
1286 /* Take ownership of lock. */
1287 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1288 &inPool->spinLock)))
1289 return sjme_error_default(error);
1291 /* Emit barrier. */
1292 sjme_thread_barrier();
1294 /* Attempt block allocation first. */
1295 resultPtr = NULL;
1296 #if defined(SJME_CONFIG_DEBUG)
1297 if (sjme_error_is(error = sjme_allocR(inPool, size,
1298 &resultPtr, file, line, func)) ||
1299 resultPtr == NULL)
1300 #else
1301 if (sjme_error_is(error = sjme_alloc(inPool, size,
1302 &resultPtr)) ||
1303 resultPtr == NULL)
1304 #endif
1305 goto fail_allocBlock;
1307 /* Then create the weak reference. */
1308 resultWeak = NULL;
1309 if (sjme_error_is(error = sjme_alloc_weakRefInternal(resultPtr,
1310 &resultWeak, inEnqueue, inEnqueueData)) || resultWeak == NULL)
1311 goto fail_allocWeak;
1313 /* Emit barrier. */
1314 sjme_thread_barrier();
1316 /* Release ownership of lock. */
1317 if (sjme_error_is(error = sjme_thread_spinLockRelease(
1318 &inPool->spinLock, NULL)))
1319 return sjme_error_default(error);
1321 /* Success! */
1322 *outAddr = resultPtr;
1323 if (outWeak != NULL)
1324 *outWeak = resultWeak;
1325 return SJME_ERROR_NONE;
1327 fail_allocWeak:
1328 fail_allocBlock:
1329 if (resultPtr != NULL)
1330 sjme_alloc_free(resultPtr);
1332 /* Release ownership of lock. */
1333 if (sjme_error_is(sjme_thread_spinLockRelease(
1334 &inPool->spinLock, NULL)))
1335 return sjme_error_default(error);
1337 return sjme_error_default(error);
1340 sjme_errorCode sjme_alloc_weakRef(
1341 sjme_attrInNotNull sjme_pointer addr,
1342 sjme_attrOutNullable sjme_alloc_weak* outWeak,
1343 sjme_attrInNullable sjme_alloc_weakEnqueueFunc inEnqueue,
1344 sjme_attrInNullable sjme_pointer inEnqueueData)
1346 volatile sjme_alloc_pool* pool;
1347 sjme_errorCode error;
1348 sjme_alloc_link* link;
1350 if (addr == NULL ||
1351 (inEnqueue == NULL && inEnqueueData != NULL))
1352 return SJME_ERROR_NULL_ARGUMENTS;
1354 /* Emit barrier. */
1355 sjme_thread_barrier();
1357 /* Recover the link. */
1358 link = NULL;
1359 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1360 &link)) || link == NULL)
1361 return sjme_error_default(error);
1363 /* No weak reference here? */
1364 if (link->weak == NULL)
1365 return SJME_ERROR_NOT_WEAK_REFERENCE;
1367 /* Take ownership of lock. */
1368 pool = link->pool;
1369 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1370 &pool->spinLock)))
1371 return sjme_error_default(error);
1373 /* Forward. */
1374 error = sjme_alloc_weakRefInternal(addr, outWeak, inEnqueue,
1375 inEnqueueData);
1377 /* Release ownership of lock. */
1378 if (sjme_error_is(sjme_thread_spinLockRelease(
1379 &pool->spinLock, NULL)))
1380 return sjme_error_default(error);
1382 return error;
1385 sjme_errorCode sjme_alloc_weakRefGet(
1386 sjme_attrInNotNull sjme_pointer addr,
1387 sjme_attrOutNullable sjme_alloc_weak* outWeak)
1389 volatile sjme_alloc_pool* pool;
1390 sjme_errorCode error;
1391 sjme_alloc_link* link;
1392 sjme_alloc_weak weak;
1394 if (addr == NULL || outWeak == NULL)
1395 return SJME_ERROR_NULL_ARGUMENTS;
1397 /* Emit barrier. */
1398 sjme_thread_barrier();
1400 /* Recover the link. */
1401 link = NULL;
1402 if (sjme_error_is(error = sjme_alloc_getLink(addr,
1403 &link)) || link == NULL)
1404 return sjme_error_default(error);
1406 /* Take ownership of lock. */
1407 pool = link->pool;
1408 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1409 &pool->spinLock)))
1410 return sjme_error_default(error);
1412 /* No weak reference here? */
1413 weak = link->weak;
1414 if (weak == NULL)
1415 error = SJME_ERROR_NOT_WEAK_REFERENCE;
1416 else
1417 error = SJME_ERROR_NONE;
1419 /* Release ownership of lock. */
1420 if (sjme_error_is(sjme_thread_spinLockRelease(
1421 &pool->spinLock, NULL)))
1422 return sjme_error_default(error);
1424 /* Did it fail? */
1425 if (sjme_error_is(error))
1426 return sjme_error_default(error);
1428 /* Give it! */
1429 *outWeak = weak;
1430 return SJME_ERROR_NONE;
1433 sjme_errorCode sjme_alloc_weakUnRef(
1434 sjme_attrInNotNull sjme_pointer addr)
1436 sjme_errorCode error;
1437 sjme_alloc_weak weak;
1439 if (addr == NULL)
1440 return SJME_ERROR_NULL_ARGUMENTS;
1442 /* Obtain weak reference. */
1443 weak = NULL;
1444 if (sjme_error_is(error = sjme_alloc_weakRefGet(addr, &weak)) ||
1445 weak == NULL)
1446 return sjme_error_default(error);
1448 /* Delete it. */
1449 if (sjme_error_is(error = sjme_alloc_weakDelete(&weak)))
1450 return sjme_error_default(error);
1452 /* Success! */
1453 return SJME_ERROR_NONE;