feat(rtl): mutex-protect weak reference table for thread safety
Add a pthread mutex around all weak reference table operations (_WeakAssign, _WeakClear, _WeakZeroSlots). The table is a shared global data structure, and concurrent _ClassRelease calls from multiple threads could corrupt the bucket chains without locking. The mutex is coarse-grained (one lock for the entire table) but sufficient because weak references are rare relative to strong ARC operations.
This commit is contained in:
parent
e093be0b70
commit
70a22267cc
|
|
@ -12,24 +12,28 @@
|
||||||
A weak reference is a slot (variable or field) pointing at a class
|
A weak reference is a slot (variable or field) pointing at a class
|
||||||
instance without contributing to its refcount. When the backing
|
instance without contributing to its refcount. When the backing
|
||||||
object's last strong reference is released and the object is about to
|
object's last strong reference is released and the object is about to
|
||||||
be freed, every registered weak slot pointing at it is nil'd — so a
|
be freed, every registered weak slot pointing at it is nil'd -- so a
|
||||||
subsequent dereference sees nil rather than dangling memory.
|
subsequent dereference sees nil rather than dangling memory.
|
||||||
|
|
||||||
Design: one global open-chained hash table, keyed on the user_ptr of
|
Design: one global open-chained hash table, keyed on the user_ptr of
|
||||||
the backing object. Each entry carries a linked list of slot
|
the backing object. Each entry carries a linked list of slot
|
||||||
addresses (pointers-to-pointers) that reference it. Inserts and
|
addresses (pointers-to-pointers) that reference it. Inserts and
|
||||||
removals are O(bucket-chain + slot-list) — acceptable because weak
|
removals are O(bucket-chain + slot-list) -- acceptable because weak
|
||||||
references are rare relative to strong references.
|
references are rare relative to strong references.
|
||||||
|
|
||||||
|
Thread safety: a single pthread mutex protects all table mutations.
|
||||||
|
The lock is coarse-grained but sufficient because weak references
|
||||||
|
are infrequent relative to strong ARC operations.
|
||||||
|
|
||||||
API:
|
API:
|
||||||
_WeakAssign(slot, new_target) — register slot under new_target and
|
_WeakAssign(slot, new_target) -- register slot under new_target and
|
||||||
store new_target at *slot; any prior
|
store new_target at *slot; any prior
|
||||||
registration for *slot is removed.
|
registration for *slot is removed.
|
||||||
new_target may be nil (just unregister).
|
new_target may be nil (just unregister).
|
||||||
_WeakClear(slot) — unregister and zero *slot. Called at
|
_WeakClear(slot) -- unregister and zero *slot. Called at
|
||||||
scope exit and field cleanup for weak
|
scope exit and field cleanup for weak
|
||||||
declarations.
|
declarations.
|
||||||
_WeakZeroSlots(target) — nil every slot pointing at target and
|
_WeakZeroSlots(target) -- nil every slot pointing at target and
|
||||||
drop target's entry from the table.
|
drop target's entry from the table.
|
||||||
Called from _ClassRelease at refcount
|
Called from _ClassRelease at refcount
|
||||||
zero, before the field-cleanup fn
|
zero, before the field-cleanup fn
|
||||||
|
|
@ -65,9 +69,16 @@ type
|
||||||
|
|
||||||
function _BlaiseGetMem(Size: Integer): Pointer; external name '_BlaiseGetMem';
|
function _BlaiseGetMem(Size: Integer): Pointer; external name '_BlaiseGetMem';
|
||||||
procedure _BlaiseFreeMem(Ptr: Pointer); external name '_BlaiseFreeMem';
|
procedure _BlaiseFreeMem(Ptr: Pointer); external name '_BlaiseFreeMem';
|
||||||
|
function pthread_mutex_init(Mutex: Pointer; Attr: Pointer): Integer;
|
||||||
|
external name 'pthread_mutex_init';
|
||||||
|
function pthread_mutex_lock(Mutex: Pointer): Integer;
|
||||||
|
external name 'pthread_mutex_lock';
|
||||||
|
function pthread_mutex_unlock(Mutex: Pointer): Integer;
|
||||||
|
external name 'pthread_mutex_unlock';
|
||||||
|
|
||||||
var
|
var
|
||||||
WeakTable: array[0..255] of PWeakEntry;
|
WeakTable: array[0..255] of PWeakEntry;
|
||||||
|
WeakMutex: array[0..5] of Int64;
|
||||||
|
|
||||||
function WeakHash(Ptr: Pointer): Integer;
|
function WeakHash(Ptr: Pointer): Integer;
|
||||||
begin
|
begin
|
||||||
|
|
@ -152,6 +163,7 @@ var
|
||||||
begin
|
begin
|
||||||
if Slot = nil then
|
if Slot = nil then
|
||||||
Exit;
|
Exit;
|
||||||
|
pthread_mutex_lock(@WeakMutex);
|
||||||
WeakUnregister(Slot);
|
WeakUnregister(Slot);
|
||||||
PP := Slot;
|
PP := Slot;
|
||||||
PP^ := NewTarget;
|
PP^ := NewTarget;
|
||||||
|
|
@ -159,14 +171,21 @@ begin
|
||||||
begin
|
begin
|
||||||
E := WeakFindOrCreate(NewTarget);
|
E := WeakFindOrCreate(NewTarget);
|
||||||
if E = nil then
|
if E = nil then
|
||||||
|
begin
|
||||||
|
pthread_mutex_unlock(@WeakMutex);
|
||||||
Exit;
|
Exit;
|
||||||
|
end;
|
||||||
S := PWeakSlot(_BlaiseGetMem(16));
|
S := PWeakSlot(_BlaiseGetMem(16));
|
||||||
if S = nil then
|
if S = nil then
|
||||||
|
begin
|
||||||
|
pthread_mutex_unlock(@WeakMutex);
|
||||||
Exit;
|
Exit;
|
||||||
|
end;
|
||||||
S^.Addr := Slot;
|
S^.Addr := Slot;
|
||||||
S^.Next := E^.Slots;
|
S^.Next := E^.Slots;
|
||||||
E^.Slots := S;
|
E^.Slots := S;
|
||||||
end;
|
end;
|
||||||
|
pthread_mutex_unlock(@WeakMutex);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure _WeakClear(Slot: Pointer);
|
procedure _WeakClear(Slot: Pointer);
|
||||||
|
|
@ -175,9 +194,11 @@ var
|
||||||
begin
|
begin
|
||||||
if Slot = nil then
|
if Slot = nil then
|
||||||
Exit;
|
Exit;
|
||||||
|
pthread_mutex_lock(@WeakMutex);
|
||||||
WeakUnregister(Slot);
|
WeakUnregister(Slot);
|
||||||
PP := Slot;
|
PP := Slot;
|
||||||
PP^ := nil;
|
PP^ := nil;
|
||||||
|
pthread_mutex_unlock(@WeakMutex);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
procedure _WeakZeroSlots(Target: Pointer);
|
procedure _WeakZeroSlots(Target: Pointer);
|
||||||
|
|
@ -192,6 +213,7 @@ var
|
||||||
begin
|
begin
|
||||||
if Target = nil then
|
if Target = nil then
|
||||||
Exit;
|
Exit;
|
||||||
|
pthread_mutex_lock(@WeakMutex);
|
||||||
H := WeakHash(Target);
|
H := WeakHash(Target);
|
||||||
Prev := nil;
|
Prev := nil;
|
||||||
E := WeakTable[H];
|
E := WeakTable[H];
|
||||||
|
|
@ -214,11 +236,16 @@ begin
|
||||||
else
|
else
|
||||||
Prev^.Next := Dead^.Next;
|
Prev^.Next := Dead^.Next;
|
||||||
_BlaiseFreeMem(Dead);
|
_BlaiseFreeMem(Dead);
|
||||||
|
pthread_mutex_unlock(@WeakMutex);
|
||||||
Exit;
|
Exit;
|
||||||
end;
|
end;
|
||||||
Prev := E;
|
Prev := E;
|
||||||
E := E^.Next;
|
E := E^.Next;
|
||||||
end;
|
end;
|
||||||
|
pthread_mutex_unlock(@WeakMutex);
|
||||||
end;
|
end;
|
||||||
|
|
||||||
|
initialization
|
||||||
|
pthread_mutex_init(@WeakMutex, nil);
|
||||||
|
|
||||||
end.
|
end.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue