gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/pkg/refs/README.md (about) 1 # Reference Counting 2 3 Go does not offer a reliable way to couple custom resource management with 4 object lifetime. As a result, we need to manually implement reference counting 5 for many objects in gVisor to make sure that resources are acquired and released 6 appropriately. For example, the filesystem has many reference-counted objects 7 (file descriptions, dentries, inodes, etc.), and it is important that each 8 object persists while anything holds a reference on it and is destroyed once all 9 references are dropped. 10 11 We provide a template in `refs_template.go` that can be applied to most objects 12 in need of reference counting. It contains a simple `Refs` struct that can be 13 incremented and decremented, and once the reference count reaches zero, a 14 destructor can be called. Note that there are some objects (e.g. `gofer.dentry`, 15 `overlay.dentry`) that should not immediately be destroyed upon reaching zero 16 references; in these cases, this template cannot be applied. 17 18 # Reference Checking 19 20 Unfortunately, manually keeping track of reference counts is extremely error 21 prone, and improper accounting can lead to production bugs that are very 22 difficult to root cause. 23 24 We have several ways of discovering reference count errors in gVisor. Any 25 attempt to increment/decrement a `Refs` struct with a count of zero will trigger 26 a sentry panic, since the object should have been destroyed and become 27 unreachable. This allows us to identify missing increments or extra decrements, 28 which cause the reference count to be lower than it should be: the count will 29 reach zero earlier than expected, and the next increment/decrement--which should 30 be valid--will result in a panic. 31 32 It is trickier to identify extra increments and missing decrements, which cause 33 the reference count to be higher than expected (i.e. a “reference leak”). 34 Reference leaks prevent resources from being released properly and can translate 35 to various issues that are tricky to diagnose, such as memory leaks. The 36 following section discusses how we implement leak checking. 37 38 ## Leak Checking 39 40 When leak checking is enabled, reference-counted objects are added to a global 41 map when constructed and removed when destroyed. Near the very end of sandbox 42 execution, once no reference-counted objects should still be reachable, we 43 report everything left in the map as having leaked. Leak-checking objects 44 implement the `CheckedObject` interface, which allows us to print informative 45 warnings for each of the leaked objects. 46 47 Leak checking is provided by `refs_template`, but objects that do not use the 48 template will also need to implement `CheckedObject` and be manually 49 registered/unregistered from the map in order to be checked. 50 51 Note that leak checking affects performance and memory usage, so it should only 52 be enabled in testing environments. 53 54 ## Debugging 55 56 Even with the checks described above, it can be difficult to track down the 57 exact source of a reference counting error. The error may occur far before it is 58 discovered (for instance, a missing `IncRef` may not be discovered until a 59 future `DecRef` makes the count negative). To aid in debugging, `refs_template` 60 provides the `enableLogging` option to log every `IncRef`, `DecRef`, and leak 61 check registration/unregistration, along with the object address and a call 62 stack. This allows us to search a log for all of the changes to a particular 63 object's reference count, which makes it much easier to identify the absent or 64 extraneous operation(s). The reference-counted objects that do not use 65 `refs_template` also provide logging, and others defined in the future should do 66 so as well.