1 /* -*- Mode: C; indent-tabs-mode: t; tab-width: 4 -*-
2 // ---------------------------------------------------------------------------
4 // Copyright (C) Stephanie Gawroriski <xer@multiphasicapps.net>
5 // ---------------------------------------------------------------------------
6 // SquirrelJME is under the Mozilla Public License Version 2.0.
7 // See license.mkd for licensing and copyright information.
8 // -------------------------------------------------------------------------*/
13 /* Include Valgrind if it is available? */
14 #if defined(SJME_CONFIG_HAS_VALGRIND)
19 #include "sjme/alloc.h"
20 #include "sjme/debug.h"
21 #include "sjme/atomic.h"
22 #include "sjme/multithread.h"
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)
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 .
48 static sjme_inline sjme_jboolean
sjme_alloc_corruptFail(
49 volatile sjme_alloc_pool
* pool
,
50 volatile sjme_alloc_link
* atLink
,
53 sjme_message("Corrupted Link %p: %s", atLink
, trigger
);
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");
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
);
80 if (sjme_debug_handlers
!= NULL
&& sjme_debug_handlers
->abort
!= NULL
)
81 sjme_debug_handlers
->abort();
83 /* Always indicate failure here. */
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 .
96 #define sjme_alloc_corruptFail(pool, atLink, trigger) SJME_JNI_TRUE
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
)
105 /* Ignore null pointers. */
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.
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
;
136 return SJME_JNI_TRUE
;
138 /* If no link is specified, ignore. */
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
,
150 /* Link is in the wrong pool. */
151 if (atLink
->pool
!= pool
)
152 return sjme_alloc_corruptFail(pool
, atLink
,
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
,
198 return sjme_alloc_corruptFail(pool
, atLink
,
200 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
202 return sjme_alloc_corruptFail(pool
, atLink
,
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? */
222 return sjme_alloc_corruptFail(pool
, atLink
,
225 /* Check common next links. */
226 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
228 return sjme_alloc_corruptFail(pool
, atLink
,
230 if (sjme_alloc_checkCorruptionRange(pool
, poolStart
, poolEnd
,
232 return sjme_alloc_corruptFail(pool
, atLink
,
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. */
255 sjme_alloc_checkCorruption(link
->pool
, 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
)
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 ||
273 return SJME_ERROR_INVALID_ARGUMENT
;
275 /* Attempt allocation. */
276 result
= malloc(useSize
);
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. */
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
;
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>";
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)));
382 /* If this is a valid link then we are allocating a nested pool. */
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
;
392 return SJME_ERROR_NONE
;
395 sjme_errorCode
sjme_alloc_poolDestroy(
396 sjme_attrOutNotNull sjme_alloc_pool
* inPool
)
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
)
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. */
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
)
435 if (outReserved
!= NULL
)
436 *outReserved
= reserved
;
437 if (outUsable
!= NULL
)
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).",
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(
479 return sjme_error_default(error
);
482 sjme_thread_barrier();
484 /* Find the first free link that this fits in. */
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
) ||
494 sjme_alloc_checkCorruption(pool
, nextFree
)))
496 error
= SJME_ERROR_MEMORY_CORRUPTION
;
500 /* Block is in the "invalid" space, skip it. */
501 if (scanLink
->space
== SJME_NUM_ALLOC_POOL_SPACE
)
504 /* Block fits perfectly here, without needing a split? */
505 if (scanLink
->blockSize
== roundSize
)
508 /* Block fits here when split, try to not split ridiculously small. */
509 if (scanLink
->blockSize
>= splitMinSize
)
511 splitBlock
= SJME_JNI_TRUE
;
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 */
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
,
534 return sjme_alloc(pool
->nextPool
, size
, outAddr
);
538 /* Otherwise fail! */
539 error
= sjme_error_outOfMemory(pool
, size
);
545 sjme_message("Found link at %p: %d bytes, we need %d with split %d.",
546 scanLink
, (int)scanLink
->blockSize
, (int)roundSize
, (int)splitBlock
);
549 /* Does this block need to be split? */
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
;
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
;
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
;
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
;
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
;
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
;
688 if (pool
== NULL
|| outAddr
== NULL
|| inAddr
== NULL
)
689 return SJME_ERROR_NULL_ARGUMENTS
;
691 /* Allocate new copy first. */
693 if (sjme_error_is(error
= SJME_DEBUG_IDENTIFIER(sjme_alloc
)(
695 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) || dest
== NULL
)
696 return sjme_error_default(error
);
699 memmove(dest
, inAddr
, size
);
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
;
719 if (pool
== NULL
|| outAddr
== NULL
|| inAddr
== NULL
)
720 return SJME_ERROR_NULL_ARGUMENTS
;
722 /* Allocate new copy first. */
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
);
730 memmove(dest
, inAddr
, size
);
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
,
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;
765 /* Calculate length of string for copying. */
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
);
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
;
785 return SJME_ERROR_NULL_ARGUMENTS
;
787 /* Need pool for all operations. */
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? */
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
;
848 return SJME_ERROR_NULL_ARGUMENTS
;
851 sjme_thread_barrier();
855 if (sjme_error_is(error
= sjme_alloc_getLink(addr
, &link
)))
856 return sjme_error_default(error
);
858 /* Get the pool we are in. */
861 /* Take ownership of lock. */
862 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
864 return sjme_error_default(error
);
866 /* Check the integrity of the block before we free it. */
868 if (sjme_alloc_checkCorruption(pool
, link
))
870 error
= SJME_ERROR_MEMORY_CORRUPTION
;
874 /* If there is a weak reference, clear it. */
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. */
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
)))
900 /* Mark block as free. */
901 link
->space
= SJME_ALLOC_POOL_SPACE_FREE
;
903 /* Clear flags, if any. */
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
;
916 link
->debugFunction
= NULL
;
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
)))
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
);
938 return SJME_ERROR_NONE
;
940 /* Release ownership of lock. */
943 fail_weakEnqueueCall
:
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
,
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
;
969 sjme_errorCode error
;
971 if (inOutAddr
== NULL
|| *inOutAddr
== NULL
)
972 return SJME_ERROR_NULL_ARGUMENTS
;
975 return SJME_ERROR_INDEX_OUT_OF_BOUNDS
;
978 sjme_thread_barrier();
980 /* Alias for free. */
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
);
992 return SJME_ERROR_NULL_ARGUMENTS
;
995 /* Recover the link. */
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
;
1016 sjme_thread_barrier();
1019 return SJME_ERROR_NONE
;
1022 /* No space to grow or shrink, move it. */
1025 /* How much do we actually want to copy? */
1026 if (newSize
< link
->allocSize
)
1029 limit
= link
->allocSize
;
1032 sjme_message("Realloc copy %d -> %d (%d)",
1033 link
->allocSize
, newSize
, limit
);
1035 /* Allocate new block. */
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
)) ||
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
);
1052 sjme_thread_barrier();
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
)
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
,
1080 return sjme_alloc_copy(inPool
, charLen
,
1081 (sjme_pointer
*)outString
, (sjme_pointer
)stringToCopy
);
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
;
1092 sjme_jboolean keepWeak
;
1093 sjme_alloc_link
* link
;
1096 if (inOutWeak
== NULL
)
1097 return SJME_ERROR_NULL_ARGUMENTS
;
1099 /* Operate on this weak. */
1104 return SJME_ERROR_NONE
;
1107 sjme_thread_barrier();
1109 /* Get the current count. */
1110 count
= sjme_atomic_sjme_jint_get(&weak
->count
);
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);
1119 /* If zero is reached, it is eligible for free. */
1120 /* Provided, the data is still there. */
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
);
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. */
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? */
1153 /* Inform our pointer that it is gone. */
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. */
1164 sjme_atomic_sjme_jint_set(&weak
->count
, count
- 1);
1167 sjme_thread_barrier();
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
;
1181 sjme_thread_barrier();
1183 if (inWeak
->link
== NULL
|| inWeak
->pointer
== NULL
)
1186 *outPointer
= inWeak
->pointer
;
1189 sjme_thread_barrier();
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
;
1208 (inEnqueue
== NULL
&& inEnqueueData
!= NULL
))
1209 return SJME_ERROR_NULL_ARGUMENTS
;
1212 sjme_thread_barrier();
1214 /* Recover the link. */
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
;
1224 /* Enqueue can be set, but not overwritten. */
1225 if (inEnqueue
!= NULL
)
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
;
1241 was
= sjme_atomic_sjme_jint_getAdd(&result
->count
, 1);
1244 sjme_thread_barrier();
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);
1254 if (outWeak
!= NULL
)
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
;
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
);
1284 sjme_thread_barrier();
1287 if (outWeak
!= NULL
)
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
);
1315 sjme_thread_barrier();
1317 /* Attempt block allocation first. */
1319 #if defined(SJME_CONFIG_DEBUG)
1320 if (sjme_error_is(error
= sjme_allocR(inPool
, size
,
1321 (sjme_pointer
*)&resultPtr
, file
, line
, func
)) ||
1324 if (sjme_error_is(error
= sjme_alloc(inPool
, size
,
1325 (sjme_pointer
*)&resultPtr
)) ||
1328 goto fail_allocBlock
;
1330 /* Then create the weak reference. */
1332 if (sjme_error_is(error
= sjme_alloc_weakRefInternal(resultPtr
,
1333 &resultWeak
, inEnqueue
, inEnqueueData
1334 SJME_DEBUG_ONLY_COMMA SJME_DEBUG_FILE_LINE_COPY
)) ||
1336 goto fail_allocWeak
;
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
);
1347 *outAddr
= resultPtr
;
1348 if (outWeak
!= NULL
)
1349 *outWeak
= resultWeak
;
1350 return SJME_ERROR_NONE
;
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
;
1377 (inEnqueue
== NULL
&& inEnqueueData
!= NULL
))
1378 return SJME_ERROR_NULL_ARGUMENTS
;
1381 sjme_thread_barrier();
1383 /* Recover the link. */
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. */
1395 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
1397 return sjme_error_default(error
);
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
);
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
;
1425 sjme_thread_barrier();
1427 /* Recover the link. */
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. */
1435 if (sjme_error_is(error
= sjme_thread_spinLockGrab(
1437 return sjme_error_default(error
);
1439 /* No weak reference here? */
1442 error
= SJME_ERROR_NOT_WEAK_REFERENCE
;
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
);
1452 if (sjme_error_is(error
))
1453 return sjme_error_default(error
);
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
;
1469 return SJME_ERROR_NULL_ARGUMENTS
;
1474 return SJME_ERROR_NONE
;
1476 /* Obtain weak reference. */
1478 if (sjme_error_is(error
= sjme_alloc_weakRefGet(sub
,
1479 &weak
)) || weak
== NULL
)
1480 return sjme_error_default(error
);
1483 #if defined(SJME_CONFIG_DEBUG)
1484 if (sjme_error_is(error
= sjme_alloc_weakDeleteR(&weak
,
1487 if (sjme_error_is(error
= sjme_alloc_weakDelete(&weak
)))
1489 return sjme_error_default(error
);
1491 /* Clear pointer if this was wiped. */
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
;
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
,
1514 "Link %08p: %s %dB in %s (%s:%d)",
1516 (rover
->space
== SJME_ALLOC_POOL_SPACE_USED
? "USED" : "FREE"),
1518 rover
->debugFunction
,
1523 return SJME_ERROR_NONE
;