Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
DeltaBundler: Consistently delete unreachable modules and their depenβ¦
β¦dencies Summary: Fixes a bug that can lead to "unknown module" errors at runtime after an incremental build. The bug occurs when we *defer* the deletion of an unreachable module (because it's a "possible cycle root" = "buffered" by the GC algorithm) but still *eagerly* delete its outbound edges. If the same module becomes reachable again before cycle collection, and is not itself queued for traversal ( = hasn't changed on disk), it would be sent to the client with an empty dependency map, regardless of whether its code has any references to other modules. (See the added regression test for an annotated example.) This bug traces to my interpretation of the paper on which I based Metro's GC algorithm in D36403390 (9065257) (*Concurrent Cycle Collection in Reference Counted Systems* - snippets shown below). In the paper, it isn't clear why `Release(s)` calls `Free` conditionally; I now think this isn't fundamental, but is simply an artifact of how the algorithm in the paper does its bookkeeping. {F803521688} {F803521380} In a conventional GC, *objects can't become reachable once they've become unreachable* - or if they can, it's via a weak reference that is checked for validity before dereferencing. So it's safe for original algorithm to mutate an unreachable object without freeing it, but it isn't safe for Metro, where unreachable modules can become reachable before the next GC pass. There are actually two possible ways to resolve this: 1. Defer mutating the module entirely: If releasing a node that's marked as a possible cycle root, *do nothing* and let the cycle collection pass handle it if necessary. 2. Free the module immediately: Ignore the fact that the node is marked as a possible cycle root, and just delete it as soon as its reference count reaches 0. If something else turns out to depend on the same module, we can recreate it from scratch (at the cost of re-reading from the transformer cache, re-resolving dependencies, etc). This diff implements approach (2) to be consistent with how we (currently) handle the acyclic case. NOTE: It might be worth going to the other extreme and deferring *all* deletions (cyclic and acyclic) to avoid recreating modules that haven't actually changed. This might speed up large incremental builds (major changes to `node_modules`, changing source control revisions while Metro is running, etc). That would be a more invasive change so I'm not tackling it here. Reviewed By: jacdebug, huntie Differential Revision: D41500887 fbshipit-source-id: 586e9bfdcfcdfc87826ea2e1dbdf3805457522b3
- Loading branch information