34 lines
1.7 KiB
Markdown
34 lines
1.7 KiB
Markdown
In order to buy some performance on the common, uninstrumented, fast path, we replace repeated
|
|
checks for both allocation instrumentation and allocator changes by a single function table
|
|
dispatch, and templatized allocation code that can be used to generate either instrumented
|
|
or uninstrumented versions of allocation routines.
|
|
|
|
When we call an allocation routine, we always indirect through a thread-local function table that
|
|
either points to instrumented or uninstrumented allocation routines. The instrumented code has a
|
|
`kInstrumented` = true template argument (or `kIsInstrumented` in some places), the uninstrumented
|
|
code has `kInstrumented` = false.
|
|
|
|
The function table is thread-local. There appears to be no logical necessity for that; it just
|
|
makes it easier to access from compiled Java code.
|
|
|
|
- The function table is switched out by `InstrumentQuickAllocEntryPoints[Locked]`, and a
|
|
corresponding `UninstrumentQuickAlloc`... function.
|
|
|
|
- These in turn are called by `SetStatsEnabled()`, `SetAllocationListener()`, et al, which
|
|
require the mutator lock is not held.
|
|
|
|
- With a started runtime, `SetEntrypointsInstrumented()` calls `ScopedSupendAll(`) before updating
|
|
the function table.
|
|
|
|
Mutual exclusion in the dispatch table is thus ensured by the fact that it is only updated while
|
|
all other threads are suspended, and is only accessed with the mutator lock logically held,
|
|
which inhibits suspension.
|
|
|
|
To ensure correctness, we thus must:
|
|
|
|
1. Suspend all threads when swapping out the dispatch table, and
|
|
2. Make sure that we hold the mutator lock when accessing it.
|
|
3. Not trust kInstrumented once we've given up the mutator lock, since it could have changed in the
|
|
interim.
|
|
|