You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardexpand all lines: _posts/2016-08-16-lock-free-gc-handles.md
+12-12
Original file line number
Diff line number
Diff line change
@@ -28,14 +28,14 @@ In addition to programmers’ regular use of handles, Mono uses them in its impl
28
28
29
29
A `GCHandle` object consists of a type and an index, packed together into a 32-bit unsigned integer. To get the value of a handle, say with `WeakReference.Target`, we first look up the array of handle data corresponding to its type, then look up the value at the given index; in pseudocode:
30
30
31
-
```
31
+
```text
32
32
(type, index) = unpack(handle)
33
33
value = handles[type][index]
34
34
```
35
35
36
36
The original implementation of GC handles was based on a bitmap allocator. For each handle type, it stored a bitmap indicating the available slots for allocating new handles, and an array of pointers to their target objects:
@@ -44,7 +44,7 @@ There’s an interesting constraint, though: when we unload an `AppDomain`, we w
44
44
45
45
But if the weak reference has expired, we can’t tell what domain it came from, because we no longer have an object to look at! So for weak references, we kept a parallel array of domain pointers:
@@ -65,21 +65,21 @@ After removing the redundant hash table, the first step toward a new lock-free i
65
65
66
66
We ended up with a single array of *slots* in the following bit format:
67
67
68
-
```
68
+
```text
69
69
PPPPPPPP…0VX
70
70
```
71
71
72
72
Where `PPPP…` are pointer bits, `V` is the “valid” flag, and `X` is the “occupied” flag, packed together with bitwise OR:
73
73
74
-
```
74
+
```text
75
75
slot = pointer | valid_bit | occupied_bit
76
76
```
77
77
78
78
If the “occupied” flag is clear, the slot is free to be claimed by `GCHandle.Alloc`. To allocate a handle, we use a CAS (“compare and swap”, also known as [`Interlocked.CompareExchange`][Interlocked.CompareExchange]) to replace a null slot with a tagged pointer, where the “occupied” and “valid” flags are set:
@@ -38,7 +39,7 @@ This was our only clue, and there are no coincidences when it comes to this sort
38
39
and the `SIGILL` would [always happen to be somewhere between](https://gist.github.com/lewurm/97dff0a56929b56a0fc5ab49af06fd06)`0x40-0x7f` or `0xc0-0xff`.
39
40
We aligned the memory dump to help verify whether the code allocator was doing something funky:
40
41
41
-
```
42
+
```bash
42
43
$ grep SIGILL *.log
43
44
custom_01.log:E/mono (13964): SIGILL at ip=0x0000007f4f15e8d0
44
45
custom_02.log:E/mono (13088): SIGILL at ip=0x0000007f8ff76cc0
@@ -58,12 +59,12 @@ Here is a pseudo version of how `libgcc` [does cache flushing on arm64](https://
0 commit comments