Files
gex/tests/integration/programs/thread-parallel.c
velzie d2d6dc7043 apt update finishes: 49 MB of Packages, parsed, exit 0
The fetch already worked; what did not was what apt does with it.

    E: Dynamic MMap ran out of room. Please increase the size of
       APT::Cache-Start. Current value: 28311552.
    E: Problem with MergeList .../binary-arm64_Packages

Bookworm's arm64 index is 49 MB of Packages and 32 MB of Translation-en, and
apt builds its cache in one mapping that it grows as it parses. Started at
the default it reaches about 27 MB and gives up. The size is a decision, not
a defect, so it is a decision the image makes: `/etc/apt/apt.conf.d/99gex`
sets `APT::Cache-Start` to 128 MB, next to the other four lines that already
tell apt it is running somewhere unusual. With that in the rootfs and nothing
on the command line:

    == apt-get update ==
    Get:1 http://deb.debian.org/debian bookworm InRelease [151 kB]
    Get:2 http://deb.debian.org/debian bookworm/main arm64 Packages [8689 kB]
    Get:3 http://deb.debian.org/debian bookworm/main Translation-en [6107 kB]
    Fetched 14.9 MB in 3min 39s
    Reading package lists...
    == apt exit 0 ==
    -rw-r--r-- 49434864 deb.debian.org_debian_dists_bookworm_main_binary-arm64_Packages
    -rw-r--r-- 32699363 deb.debian.org_debian_dists_bookworm_main_i18n_Translation-en

and `apt-cache show bash` answers out of it.

Threads as Workers, continued, and still not working. Four more things that
were one set of bytes for a whole process and have to be one per Worker:
which thread it is running, where it resumes, why it stopped, and whether it
meant to give the stack back -- the last of those is why a thread that handed
the stack back correctly was reported as having crashed inside the shim. A
thread also has to be *entered* on its own registers, which it was not: `run`
starts at the first thread's context, so four threads all entered at 0x1000,
re-executed whatever the main thread was in the middle of, and one of them
came out the other side having forked. And a Worker that owns one thread must
not switch: neither the scheduler nor the safepoint may hand it another
thread, however ready that thread looks.

Where it stands: four threads start in four Workers, run their own registers,
and then one branches to address zero out of a futex. What is left is the
same kind of thing again -- the kernel keeps "which context is running" in a
field, and a field is per process.

`tests/integration/programs/thread-parallel.c` is the small version of
`threads.c`: four threads that share nothing, so that when both fail it says
whether the mechanism is broken or only the contention is.
2026-08-08 22:59:50 -04:00

53 lines
1.6 KiB
C

/* Threads that do not fight over anything.
*
* The smallest program that can tell whether a thread ran at all in a Worker
* of its own: each one writes only its own slot, and the main thread joins
* them and adds up. No mutex, no condition variable, one futex per join.
*
* `threads.c` next door is the hard version and shares a mutex twenty
* thousand times per thread. When both fail, this one says whether the
* mechanism is broken or only the contention is, and those are days apart.
*/
#include <pthread.h>
#include <stdio.h>
#define N 4
#define ITERS 100000
struct arg { int id; long sum; int tid_seen; };
static __thread int mine;
static void *worker(void *p) {
struct arg *a = p;
/* Thread-local storage, which is the one piece of per-thread state that
is not registers: each thread must see only its own. */
mine = a->id;
for (int i = 0; i < ITERS; i++) a->sum += i;
a->tid_seen = mine;
return NULL;
}
int main(void) {
pthread_t t[N];
struct arg args[N];
for (int i = 0; i < N; i++) {
args[i] = (struct arg){.id = i + 1, .sum = 0, .tid_seen = -1};
if (pthread_create(&t[i], NULL, worker, &args[i]) != 0) {
printf("pthread_create failed\n");
return 1;
}
}
long total = 0;
int tls_ok = 1;
for (int i = 0; i < N; i++) {
pthread_join(t[i], NULL);
total += args[i].sum;
if (args[i].tid_seen != i + 1) tls_ok = 0;
}
printf("sum %ld expected %ld\n", total, (long)N * ITERS * (ITERS - 1) / 2);
printf("tls %s\n", tls_ok ? "ok" : "WRONG");
printf("main tls %d\n", mine);
printf("done\n");
return 0;
}