Skip to content

CBL-8494: Use bit 1 instead of the high bit for AtomicWrapper busy flag#308

Merged
pasin merged 1 commit into
release/4.1from
fix/cbl-8494
Jun 21, 2026
Merged

CBL-8494: Use bit 1 instead of the high bit for AtomicWrapper busy flag#308
pasin merged 1 commit into
release/4.1from
fix/cbl-8494

Conversation

@borrrden

Copy link
Copy Markdown
Member

AtomicWrapper's spinlock stored its "busy" flag in the high bit of the pointer (bit 63 on 64-bit). On 64-bit Android, Bionic's tagged pointers (TBI) place an allocator tag in the top byte of every heap pointer, so bit 63 is frequently already set. getAndLock() then reads the slot as permanently busy and spins forever, wedging the owning thread with no crash or ANR. The high bit is also a real address bit on 32-bit ABIs.

Move the flag to bit 1 (value 2). RefCounted has an atomic<int32_t> member, so it is always >= 4-aligned and bits 0 and 1 are guaranteed zero in any stored pointer on every ABI. Bit 0 stays reserved for Fleece's mutable-Value low-bit tag. getAndLock() already returns the bit-cleared value, so no dereference-site masking is needed.

AtomicWrapper's spinlock stored its "busy" flag in the high bit of the
pointer (bit 63 on 64-bit). On 64-bit Android, Bionic's tagged pointers
(TBI) place an allocator tag in the top byte of every heap pointer, so
bit 63 is frequently already set. getAndLock() then reads the slot as
permanently busy and spins forever, wedging the owning thread with no
crash or ANR. The high bit is also a real address bit on 32-bit ABIs.

Move the flag to bit 1 (value 2). RefCounted has an atomic<int32_t>
member, so it is always >= 4-aligned and bits 0 and 1 are guaranteed
zero in any stored pointer on every ABI. Bit 0 stays reserved for
Fleece's mutable-Value low-bit tag. getAndLock() already returns the
bit-cleared value, so no dereference-site masking is needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@borrrden borrrden requested a review from snej June 20, 2026 04:30
@snej

snej commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

I'm not sure bit 1 will work for mutable Fleece values. The ‘Value‘ is really an internal field of a heap-allocated HeapValue struct, withs a 1-byte pad before it to give it an odd address.

The HeapValue will be at least 8-byte aligned in 64-bit, but I don't remember whether the offset of the pad in the whole struct is a multiple of 4

@snej

snej commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

...ok, it appears bit 1 is available. A HeapValue has a vtable ptr, a 32-bit refcount, a 1-byte pad, then the Value. So the offset of the Value is 13, i.e. 1101. If we assume at least 4-byte alignment of heap allocations, bit 1 is always 0.

@snej

snej commented Jun 21, 2026

Copy link
Copy Markdown
Contributor

PS: Happy Father's Day, Jim!

@pasin pasin merged commit c85566d into release/4.1 Jun 21, 2026
3 checks passed
@pasin pasin deleted the fix/cbl-8494 branch June 21, 2026 22:22
@borrrden

Copy link
Copy Markdown
Member Author

PS: Happy Father's Day, Jim!

To you as well!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants