Skip to content

Conversation

@mgyoo86
Copy link
Collaborator

@mgyoo86 mgyoo86 commented Dec 9, 2025

Summary

Implement automatic detection of "untracked" acquire! calls (from inner functions or parent scope outside @with_pool), preventing pool state corruption in nested scope scenarios. Also includes code organization improvements.

Key Changes

1. Untracked Acquire Detection

  • Problem: Untracked acquire! calls (from inner functions or parent scope) could corrupt arrays on rewind when using typed checkpoint/rewind
  • Solution: Track untracked acquires via _untracked_flags per depth level; automatically fallback to full checkpoint/rewind when detected

2. 1-Based Sentinel Pattern

  • Eliminates isempty() checks in hot paths
  • _checkpoint_n_active=[0], _checkpoint_depths=[0] always have sentinel values
  • ~2-5% performance improvement in checkpoint/rewind cycles
  • ~20% overhead reduction in acquire! calls (branch elimination in _mark_untracked!)

3. Macro Transformation

  • acquire! calls inside @with_pool are transformed to _acquire_impl!
  • Transformed calls bypass untracked marking (macro already tracks them)
  • External helper calls still use acquire! → properly marked as untracked

4. Code Organization

  • Split core.jl into acquire.jl (~410 lines) and state.jl (~240 lines)

5. Other Improvements

  • Orphaned checkpoint cleanup in nested scope rewind
  • Auto-checkpoint for dynamically created TypedPool inside @with_pool
  • Support acquire_view!, acquire_array! aliases in type extraction

…action

- Add support for acquire!(pool, x) form which generates eltype(x) for
  checkpoint/rewind optimization
- Add unsafe_acquire! recognition to _extract_acquire_types
- Handle eltype(x) expressions in _filter_static_types:
  - External variables: use eltype(x) for typed checkpoint
  - Local variables: fall back to full checkpoint (not defined at checkpoint time)
- Update docstrings to reflect new functionality
_extract_acquire_types tests:
- similar-style acquire!(pool, x) generates eltype(x)
- unsafe_acquire! single and multiple types
- mixed acquire!/unsafe_acquire!/similar-style
- qualified unsafe_acquire! calls
- pool isolation (no AST pollution)

_filter_static_types tests:
- eltype(x) with external variable (usable)
- eltype(x) with local variable (fallback)
- eltype mixed with static types
- complex expressions in eltype

Macro expansion tests:
- Similar-style expansion with external array
- Local array fallback behavior
- unsafe_acquire! type extraction
- Mixed traditional/unsafe/similar-style
_extract_acquire_types tests:
- custom struct type extraction (MyCustomType)
- multiple custom types
- type parameter T (from where clause)
- multiple type parameters (T, S)
- mixed: builtin, custom, type parameter

Integration tests:
- custom type with filtering
- type parameter integration
- type parameter vs local variable conflict (T = eltype(x))
- mixed types with filtering

Macro expansion tests:
- custom struct type expansion
- type parameter T expansion
- mixed builtin/custom/type parameter
…tion

Extends _extract_acquire_types to recognize all acquire function aliases:
- acquire_view! (alias for acquire!)
- acquire_array! (alias for unsafe_acquire!)

This ensures @with_pool macro generates typed checkpoint!/rewind! calls
for code using the explicit naming aliases, not just the base functions.

Includes comprehensive tests for alias type extraction and integration.
Add fields and functions to support detecting and handling acquire calls
from inner functions that the macro cannot see at compile time.

Changes to TypedPool:
- saved_depths: Track which checkpoint depth each saved_stack entry belongs to

Changes to AdaptiveArrayPool:
- check_depth: Current checkpoint nesting depth counter
- had_untracked: Per-depth flag indicating if untracked acquire occurred

New functions:
- all_type_stacks(): Iterator over all TypedPools (6 fixed + others)
- foreach_type_stack(): Callback pattern for TypedPool iteration

Refs: design/untracked_acquire_design.md sections 3.3, 4.5
Implement the core functionality for detecting acquire calls from inner
functions that the macro cannot see at compile time (untracked acquires).

New internal implementation functions:
- _acquire_impl!: Core acquire logic (no marking overhead)
- _unsafe_acquire_impl!: Core unsafe_acquire logic (no marking overhead)
- _mark_untracked!: Mark current depth as having untracked acquire

Updated user-facing functions:
- acquire!: Now wraps _acquire_impl! with _mark_untracked! call
- unsafe_acquire!: Now wraps _unsafe_acquire_impl! with _mark_untracked! call

Updated state management:
- checkpoint!: Increments check_depth, initializes had_untracked flag
- rewind!: Uses saved_depths for accurate pop/restore decision
- _checkpoint_typed_pool!: Helper with depth tracking
- _rewind_typed_pool!: Helper with saved_depths logic
- empty!: Resets check_depth and had_untracked

New aliases for macro transformation:
- _acquire_view_impl! = _acquire_impl!
- _acquire_array_impl! = _unsafe_acquire_impl!

Design: Macro-transformed code calls _*_impl! directly (zero overhead),
while code outside @with_pool calls acquire! wrapper which marks untracked.

Refs: design/untracked_acquire_design.md sections 4.1-4.4
Transform acquire! calls to _acquire_impl! in macro-generated code,
enabling zero-overhead acquire within @with_pool while detecting
untracked acquires from inner functions.

New transformation function:
- _transform_acquire_calls: AST transformation for acquire calls
  - acquire!/acquire_view! → _acquire_impl!
  - unsafe_acquire!/acquire_array! → _unsafe_acquire_impl!
  - Supports qualified names (AAP.acquire!, A.B.C.acquire!, etc.)
  - Only transforms calls matching the target pool name

GlobalRef constants for module-qualified references:
- _ACQUIRE_IMPL_REF: GlobalRef to AdaptiveArrayPools._acquire_impl!
- _UNSAFE_ACQUIRE_IMPL_REF: GlobalRef to AdaptiveArrayPools._unsafe_acquire_impl!

Updated macro code generation:
- _generate_pool_code: Transform body, add had_untracked check
- _generate_function_pool_code: Transform body, add had_untracked check
- Typed rewind now falls back to full rewind if untracked detected

Refs: design/untracked_acquire_design.md sections 4.3, 4.6
Add comprehensive tests for the acquire call transformation function
used by macros to enable zero-overhead acquire within @with_pool.

Test categories:
- Basic transformation: acquire!, unsafe_acquire!, acquire_view!, acquire_array!
- Qualified names: AdaptiveArrayPools.acquire!, AAP.acquire!, A.B.C.acquire!
- Pool name matching: Only transforms calls with matching pool argument
- Recursive transformation: Nested blocks, function calls, mixed pools
- Similar-style: acquire!(pool, x) form transformation
- GlobalRef verification: Correct module binding for _*_impl! functions

Total: 33 new tests for _transform_acquire_calls
…llback

- Change check_depth to 1-indexed (Julia idiomatic, 1 = global scope)
- Add checkpoint-time check: use full checkpoint if parent had untracked acquires
- Silently reset n_active=0 for helper-acquired new types instead of erroring
- Auto-checkpoint new TypedPools created inside @with_pool scope
- Add 4 tests: parent protection, same type protection, new type reset, typed performance
…ames

- Rename TypedPool fields:
  - saved_stack → _checkpoint_n_active (stores n_active at checkpoints)
  - saved_depths → _checkpoint_depths (stores depth of each checkpoint)
- Rename AdaptiveArrayPool fields:
  - check_depth → _current_depth (current scope depth, 1=global)
  - had_untracked → _untracked_flags (per-depth untracked flags)
- Initialize with sentinel values to eliminate isempty() checks:
  - TypedPool: _checkpoint_n_active=[0], _checkpoint_depths=[0]
  - AdaptiveArrayPool: _current_depth=1, _untracked_flags=[false]
- Simplify _rewind_typed_pool! by removing redundant isempty checks
- Update empty!() functions to restore sentinel values

This provides 17-27% speedup in checkpoint/rewind hot paths.
When a nested scope does full checkpoint but typed rewind, checkpoints
from the nested scope remain on the stack (orphaned). This caused
incorrect state restoration when outer scopes performed full rewind.

Changes:
- Add orphaned checkpoint cleanup loop in _rewind_typed_pool!
- Pop stale checkpoints (depth > current) before normal rewind logic
- Add complex nested test cases for L1 @with_pool → L2 untracked → L3 @with_pool
- Add L1→L2→L3 nested tests using unsafe_acquire! instead of acquire!
- Add acquire_array! alias tests in nested scenarios (including 2D)
- Add mixed acquire!/unsafe_acquire!/acquire_array! test in single scenario
- Add acquire_view! alias nested test with ReshapedArray
- Add N-D unsafe_acquire! nested tests (2D, 3D, tuple dimensions)

Verifies that all acquire variants trigger untracked detection correctly
and state management works across nested @with_pool scopes.
Improve code organization by separating concerns:
- acquire.jl (~410 lines): safe_prod, get_view!, get_nd_*!,
  _mark_untracked!, _*_impl!, acquire!, unsafe_acquire!, aliases
- state.jl (~240 lines): checkpoint!, rewind!, empty!,
  @generated typed versions

Include order: types.jl → utils.jl → acquire.jl → state.jl → task_local_pool.jl → macros.jl
@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

❌ Patch coverage is 94.49153% with 13 lines in your changes missing coverage. Please review.
✅ Project coverage is 94.60%. Comparing base (e51db45) to head (26e81d9).
⚠️ Report is 16 commits behind head on master.

Files with missing lines Patch % Lines
src/acquire.jl 77.77% 10 Missing ⚠️
src/macros.jl 95.94% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master       #6      +/-   ##
==========================================
- Coverage   97.27%   94.60%   -2.67%     
==========================================
  Files           6        7       +1     
  Lines         440      575     +135     
==========================================
+ Hits          428      544     +116     
- Misses         12       31      +19     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR implements automatic detection of "untracked" acquire! calls (from helper functions or parent scope) to prevent pool state corruption in nested @with_pool scenarios. It also introduces a 1-based sentinel pattern for improved performance and reorganizes code for better maintainability.

Key Changes

  • Untracked Acquire Detection: Tracks untracked acquire! calls via _untracked_flags per depth level, automatically falling back to full checkpoint/rewind when detected to protect parent scope arrays
  • 1-Based Sentinel Pattern: Eliminates isempty() checks in hot paths by maintaining _checkpoint_n_active=[0] and _checkpoint_depths=[0] sentinel values, providing ~2-5% performance improvement
  • Macro Transformation: Transforms acquire! calls inside @with_pool to _acquire_impl!, bypassing untracked marking overhead since macro already tracks these calls
  • Code Organization: Splits core.jl into acquire.jl (~410 lines) and state.jl (~240 lines) for better separation of concerns

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/types.jl Added _current_depth and _untracked_flags fields to AdaptiveArrayPool; renamed saved_stack to _checkpoint_n_active and added _checkpoint_depths to TypedPool; implemented auto-checkpoint for dynamically created TypedPool instances
src/state.jl New file containing all checkpoint/rewind/empty! implementations with 1-based sentinel pattern and depth-aware rewind logic
src/acquire.jl New file containing acquisition APIs; added _mark_untracked! and internal _acquire_impl!/_unsafe_acquire_impl! functions for macro transformation
src/macros.jl Enhanced type extraction to support unsafe_acquire!, acquire_view!, acquire_array! aliases and eltype() expressions; added _transform_acquire_calls to transform acquire calls to _impl! variants; added untracked flag checks for dynamic fallback
src/AdaptiveArrayPools.jl Updated includes to reference new acquire.jl and state.jl files
test/test_state.jl Added comprehensive nested scope tests covering parent scope protection, helper functions, mixed acquire variants, and N-dimensional arrays; updated allocation tests to reference new field names
test/test_macro_internals.jl Added extensive tests for type extraction with aliases, similar-style forms, custom types, type parameters, and transformation logic
test/test_macro_expansion.jl Added macro expansion tests for new features including similar-style, unsafe_acquire!, and custom types

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mgyoo86 mgyoo86 merged commit bfd4c31 into master Dec 9, 2025
6 of 8 checks passed
@mgyoo86 mgyoo86 deleted the improve/state_manage branch December 9, 2025 23:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant