Skip to content

Drop AccessStruct#242

Open
reinauer wants to merge 41 commits into
cnvogelg:mainfrom
reinauer:Drop_AccessStruct
Open

Drop AccessStruct#242
reinauer wants to merge 41 commits into
cnvogelg:mainfrom
reinauer:Drop_AccessStruct

Conversation

@reinauer
Copy link
Copy Markdown
Contributor

@reinauer reinauer commented Apr 7, 2026

Based on top of my other patch... this one cleans up the code base to get rid of AccessStruct dependencies as @cnvogelg mentioned they are obsolete.

reinauer added 30 commits April 2, 2026 22:37
AmiFuse starts handlers before a scheduler-backed task exists,
so signal allocation and message ports still need to work when
ctx.task is unset.

Teach the lexec helpers to keep fallback signal state,
preserve the historical signal 0 CreateMsgPort() behavior,
auto-track port-manager queues, and make empty GetMsg() return
None.

Add a pytask that exercises this no-task path directly
through vamos so the handler startup semantics stay covered.
Run the fallback exec pytask with a temporary HOME so
vamos creates its ram: volume tree in a writable location.

This keeps the new coverage working under normal pytest
runs instead of depending on ~/.vamos being writable.
AmigaOS does not fail an otherwise valid ClockData just
because the weekday field is invalid or inconsistent.

Write Sunday-based weekdays when filling ClockData so the
stored value matches Amiga numbering, and stop rejecting
valid dates when the stored weekday does not match Python's
weekday numbering.

Update the util_date coverage and add unit tests for the
weekday mapping and the relaxed read path.
Handler startup reaches exec.library before the scheduler-backed
wait and message paths are fully available.

Initialize the remaining exec global lists, preserve the
fallback Wait() bookkeeping, zero new IORequests, record their
reply port metadata, and add the generic device I/O helpers for
DoIO/SendIO/CheckIO/WaitIO.

Add pytask coverage for exec base list setup, IORequest
initialization, and the remaining fallback Wait() path.
Serve timer.device requests through BeginIO so exec.library's
new DoIO/SendIO path can drive TimeRequest-based callers.

Handle TR_GETSYSTIME via the TimeRequest layout and keep the
request state consistent for reply-based I/O.

Add pytask coverage for DoIO/SendIO/WaitIO against timer.device
and keep the existing timer pytask and gcc test_devtimer suite
green.
Some handler paths open input.device and expect BeginIO to
complete successfully even though they do not rely on richer
input semantics.

Register input.device, provide the minimal BeginIO/AbortIO
implementation, and add pytask coverage that opens the device
through exec.library and completes a request.
WaitPkt needs to pull packets from the current process message
port, and ReplyPkt must send the reply back to the packet's
original reply port even after dp_Port is rewritten for the
current process.

Restore WaitPkt on top of the exec port manager, preserve the
original reply destination in ReplyPkt, normalize signed result
values, and keep the message links consistent when packets move
through the queue.

Add a process-mode pytask that exercises empty WaitPkt,
message delivery, and ReplyPkt routing.
Implement the DOS list helpers needed by handler startup and add a
minimal CreateNewProc() path for child process setup.

Cover FindDosEntry(), AttemptLockDosList(), MakeDosEntry(),
AddDosEntry(), RemDosEntry(), FreeDosEntry(), and
CreateNewProc() with pytask coverage.
Keep invalid ClockData failures out of normal output by logging
Date2Amiga() and CheckDate() invalid-date diagnostics at debug
level instead.

Add unit coverage for the invalid ClockData path so normal log
levels stay quiet.
Use each partition's own DosEnvec geometry when computing
partition block counts instead of the RDB header's global
cyl_blks value.

Also report partition ratios against the physical disk total and
add unit coverage for the mixed-geometry case.
SFS starts a helper process named "SFS DosList handler" and then
polls FindPort() for that name during startup.

The rebased tree registered the child pr_MsgPort for message
delivery, but never gave it a name and only searched the public
port list in FindPort(). That left SFS spinning forever waiting
for a port that was already present in the port manager.

Name child message ports from NP_Name and fall back to
registered-port lookup by mp_Node.ln_Name when FindPort()
misses the public list.
SFS uses intuition.library EasyRequestArgs() during startup when it
needs to surface a request. The rebased tree still had the helper
with an old single-argument signature, so the stub generator
treated it as missing and returned d0=0.

Match the fd signature and return success from the compatibility
stub so startup can continue.
SFS initializes a large set of MinList structures during startup.
The rebased tree left NewMinList() as a missing exec.library stub,
so those list heads never became valid and later startup work fell
apart.

Initialize the MinList header in place and cover it with a pytask
regression test.
Wire the private exec vectors at -510 and -516 to their real
RawMayGetChar and RawPutChar names, and send RawPutChar
output to the host debug stream.

This lets handlers that log through RawPutChar show their
serial-style debug output under vamos instead of dropping it.
RawMayGetChar currently reports no pending input.
Add pytask coverage for a buffered putproc callback that updates
a (buffer, remaining) pair through the normal RawDoFmt runner
path.

While touching RawDoFmt, factor the existing known C-string
callback detection into small helpers without changing behavior.
Cache the hot IORequest and MsgPort field offsets during exec
setup and use raw memory access in the BeginIO, SendIO, CheckIO,
and WaitIO path.

This avoids rebuilding AccessStruct wrappers for the same fields on
every device I/O call. In the current PFS benchmark it cuts the direct
worker profile from about 3.83M calls to 3.02M and reduces the
_dispatch_begin_io hotspot noticeably while keeping the matrix and
pytest coverage green.
Compile the register-to-argument readers once when building a
library stub instead of repeating the issubclass dispatch on every
call.

This cuts overhead in the hot stub base_func path without changing the
wrapped method signatures. In the current PFS benchmark it drops the
3-run average from about 1.265s to 1.137s and reduces the direct worker
profile to about 2.97M calls while keeping pytest and the matrix green.
Cache the resolved index path for dotted field lookups so
AccessStruct stops splitting names and walking field defs on
every access.

This keeps the hot struct access path on cached indices while
preserving the same external lookup behavior.
Convert FileManager, LockManager and MatchFirstNext to the

generated struct wrappers and move Lock onto typed fields.

This drops direct AccessStruct use from the DOS-side packet and
matcher helpers while keeping Lock compatible with the older
FileInfoBlock callers that DosLibrary still drives through
AccessStruct. That shrinks the remaining cleanup surface without
forcing the wider allocator and DosLibrary work into the same
change.
Replace the AccessStruct-based MemHeader, MemChunk and

SignalSemaphore accessors with generated struct wrappers.

These helpers only need fixed field reads and writes, so the
string-based wrapper adds churn without buying anything. Switching
now removes more obsolete callers while staying clear of the
separate allocator API cleanup.
Convert DosList and MatchFirstNext to generated struct wrappers,
and switch the adjacent DosLibrary helpers to the same typed
accessors.

These paths share the same DOS list, anchor, file info and device
proc state, so keeping them together removes another batch of
obsolete AccessStruct callers without dragging allocator API work
into the change.
Replace the remaining direct AccessStruct callers in DosLibrary
with generated struct wrappers for the packet, date, segment,
CLI and library state touched by these helpers.

This finishes the caller-side cleanup in vamos without changing
the allocator API yet. After this, the only remaining non-test
AccessStruct users are the wrapper itself and mem.alloc.
alloc_struct(), map_struct(), and alloc_lib() were the last places
that still constructed AccessStruct. They also define the Memory
object API that the DOS runtime still uses via mem.access, so simply
dropping the helper would force a much larger conversion.

Replace AccessStruct construction with a small adapter around the
generated struct instance and store that typed object on Memory.
This keeps mem.access working for old callers while giving new code
a direct mem.struct handle and removes allocator-side dependency on
AccessStruct.
The remaining AccessStruct cleanup is now concentrated in Process and
DosLibrary, but a few smaller DOS helpers still used mem.access for
simple field updates.

Convert run_command(), CSource, and FileHandle to use the typed struct
objects returned by the allocator. Add focused tests for CSource
memory round-tripping and FileHandle allocation so these mechanical
conversions stay covered while the larger Process cleanup is still
pending.
Process was the largest remaining user of mem.access outside
DosLibrary. Most of its field updates are direct process, CLI, and
packet initialization, so keeping string-path access there only added
compatibility overhead.

Convert the process runtime helpers to use typed structs directly for
CLI setup, shell packet setup, process field initialization, and the
input/output/current-dir helpers. This also fixes the stale self.clip
reference in the shell startup-file path while preserving the old
BPTR-as-address behaviour.
DosLibrary was the last substantial runtime user of mem.access.
The remaining call sites were all direct field updates in shell
variables, RDArgs, process I/O state, and DOS object/list helpers,
so keeping string-path access there only preserved compatibility
plumbing.

Convert those paths to typed structs and store local shell variables
as LocalVarStruct objects instead of access wrappers. Rename the
library's typed base field to lib_struct so the remaining access
matches are only the allocator compatibility layer.
No in-tree runtime code still depends on Memory.access or the
allocator-side access shim. Keeping those wrappers in alloc.py only
preserved an extra API layer around typed structs and hid whether
callers were still using the old path.

Remove Memory.access, MemoryAlloc.access, and the internal struct
access compatibility adapter. Keep Memory wrappers for ownership and
labels, but make struct allocations expose only the typed struct
object. Update the allocator tests to check the direct binding
behaviour instead of the removed compatibility surface.
AccessStruct is no longer used by the vamos runtime or allocator.
Keeping it in the umbrella astructs import kept the obsolete helper in
the default public surface even after the caller cleanup was finished.

Stop re-exporting AccessStruct from astructs/__init__.py and update
the dedicated unit test to import it from its direct module instead.
This keeps the standalone compatibility module available without
presenting it as part of the primary typed-struct API.
Teach MemoryAlloc to return bound typed structs directly for
callers that want astruct objects instead of Memory wrappers.

Keep alloc_struct() for compatibility, but add alloc_astruct()
and let TypeBase.alloc() reuse the bound object when no setup
kwargs require a second instance. This removes one layer of
wrapper churn from hot typed-struct paths without changing raw
memory allocation APIs.
Switch the DOS and exec runtime helpers away from wrapper
Memory objects where they only allocated a struct and then
immediately dereferenced .struct.

Use alloc_astruct() for owned allocations and direct typed views
for mapped structs so the hot paths stay on typed objects. This
keeps the caller code simpler and reduces struct wrapper churn in
process, dos list, file handle, packet, and port handling.
reinauer added 11 commits April 6, 2026 23:06
Keep the Memory wrapper for compatibility APIs that still expose
.struct, but store lightweight ownership records for typed
allocations that only need addr, size, and label for free paths.

This removes one extra wrapper object from alloc_astruct() and
alloc_alib() while preserving allocator bookkeeping, labels, and
get_memory() lookup semantics.
Route AmigaStruct.alloc() and Library.alloc() through the typed
allocator fast paths instead of allocating a wrapper first and
binding a second object on top.

This lets typed callers keep a single bound object with the new
lightweight ownership record, and the updated unit tests lock down
the direct-allocation contract for structs and libraries.
Install descriptors for struct fields so normal attribute access
uses precomputed index paths instead of name-based lookup.

Also switch internal struct, pointer, and base-type state to
object.__setattr__ on hot paths. This cuts Python attribute
dispatch and field wrapper churn during load-heavy workloads.

This speeds up AmiFUSE load tests by about 32%.
Add a fast bind path for typed struct fields so lazy field
creation no longer runs the normal constructor path on every
field access. This keeps aliases like List correct via a bind
setup hook while reducing typed field dispatch cost.

This change speeds up AmiFUSE load tests by about 15%
Bound structs always allocated a free-ref list and ran setup()
even when no keyword initialization was requested. That added
constructor work to every plain typed view created through the
field access path.

Use a shared empty tuple until setup kwargs are present. In
AmiFuse load tests this improves steady-state medians by
about 1.3% on average across PFS3, SFS, and FFS.
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