| 1 | """ |
|---|
| 2 | Helpers for managing garbage collection. |
|---|
| 3 | |
|---|
| 4 | :ivar fileDescriptorResource: A garbage-collection-informing resource tracker |
|---|
| 5 | for file descriptors. This is used to trigger a garbage collection when |
|---|
| 6 | it may be possible to reclaim a significant number of file descriptors as |
|---|
| 7 | a result. Register allocation and release of *bare* file descriptors with |
|---|
| 8 | this object (file objects, socket objects, etc, have their own integration |
|---|
| 9 | with the garbage collector and don't need to bother with this). |
|---|
| 10 | |
|---|
| 11 | Ported to Python 3. |
|---|
| 12 | """ |
|---|
| 13 | |
|---|
| 14 | __all__ = [ |
|---|
| 15 | "fileDescriptorResource", |
|---|
| 16 | ] |
|---|
| 17 | |
|---|
| 18 | import gc |
|---|
| 19 | |
|---|
| 20 | import attr |
|---|
| 21 | |
|---|
| 22 | @attr.s |
|---|
| 23 | class _ResourceTracker: |
|---|
| 24 | """ |
|---|
| 25 | Keep track of some kind of resource and trigger a full garbage collection |
|---|
| 26 | when allocations outnumber releases by some amount. |
|---|
| 27 | |
|---|
| 28 | :ivar int _counter: The number of allocations that have happened in excess |
|---|
| 29 | of releases since the last full collection triggered by this tracker. |
|---|
| 30 | |
|---|
| 31 | :ivar int _threshold: The number of excess allocations at which point a |
|---|
| 32 | full collection will be triggered. |
|---|
| 33 | """ |
|---|
| 34 | _counter = attr.ib(default=0) |
|---|
| 35 | _threshold = attr.ib(default=25) |
|---|
| 36 | |
|---|
| 37 | def allocate(self): |
|---|
| 38 | """ |
|---|
| 39 | Register the allocation of an instance of this resource. |
|---|
| 40 | """ |
|---|
| 41 | self._counter += 1 |
|---|
| 42 | if self._counter > self._threshold: |
|---|
| 43 | gc.collect() |
|---|
| 44 | # Garbage collection of this resource has done what it can do. If |
|---|
| 45 | # nothing was collected, it doesn't make any sense to trigger |
|---|
| 46 | # another full collection the very next time the resource is |
|---|
| 47 | # allocated. Start the counter over again. The next collection |
|---|
| 48 | # happens when we again exceed the threshold. |
|---|
| 49 | self._counter = 0 |
|---|
| 50 | |
|---|
| 51 | |
|---|
| 52 | def release(self): |
|---|
| 53 | """ |
|---|
| 54 | Register the release of an instance of this resource. |
|---|
| 55 | """ |
|---|
| 56 | if self._counter > 0: |
|---|
| 57 | # If there were any excess allocations at this point, account for |
|---|
| 58 | # there now being one fewer. It is not helpful to allow the |
|---|
| 59 | # counter to go below zero (as naturally would if a collection is |
|---|
| 60 | # triggered and then subsequently resources are released). In |
|---|
| 61 | # that case, we would be operating as if we had set a higher |
|---|
| 62 | # threshold and that is not desired. |
|---|
| 63 | self._counter -= 1 |
|---|
| 64 | |
|---|
| 65 | fileDescriptorResource = _ResourceTracker() |
|---|