Manage task memory.

Control dormant stack reclamation and release task-private allocations at explicit lifetime boundaries.

STEP 06

Control stack and region memory.

Flyology has separate controls for stack capacity, dormant stack residency, and task-private object lifetime. None of these controls is a process memory limit.

Stack capacity
Use Ada Storage_Size and the stack-pool snapshot described in Task stack sizing. This controls usable stack sizing; it does not request paging.
Dormant stack residency
Use Flyology.Dormancy for a lightweight task that will wait only on a sufficiently distant timer and can tolerate a slower wake.
Task-private object lifetime
Use Flyology.Memory_Regions when a group of allocations belongs to one task phase and can be finalized and released together.
Shared or transferred data
Keep it in an explicitly shared owner or bounded pool. A task-owned region is not a shared allocator, and references into it must not outlive the region.

Opt a timer sleeper into stack reclamation

Prompt is the default dormancy policy. On supported Linux hosts, Reclaimable asks the kernel to mark applicable stack pages as cold. Page_Out asks the kernel to reclaim applicable pages through the configured swap path.

Set_Policy applies the selected policy to the calling task. Both nondefault policies apply only after a lightweight task switches to its scheduler stack. The task must wait only on a timer with at least Minimum_Wait remaining.

latency-tolerant timer wait
with Flyology.Dormancy;

Flyology.Dormancy.Set_Policy
  (Flyology.Dormancy.Page_Out, Minimum_Wait => 30.0);

delay 300.0;

Flyology.Dormancy.Set_Policy (Flyology.Dormancy.Prompt);

Descriptor waits are excluded because scheduler links can remain on the suspended stack. File waits are excluded because the kernel can own a stack-backed buffer. Native tasks accept only Prompt. On unsupported hosts, the capability queries return false and Flyology requests no page advice.

Use Cold_Advice_Supported and Pageout_Advice_Supported before you report a host capability. Group snapshots report eligible timer waits, advised stacks and bytes, attempts, accepted calls, and failures.

Release task-private allocations as one region

The native or lightweight task that declares Flyology.Memory_Regions.Task_Pool owns it. Each region grows in backing chunks when named-subpool allocators request storage. Explicit release finalizes controlled objects and returns all backing chunks. Pool finalization releases all regions that remain live.

Create_Region returns a Region_Handle for allocations that share one lifetime. A pool declared in a task body is finalized when the task ends, and its live regions are released automatically. Call Release when the region has an earlier lifetime boundary.

Task termination does not reclaim a dynamically allocated pool. The application must finalize and deallocate that pool.

a task-lifetime region released at task termination
with Flyology.Memory_Regions;

procedure Memory_Region_Example is
   task Worker;

   task body Worker is
      Pool : aliased Flyology.Memory_Regions.Task_Pool;

      type Node is record
         Value : Integer;
      end record;
      type Node_Access is access Node;
      for Node_Access'Storage_Pool use Pool;

      Region : Flyology.Memory_Regions.Region_Handle :=
        Flyology.Memory_Regions.Create_Region
          (Pool, Chunk_Storage => 64 * 1_024);

      Head : Node_Access := new (Region) Node'(Value => 42);
   begin
      Head.Value := Head.Value + 1;

      --  Worker termination now finalizes Pool and releases Region.
   end Worker;
begin
   null;
end Memory_Region_Example;

Every allocator for an access type associated with the pool must name a region. An unnamed allocator raises Program_Error. A call from another task raises Ownership_Error. Statistics reports live regions, consumed storage with allocator overhead, and reserved chunk capacity.

Understand the region cost model

Regions group objects that have the same lifetime. The pool allocates these objects from larger chunks and releases them at one cleanup boundary.

Task pool
Stores fixed ownership and accounting state with the Ada subpool controller. Flyology adds no per-pool mutex because only the owning task can use the pool. The tasking runtime briefly locks its subpool list during region attachment and detachment.
Region creation
Allocates one Flyology region descriptor and one runtime registration node while attaching it to the pool. The first object allocation creates the first backing chunk lazily.
Common allocation
Checks task ownership, aligns an address, advances the current chunk offset, and updates counters. The operation takes no region lock. It does not call the underlying heap while the current chunk has room.
Chunk growth
Allocates and links one new chunk when the current chunk is full. The default payload capacity is 64 KiB. A larger object receives enough capacity for its size and alignment.
Bulk cleanup
Finalizes controlled objects and frees the registration node, backing chunks, and region descriptor. Explicit Release cleans one region. Task-end pool finalization cleans all live regions.

For N object allocations in R regions and K chunks, the runtime makes approximately 2R + K management allocations: one descriptor and one registration node per region, plus the chunks. Most of the N object allocations are bump operations in an existing chunk, so this arrangement is most useful when R + K is much smaller than N.

Reclamation is synchronous and frees the corresponding management allocations, although controlled objects still require individual finalization. A lightweight task with many chunks or expensive finalizers can therefore occupy its event-loop pthread until cleanup finishes. Because the allocator checks only the current chunk, unused space in older chunks remains reserved until release.

Reserved_Storage counts chunk payload capacity, not region descriptors, chunk headers, or underlying allocator metadata. Use smaller phase regions and explicit release boundaries when cleanup time or unused chunk space matters.

The current implementation has no region quota, retained-chunk cache, trimming operation, or connection to dormant-stack advice. It does not redirect ordinary Ada allocation. Set admission limits for potentially large phases and inspect region statistics before release. Every access value into a released region is invalid.