kernel: M3 shared-fate — shared-memory frames live while any mapping does
Each address space that maps a shared-memory region now holds its own reference, recorded on the AddressSpaceRef and dropped when the space is destroyed — so 'last reference' means no handles AND no mappings, and a region's frames can no longer be freed out from under a sibling thread (or any other live mapper) when the handle-holding task dies. The group-death notification still posts after every mapping release. (docs/shared-fate-plan.md M3)
This commit is contained in:
@@ -185,7 +185,18 @@ const AddressSpaceRef = struct {
|
||||
group_supervisor: u32 = 0,
|
||||
group_reason: abi.ExitReason = .exited,
|
||||
group_exit_endpoint: ?*anyopaque = null,
|
||||
// Shared-memory objects mapped into this space (docs/shared-fate-plan.md M3).
|
||||
// Each mapping holds one reference to its object, dropped through
|
||||
// `space_mapping_release_hook` when the space is destroyed — so "last
|
||||
// reference" means no handles AND no mappings, and frames can never be freed
|
||||
// while a live space still maps them. Opaque: the object type is the IPC
|
||||
// layer's.
|
||||
mappings: [maximum_space_mappings]?*anyopaque = .{null} ** maximum_space_mappings,
|
||||
};
|
||||
|
||||
/// Shared-memory mappings one address space can hold — matches the per-task
|
||||
/// handle table's order of magnitude; `shared_memory_map` fails when full.
|
||||
const maximum_space_mappings = 16;
|
||||
var address_space_refs = [_]AddressSpaceRef{.{}} ** maximum_tasks;
|
||||
var address_space_destroy_count: u64 = 0;
|
||||
|
||||
@@ -265,11 +276,19 @@ fn releaseAddressSpace(root: u64) void {
|
||||
const supervisor = entry.group_supervisor;
|
||||
const reason = entry.group_reason;
|
||||
const endpoint = entry.group_exit_endpoint;
|
||||
const mappings = entry.mappings;
|
||||
entry.* = .{};
|
||||
architecture.destroyAddressSpace(root);
|
||||
address_space_destroy_count += 1;
|
||||
// Release the mapping references now that no mapping exists —
|
||||
// `device_grant`-tagged leaves kept destroyAddressSpace's sweep off
|
||||
// the frames, so this drop is what may actually free them (M3).
|
||||
if (space_mapping_release_hook) |release| {
|
||||
for (mappings) |slot| if (slot) |object| release(object);
|
||||
}
|
||||
// The group-death moment: the space is gone, every member is dead.
|
||||
// process.zig posts the leader's deferred exit publication here.
|
||||
// process.zig posts the leader's deferred exit publication here —
|
||||
// last, so the supervisor's notification postdates every release.
|
||||
if (was_dying) if (group_exit_hook) |hook| hook(leader, supervisor, reason, endpoint);
|
||||
}
|
||||
return;
|
||||
@@ -304,6 +323,30 @@ pub fn markGroupDyingLocked(root: u64, leader: u32, supervisor: u32, reason: abi
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Called (lock held) once per recorded shared-memory mapping when an address
|
||||
/// space is destroyed — drops the mapping's object reference. Registered by
|
||||
/// process.zig (the object type lives in the IPC layer).
|
||||
pub var space_mapping_release_hook: ?*const fn (*anyopaque) void = null;
|
||||
|
||||
/// Record a shared-memory mapping on `root`'s space; its reference is dropped
|
||||
/// via `space_mapping_release_hook` at space destruction. Returns false —
|
||||
/// recording nothing — if the space has no live entry or its mapping table is
|
||||
/// full. On true, the caller has transferred one object reference to the space.
|
||||
/// Caller holds the lock.
|
||||
pub fn recordSpaceMappingLocked(root: u64, object: *anyopaque) bool {
|
||||
for (&address_space_refs) |*entry| {
|
||||
if (entry.count == 0 or entry.root != root) continue;
|
||||
for (&entry.mappings) |*slot| {
|
||||
if (slot.* == null) {
|
||||
slot.* = object;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Whether `root`'s group is already dying. Caller holds the lock.
|
||||
pub fn groupDyingLocked(root: u64) bool {
|
||||
for (&address_space_refs) |*entry| {
|
||||
|
||||
Reference in New Issue
Block a user