Skip to content

On Windows, ActorBase's layout depends on AETHER_NO_THREADING, so a cooperative libaether and the program built against it disagree #3120

Closed

Description

@nicolas-maman

What happens

On Windows, ActorBase has two layouts: one in code compiled with -DAETHER_NO_THREADING and another in code compiled without it. The cooperative libaether (make stdlib EXTRA_CFLAGS=-DAETHER_NO_THREADING, the setup make ci-coop uses) and a program built against it by ae (which does not pass the flag) disagree on where every field after thread sits.

With main-thread mode removed (#3112), every actor program built that way dies at exit with heap corruption (0xC0000374), in coop_free_actor -> free(actor->spsc_queue). The runtime reads spsc_queue 8 bytes before where the program's spawn left it. Before #3112 the same mismatch was there, but main_thread_only's 4 bytes happened to absorb it in padding, so the offsets agreed by luck.

offsetof measured with MinGW GCC 16.2, x86_64:

tree flags sizeof thread spsc_queue dead inflight
before #3112 threads 1680 1568 1592 1632 1676
before #3112 -DAETHER_NO_THREADING 1680 1568 1592 1632 1676
after #3112 threads 1680 1568 1592 1632 1676
after #3112 -DAETHER_NO_THREADING 1672 1568 1584 1624 1668

Why

runtime/utils/aether_thread.h takes its no-threads branch first, whatever the platform. That branch says Windows is "handled by the #elif _WIN32 path below (not here)", but with AETHER_NO_THREADING it is not. MinGW defines none of _POSIX_THREADS, __unix__, __linux__ and the others the branch tests, so Windows falls to the bare-metal typedefs: typedef int pthread_t, int mutexes and conditions. The threaded Windows path defines pthread_t as void* (a HANDLE), the mutex as a CRITICAL_SECTION-shaped struct and the condition as a CONDITION_VARIABLE-shaped one.

On Linux and macOS both builds include <pthread.h>, so the layouts agree there, and ci-coop (Linux only) passes.

Fix

On Windows, the no-threads branch uses the same type definitions as the threaded Windows path, so a struct's layout never depends on the flag. Add a test that compiles every runtime struct a program shares with libaether, with and without the flag, and compares their layouts.

Activity

  1. nicolas-maman commented on Oct 26, 2026

    @nicolas-maman
    CollaboratorAuthor

    Fixed by 74d9ccc, "One Windows thread-type layout with and without AETHER_NO_THREADING (#3120)", which merged with #3101 (the commit named the issue without a closing keyword, so it stayed open). Checked on main at 00df4c7 (0.848.0), MinGW gcc, Windows: tests/integration/thread_types_layout passes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions