mirror of
https://github.com/Z3Prover/z3
synced 2026-08-11 00:11:48 +00:00
Creating and disposing `Context` instances causes unbounded native
memory growth (~12 GB for 100k contexts) because `NativeContext` had no
finalizer — if `Dispose()` was never called, the native Z3 context
leaked permanently. Additionally, both `Context` and `NativeContext` had
delegate lifetime and thread-safety issues in their disposal paths.
## `NativeContext.cs`
- **Add missing finalizer** `~NativeContext() { Dispose(); }` — the root
cause of permanent leaks when callers don't explicitly dispose
- **Atomic disposal** via `Interlocked.Exchange(ref m_ctx, IntPtr.Zero)`
— prevents double-free when `Dispose()` is called concurrently (e.g.
user code + finalizer race)
- **Delegate lifetime** — capture `errHandler` locally +
`GC.KeepAlive(errHandler)` after `Z3_del_context`; the GC could
otherwise collect the error handler callback before the native
destructor finishes
- **Remove dead code** — `GC.SuppressFinalize` in `InitContext()` and
`GC.ReRegisterForFinalize` in `Dispose()` were both no-ops (no finalizer
existed); the latter would have caused infinite finalization with the
new finalizer
- **GC memory pressure** — `GC.AddMemoryPressure(8MB)` on init /
`GC.RemoveMemoryPressure(8MB)` on dispose, guarded by
`m_memPressureAdded` flag, so the GC schedules finalizers promptly when
contexts accumulate
## `Context.cs`
- **Thread-safe disposal** — capture `ctx` and `errHandler` inside the
existing `lock(this)` block; previously both were read outside the lock,
allowing two concurrent callers to both capture the same non-zero `ctx`
and double-free it
- **Delegate lifetime** — same `errHandler` + `GC.KeepAlive` pattern as
`NativeContext`
- **`GC.SuppressFinalize` placement** — moved inside the `if (m_ctx !=
IntPtr.Zero)` block, before cleanup, per .NET best practice
- **GC memory pressure** — same add/remove pattern, conditioned on
`!is_external` via `m_memPressureAdded` flag
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
|
||
|---|---|---|
| .. | ||
| c++ | ||
| dll | ||
| dotnet | ||
| go | ||
| java | ||
| js | ||
| julia | ||
| mcp | ||
| ml | ||
| python | ||
| api_algebraic.cpp | ||
| api_arith.cpp | ||
| api_array.cpp | ||
| api_ast.cpp | ||
| api_ast_map.cpp | ||
| api_ast_map.h | ||
| api_ast_vector.cpp | ||
| api_ast_vector.h | ||
| api_bv.cpp | ||
| api_config_params.cpp | ||
| api_context.cpp | ||
| api_context.h | ||
| api_datalog.cpp | ||
| api_datalog.h | ||
| api_datatype.cpp | ||
| api_finite_set.cpp | ||
| api_fpa.cpp | ||
| api_goal.cpp | ||
| api_goal.h | ||
| api_log.cpp | ||
| api_model.cpp | ||
| api_model.h | ||
| api_numeral.cpp | ||
| api_opt.cpp | ||
| api_params.cpp | ||
| api_parsers.cpp | ||
| api_pb.cpp | ||
| api_polynomial.cpp | ||
| api_polynomial.h | ||
| api_qe.cpp | ||
| api_quant.cpp | ||
| api_rcf.cpp | ||
| api_seq.cpp | ||
| api_solver.cpp | ||
| api_solver.h | ||
| api_special_relations.cpp | ||
| api_stats.cpp | ||
| api_stats.h | ||
| api_tactic.cpp | ||
| api_tactic.h | ||
| api_util.h | ||
| CMakeLists.txt | ||
| z3.h | ||
| z3_algebraic.h | ||
| z3_api.h | ||
| z3_ast_containers.h | ||
| z3_fixedpoint.h | ||
| z3_fpa.h | ||
| z3_logger.h | ||
| z3_macros.h | ||
| z3_optimization.h | ||
| z3_polynomial.h | ||
| z3_private.h | ||
| z3_rcf.h | ||
| z3_replayer.cpp | ||
| z3_replayer.h | ||
| z3_spacer.h | ||
| z3_v1.h | ||