Move cleanup handler elsewhere; Add sjme_alloc_copyWeak to copy data as a weak reference.
[SquirrelJME.git] / nanocoat / lib / base / alloc.c
bloba6366d4fc26b03b0aa822699974af64a57287866
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"
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)
38 /**
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 .
45 * @since 2023/12/29
47 static sjme_inline sjme_jboolean sjme_alloc_corruptFail(
48 volatile sjme_alloc_pool* pool,
49 volatile sjme_alloc_link* atLink,
50 const char* trigger)
52 sjme_message("Corrupted Link %p: %s", atLink, trigger);
54 /* Ignore if null. */
55 if (atLink == NULL)
56 return SJME_JNI_TRUE;
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");
69 else
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);
78 /* Abort. */
79 if (sjme_debug_handlers != NULL && sjme_debug_handlers->abort != NULL)
80 sjme_debug_handlers->abort();
82 /* Always indicate failure here. */
83 return SJME_JNI_TRUE;
85 #else
86 /**
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 .
93 * @since 2023/12/29
95 #define sjme_alloc_corruptFail(pool, atLink, trigger) SJME_JNI_TRUE
96 #endif
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)
102 uintptr_t check;
104 /* Ignore null pointers. */
105 if (atLink == NULL)
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.
126 * @since 2023/12/29
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;
134 if (pool == NULL)
135 return SJME_JNI_TRUE;
137 /* If no link is specified, ignore. */
138 if (atLink == NULL)
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,
147 "Wrong back guard");
149 /* Link is in the wrong pool. */
150 if (atLink->pool != pool)
151 return sjme_alloc_corruptFail(pool, atLink,
152 "Wrong pool");
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,
196 atLink->freePrev))
197 return sjme_alloc_corruptFail(pool, atLink,
198 "Corrupt freePrev");
199 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
200 atLink->freeNext))
201 return sjme_alloc_corruptFail(pool, atLink,
202 "Corrupt freeNext");
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? */
220 else
221 return sjme_alloc_corruptFail(pool, atLink,
222 "Incorrect space");
224 /* Check common next links. */
225 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
226 atLink->prev))
227 return sjme_alloc_corruptFail(pool, atLink,
228 "Corrupt prev");
229 if (sjme_alloc_checkCorruptionRange(pool, poolStart, poolEnd,
230 atLink->next))
231 return sjme_alloc_corruptFail(pool, atLink,
232 "Corrupt next");
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. */
253 if (checkCorruption)
254 sjme_alloc_checkCorruption(link->pool, link);
256 /* Success! */
257 *outLink = 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)
265 sjme_pointer result;
266 sjme_jint useSize;
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 ||
271 size > useSize)
272 return SJME_ERROR_INVALID_ARGUMENT;
274 /* Attempt allocation. */
275 result = malloc(useSize);
276 if (!result)
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. */
304 pool = baseAddr;
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;
355 /* Link in pools. */
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>";
369 #endif
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)));
379 #endif
381 /* If this is a valid link then we are allocating a nested pool. */
382 #if 0
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;
387 #endif
389 /* Use the pool. */
390 *outPool = pool;
391 return SJME_ERROR_NONE;
394 sjme_errorCode sjme_alloc_poolDestroy(
395 sjme_attrOutNotNull sjme_alloc_pool* inPool)
397 if (inPool == NULL)
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)
409 sjme_jint total, i;
410 sjme_jint reserved;
411 sjme_jint usable;
413 if (pool == NULL)
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. */
420 reserved = 0;
421 usable = 0;
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)
433 *outTotal = total;
434 if (outReserved != NULL)
435 *outReserved = reserved;
436 if (outUsable != NULL)
437 *outUsable = usable;
439 /* Success! */
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).",
463 func, file, line);
464 #endif
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(
477 &pool->spinLock)))
478 return sjme_error_default(error);
480 /* Emit barrier. */
481 sjme_thread_barrier();
483 /* Find the first free link that this fits in. */
484 scanLink = NULL;
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) ||
492 (nextFree != NULL &&
493 sjme_alloc_checkCorruption(pool, nextFree)))
495 error = SJME_ERROR_MEMORY_CORRUPTION;
496 goto fail_corrupt;
499 /* Block is in the "invalid" space, skip it. */
500 if (scanLink->space == SJME_NUM_ALLOC_POOL_SPACE)
501 continue;
503 /* Block fits perfectly here, without needing a split? */
504 if (scanLink->blockSize == roundSize)
505 break;
507 /* Block fits here when split, try to not split ridiculously small. */
508 if (scanLink->blockSize >= splitMinSize)
510 splitBlock = SJME_JNI_TRUE;
511 break;
515 /* Out of memory. */
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 */
520 /* accordingly. */
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,
531 file, line, func);
532 #else
533 return sjme_alloc(pool->nextPool, size, outAddr);
534 #endif
537 /* Otherwise fail! */
538 error = SJME_ERROR_OUT_OF_MEMORY;
539 goto fail_noMemory;
542 #if 0
543 /* Debug. */
544 sjme_message("Found link at %p: %d bytes, we need %d with split %d.",
545 scanLink, (int)scanLink->blockSize, (int)roundSize, (int)splitBlock);
546 #endif
548 /* Does this block need to be split? */
549 if (splitBlock)
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;
558 goto fail_corrupt;
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;
611 goto fail_corrupt;
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;
644 #endif
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;
652 goto fail_corrupt;
655 /* Emit barrier. */
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;
667 fail_corrupt:
668 fail_noMemory:
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;
685 sjme_pointer dest;
687 if (pool == NULL || outAddr == NULL || inAddr == NULL)
688 return SJME_ERROR_NULL_ARGUMENTS;
690 /* Allocate new copy first. */
691 dest = NULL;
692 if (sjme_error_is(error = SJME_DEBUG_IDENTIFIER(sjme_alloc)(
693 pool, size, &dest
694 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY)) || dest == NULL)
695 return sjme_error_default(error);
697 /* Copy over. */
698 memmove(dest, inAddr, size);
699 *outAddr = dest;
701 /* Success! */
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;
716 sjme_pointer dest;
718 if (pool == NULL || outAddr == NULL || inAddr == NULL)
719 return SJME_ERROR_NULL_ARGUMENTS;
721 /* Allocate new copy first. */
722 dest = NULL;
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);
728 /* Copy over. */
729 memmove(dest, inAddr, size);
730 *outAddr = dest;
732 /* Success! */
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,
741 ...)
743 #define BUF_SIZE 512
744 char buf[BUF_SIZE];
745 va_list arg;
746 int len;
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;
761 /* End them. */
762 va_end(arg);
764 /* Calculate length of string for copying. */
765 len = strlen(buf);
767 /* Copy it. */
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);
771 #undef BUF_SIZE
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;
781 sjme_jint addedSize;
783 if (link == NULL)
784 return SJME_ERROR_NULL_ARGUMENTS;
786 /* Need pool for all operations. */
787 pool = link->pool;
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? */
795 right = link->next;
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;
846 if (addr == NULL)
847 return SJME_ERROR_NULL_ARGUMENTS;
849 /* Emit barrier. */
850 sjme_thread_barrier();
852 /* Get the link. */
853 link = NULL;
854 if (sjme_error_is(error = sjme_alloc_getLink(addr, &link)))
855 return sjme_error_default(error);
857 /* Get the pool we are in. */
858 pool = link->pool;
860 /* Take ownership of lock. */
861 if (sjme_error_is(error = sjme_thread_spinLockGrab(
862 &pool->spinLock)))
863 return sjme_error_default(error);
865 /* Check the integrity of the block before we free it. */
866 pool = link->pool;
867 if (sjme_alloc_checkCorruption(pool, link))
869 error = SJME_ERROR_MEMORY_CORRUPTION;
870 goto fail_corrupt;
873 /* If there is a weak reference, clear it. */
874 weak = link->weak;
875 if (weak != NULL)
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. */
889 link->weak = NULL;
890 weak->link = NULL;
891 weak->pointer = NULL;
894 /* Mark block as free. */
895 link->space = SJME_ALLOC_POOL_SPACE_FREE;
897 /* Clear flags, if any. */
898 link->flags = 0;
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;
909 link->debugLine = 0;
910 link->debugFunction = NULL;
911 #endif
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)))
921 goto fail_merge;
923 /* Emit barrier. */
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);
931 /* Success! */
932 return SJME_ERROR_NONE;
934 /* Release ownership of lock. */
935 fail_corrupt:
936 fail_weakEnqueueCall:
937 fail_merge:
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,
950 SJME_JNI_TRUE);
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;
959 sjme_pointer result;
960 sjme_pointer source;
961 sjme_jint limit;
962 sjme_errorCode error;
964 if (inOutAddr == NULL || *inOutAddr == NULL)
965 return SJME_ERROR_NULL_ARGUMENTS;
967 if (newSize < 0)
968 return SJME_ERROR_INDEX_OUT_OF_BOUNDS;
970 /* Emit barrier. */
971 sjme_thread_barrier();
973 /* Alias for free. */
974 source = *inOutAddr;
975 if (newSize == 0)
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);
981 /* Clear pointer. */
982 *inOutAddr = NULL;
984 /* Success! */
985 return SJME_ERROR_NULL_ARGUMENTS;
988 /* Recover the link. */
989 link = NULL;
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;
1008 /* Emit barrier. */
1009 sjme_thread_barrier();
1011 /* Success! */
1012 return SJME_ERROR_NONE;
1015 /* No space to grow or shrink, move it. */
1016 else
1018 /* How much do we actually want to copy? */
1019 if (newSize < link->allocSize)
1020 limit = newSize;
1021 else
1022 limit = link->allocSize;
1024 /* Debug. */
1025 sjme_message("Realloc copy %d -> %d (%d)",
1026 link->allocSize, newSize, limit);
1028 /* Allocate new block. */
1029 result = NULL;
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)) ||
1033 result == NULL)
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);
1044 /* Emit barrier. */
1045 sjme_thread_barrier();
1047 /* Success! */
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)
1059 sjme_jint charLen;
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,
1071 file, line, func);
1072 #else
1073 return sjme_alloc_copy(inPool, charLen,
1074 outString, stringToCopy);
1075 #endif
1078 sjme_errorCode sjme_noOptimize sjme_alloc_weakDelete(
1079 sjme_attrInOutNotNull sjme_alloc_weak* inOutWeak)
1081 sjme_errorCode error;
1082 sjme_alloc_weak weak;
1083 sjme_jint count;
1084 sjme_jboolean keepWeak;
1085 sjme_alloc_link* link;
1086 sjme_pointer block;
1088 if (inOutWeak == NULL)
1089 return SJME_ERROR_NULL_ARGUMENTS;
1091 /* Operate on this weak. */
1092 weak = *inOutWeak;
1094 /* Already free? */
1095 if (weak == NULL)
1096 return SJME_ERROR_NONE;
1098 /* Emit barrier. */
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. */
1106 link = weak->link;
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);
1118 if (!keepWeak)
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. */
1127 link->weak = NULL;
1128 weak->link = NULL;
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? */
1136 if (!keepWeak)
1138 /* Inform our pointer that it is gone. */
1139 *inOutWeak = NULL;
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. */
1148 else if (count > 1)
1149 sjme_atomic_sjme_jint_set(&weak->count, count - 1);
1151 /* Emit barrier. */
1152 sjme_thread_barrier();
1154 /* Success! */
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;
1165 /* Emit barrier. */
1166 sjme_thread_barrier();
1168 if (inWeak->link == NULL || inWeak->pointer == NULL)
1169 *outPointer = NULL;
1170 else
1171 *outPointer = inWeak->pointer;
1173 /* Emit barrier. */
1174 sjme_thread_barrier();
1176 /* Success! */
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;
1194 /* Emit barrier. */
1195 sjme_thread_barrier();
1197 /* Recover the link. */
1198 link = NULL;
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;
1205 if (result != NULL)
1207 /* Enqueue can be set, but not overwritten. */
1208 if (inEnqueue != NULL)
1210 /* Set it? */
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;
1223 /* Count up. */
1224 sjme_atomic_sjme_jint_getAdd(&result->count, 1);
1226 /* Emit barrier. */
1227 sjme_thread_barrier();
1229 /* Use it. */
1230 *outWeak = result;
1231 return SJME_ERROR_NONE;
1234 /* We need to allocate the link. */
1235 if (sjme_error_is(error = sjme_alloc(link->pool, sizeof(*result),
1236 &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;
1251 /* Emit barrier. */
1252 sjme_thread_barrier();
1254 /* Success! */
1255 *outWeak = result;
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);
1281 /* Emit barrier. */
1282 sjme_thread_barrier();
1284 /* Attempt block allocation first. */
1285 resultPtr = NULL;
1286 #if defined(SJME_CONFIG_DEBUG)
1287 if (sjme_error_is(error = sjme_allocR(inPool, size,
1288 &resultPtr, file, line, func)) ||
1289 resultPtr == NULL)
1290 #else
1291 if (sjme_error_is(error = sjme_alloc(inPool, size,
1292 &resultPtr)) ||
1293 resultPtr == NULL)
1294 #endif
1295 goto fail_allocBlock;
1297 /* Then create the weak reference. */
1298 resultWeak = NULL;
1299 if (sjme_error_is(error = sjme_alloc_weakRefInternal(resultPtr,
1300 &resultWeak, inEnqueue, inEnqueueData)) || resultWeak == NULL)
1301 goto fail_allocWeak;
1303 /* Emit barrier. */
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);
1311 /* Success! */
1312 *outAddr = resultPtr;
1313 if (outWeak != NULL)
1314 *outWeak = resultWeak;
1315 return SJME_ERROR_NONE;
1317 fail_allocWeak:
1318 fail_allocBlock:
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;
1344 /* Emit barrier. */
1345 sjme_thread_barrier();
1347 /* Recover the link. */
1348 link = NULL;
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. */
1358 pool = link->pool;
1359 if (sjme_error_is(error = sjme_thread_spinLockGrab(
1360 &pool->spinLock)))
1361 return sjme_error_default(error);
1363 /* Forward. */
1364 error = sjme_alloc_weakRefInternal(addr, outWeak, inEnqueue,
1365 inEnqueueData);
1367 /* Release ownership of lock. */
1368 if (sjme_error_is(sjme_thread_spinLockRelease(
1369 &pool->spinLock, NULL)))
1370 return sjme_error_default(error);
1372 return error;