aboutsummaryrefslogtreecommitdiffstats
Commit message (Collapse)AuthorAgeFilesLines
* Measure computing faster: a search 2.1 times as fast, TPC-C 4.1%, four ↵eunhhu2026-10-0467-0/+559
| | | | fingers found and undone (121)
* Say why the dashboard's push-to-poll CPU sits at a tenth: an event costs ↵eunhhu2026-10-041-0/+4
| | | | what a request does (120)
* The runs of research 122: writes beside views that compute, one asker and ↵eunhhu2026-10-043-0/+283
| | | | two, and a plain view's reads
* Compute views with a body on view workers, off the connection threads, asked ↵eunhhu2026-10-043-81/+393
| | | | | | | | | | | | | | | | | | | | and pushed (020) - pure-serve --view-workers N (a CPU each; 0: on the connection threads): a GET of a view with a body (or a table with such a column) goes to the workers, which show it from the app's newest snapshot on readers of their own and give the answer back as an owner does (eventfd). The connection thread reads and writes its other connections meanwhile. - A snapshot of another module than the request was resolved with, or none (the app stopped), answers 503 stopped. Past an app's queue=N of them, 503 busy. - A push group of such a view hands one evaluation at a time; a newer snapshot meanwhile is evaluated after it; a group whose subscribers all left stays until its evaluation is back. - check.py server_view_workers: 8 askers of a long view while its app changes module (another view in its place) and then goes: every answer the old module's, the new one's, 503 or 404; streams closed at once leave nothing; --view-workers 0 as before.
* End a loop's iteration at the block's end check, not a second one per ↵eunhhu2026-10-041-12/+12
| | | | instruction (020)
* Keep one finger a thread, as before: four cost the bank's queries ↵eunhhu2026-10-043-37/+28
| | | | instructions and a search no time (020)
* Wait for a counter before searching in bfs-run, and the runs of research 121eunhhu2026-10-042-0/+67
|
* Search an event's keyed lists by a key set and lists by fingers, add by key ↵eunhhu2026-10-047-97/+762
| | | | | | | | | | | | | | | | | | | | in one descent, and loop without recursion (020) - An event's list keyed by a number keeps its keys beside its tree: bits while they are small whole numbers, else an open-addressing table; has() reads only that, and inserts, removes, key changes, their undoing and clears keep it. - Each thread keeps fingers on the last four leaves it read (list id and version must match): reads by position and searches by key that land in them do not descend. - pure_list_add finds a key's place and inserts there in one descent; a stored keyed list's appends take it too, journaled as inserts. - Child searches count over the whole guide; item fields know at load whether they are text; loops run on a stack of loop records, not by recursion; module-list.c builds at -O3. - test-module: the key set and fingers answer as a model at every step (bits to table, removes inside runs, undone changes, clears, shares), and a stored keyed list's adds replay from its journal.
* Say where a search's time goes and how to make it and long views fast, with ↵eunhhu2026-10-042-0/+167
| | | | criteria (020)
* Wake each thread at most once a push, end streams only of stopped apps, and ↵eunhhu2026-10-046-4/+130
| | | | show a subscriber costs writes nothing (019)
* Say what pushing views costs and how fresh it keeps them (019, research 120)eunhhu2026-10-0454-1/+458
|
* Watch a subscriber for writing only while it keeps output, and measure ↵eunhhu2026-10-043-2/+247
| | | | freshness by push and polling (019)
* Push a view's value to its subscribers over SSE, once per group and interval ↵eunhhu2026-10-044-6/+613
| | | | (019)
* Fix 019 before building: compare equal freshness, bound pushes per interval, ↵eunhhu2026-10-041-9/+24
| | | | carry streams across module changes
* Say how pure-serve pushes a view's value to its subscribers, and what it ↵eunhhu2026-10-041-0/+91
| | | | must show (019)
* Put lifetimes and computing in the checkpoint (018)eunhhu2026-10-041-1/+2
|
* Say what views that compute cost the writes beside them (018 stage 3, ↵eunhhu2026-10-048-1/+383
| | | | research 119)
* Say why stage 3's first isolation criterion could not hold, and what to ↵eunhhu2026-10-041-0/+21
| | | | measure instead (018)
* Let a view compute in its body, on a reader's own lists (018 stage 3)eunhhu2026-10-047-35/+292
|
* Say what computing in one event costs and finds (018 stage 2, research 118)eunhhu2026-10-045-4/+187
|
* Measure 018 stage 2: a graph search in one event, the pairs, and runs to ↵eunhhu2026-10-0434-0/+511
| | | | compare across machines (033)
* Count in loops that nest, break, and keep an event's own lists, every event ↵eunhhu2026-10-049-54/+422
| | | | bounded (018 stage 2)
* Say what a transient state saves a write (018 stage 1, research 117)eunhhu2026-10-045-1/+142
|
* Keep transient states the app's while it runs, never stored (018 stage 1)eunhhu2026-10-047-25/+266
|
* Say how aup chooses what a state outlives, and computes in memory (018)eunhhu2026-10-041-0/+199
|
* Say what an app's tokens cost (017 3b, research 116)eunhhu2026-10-045-3/+178
| | | | | | | | Requests to an app that checks tokens go 2.92% slower than to an open one on 64 keep-alive connections, 2.2% of it the head the token makes longer (an open app sent the same head is as slow); hashing every request on a new connection costs 0.91%. 031 measures the three in turns.
* Take an app's requests only with a token its tokens file grants (017, 3b)eunhhu2026-10-046-21/+495
| | | | | | | | | | | | | | | An app given a tokens file (tokens=FILE in --apps, --tokens FILE for the one app of a host) answers a request only when it bears a token (Authorization: Bearer) whose SHA-256 the file lists with a grant: read for its views, queries and documents, write for all, input=NAME for those inputs alone; 401 unauthorized (WWW-Authenticate: Bearer) without one, 403 forbidden past its grant. A delivery sealed by a key --sender names needs none. The digests are compared in constant time; a connection keeps its last token's digest, so the same token is hashed once. SHA-256 is the host's own (serve/sha256.c), no OpenSSL loaded. A reading puts a changed tokens file in place with the app running on; a file that is not one stops the app. pure-serve --make-token GRANT makes a token and its line. load takes PURE_LOAD_TOKEN.
* Say how an app's routes take tokens, and what it must show (017, 3b)eunhhu2026-10-041-1/+30
| | | | | | | | | An app given a tokens file (tokens=FILE, --tokens FILE) takes requests bearing a token whose SHA-256 the file lists: read for its views, queries and documents, write for all, input=NAME for one input alone; 401 without one, 403 past its grant. A delivery another node sealed needs none: its key told who sent it. A connection keeps its last token's grant; a reading swaps the tokens without stopping the app.
* Say that reading the code found stage 3's two races, and how a host changes appseunhhu2026-10-043-5/+9
| | | | | | | | | The use after free in local_done and the owner's lock made after another app's outlet used it were found reading the code, not by the check, which passed with both before it was made to change senders (115, 017). The usage now says changed lines change in place unless their store moved; and 115 names the path no check takes: a module refused while changing in place.
* Say what apps that change on a running host showed (017 stage 3, research 115)eunhhu2026-10-0446-23/+747
| | | | | | | | | | Changing an app in place keeps a million items' app answering but for 1.12 ms (a host restart: 150 ms; stopping and starting it again from its store, as first built, 150.4 ms), 171.5 ms when its records change shape (332 ms). Another app's p99 stays 0.97 times as it was while one changes every 0.2 s; a host of one app is as it was (TPC-C +0.7%, instructions at most +0.02%); each module change leaves 19 KB. 030 holds the windows of both builds, the isolation, the pairs, the counts and the memory.
* Change a running app in place, its states kept in memory (017, stage 3)eunhhu2026-10-042-46/+126
| | | | | | | | | | | | | | | | | | Stopping an app and starting it again reads its whole store again: a million items took 142 ms, as long as restarting the host (030). A running app whose line changed but not its store now goes quiet (what it took applied, saved and answered, its batches back, its outlets closed, its checkpoint finished), has its module replaced in memory (pure_app_replace carries its states, and the sends in its outbox, by name), its budgets set and its outlets made anew, and resumes; a module its states do not go into stops it. An app whose store changed, one a save of which is uncertain, and one that does not run still start from their stores. A reading says changed for the first. A snapshot is published again when the module changed though the states did not. server_apps_lifecycle reads the changing apps' views (lists among them) as they change, and moves an app's store; it passed 10 times alone, 5 under load and 6 with an ASan pure-serve.
* Measure how long an app changing module cannot answer, against a host ↵eunhhu2026-10-046-0/+293
| | | | | | | | | | | | restart (030) An app of a million items (a list grown by doubling) or of a thousand, in a host of apps, its store in RAM: the longest gap between two answered writes while it changes module on SIGHUP (stopped and started again from its store, 71e319b), and while the host is restarted. A thousand items: 1.59 ms against 3.05 (median of 20 and 10). A million: 150.64 ms against 149.73 (10 and 5): both are the store read again (pure_app_start, 142 ms), so changing does not answer sooner than restarting.
* Start, stop and change apps on a running host on SIGHUP (017, stage 3)eunhhu2026-10-042-142/+762
| | | | | | | | | | | | | | | | | | | | | | | | A host of apps reads its --apps file again on SIGHUP, on a thread of its own (pure-apps, the one that takes the signal): a file with a wrong line changes nothing; the apps of lines gone stop and their routes go (404 no_app); those of changed lines, and those not running (they could not start, or a save of theirs is uncertain), start again from their stores, states carried by name; new ones take places of their own; a line out says what was done. An app stops by taking nothing more (503 stopped), applying, saving and answering what it took, waiting for its batches at other apps, then closing its outlets (what they did not deliver stays in the outbox) and its store. Its link streams find their inputs again as it starts. Owners' locks are made with their places; counts are kept over an app's runs; workers grow with the apps that run, a CPU each at most. server_apps_lifecycle adds, removes and changes apps, and changes the receivers (a local outlet's and a link's, their inputs at other numbers, taken by name) and the senders while 600 sends each and writes go: every send arrives once and in order, a write answered 200 is applied once and one answered 503 stopped not at all, the app left alone fails no request; an app whose store cannot be made starts at the next reading once it can; kill -9 and the host starts as the file says. It passed 10 times alone, 5 under load, 6 with an ASan pure-serve.
* Let a link's receiver find its streams' inputs again (017, stage 3)eunhhu2026-10-045-5/+173
| | | | | | | | | | | | | A stream finds its input once, when it is made; an app that starts again with another module may have the input at another number, of another shape, or none. pure_link_receiver_rebind marks the streams to inputs of a prefix stale: each finds its input again by name, with the fields it was laid out with, before its next delivery and at its sender's next packet, keeping what it holds; one none takes is refused as a new one would be. pure_link_listener_rebind asks it of the listener's thread. test-link's rebound case stops its app, starts it with the input at another number, then of another shape taken by name: all 3,000 records come once, in order (without the rebind, none after the stop).
* Carry the module a request was resolved with, and give apps places (017, ↵eunhhu2026-10-041-121/+269
| | | | | | | | | | | | | | | | | stage 3) Groundwork for apps that start, stop and change on a running host, with no change in what a host does: - an app's module is on the heap, and the module its requests are resolved with is published (live, NULL while it does not run); a module it ran is kept while the host runs; - a request, a view, a link's binding and a local outlet carry the module they were resolved with, and an owner takes none resolved with another (503 stopped); a local outlet finds its input again when its receiver runs another module, and holds its sends while it runs none; - the published snapshot is the owner's, outliving an executor; - apps take places, made when the host starts (--app-slots, 256), never given to another name, so threads find apps by name as others start.
* Say how apps start, stop and change on a running host, and what it must show ↵eunhhu2026-10-041-1/+76
| | | | | | | | | | | | (017, stage 3) The --apps file is the host's shape: on SIGHUP the host reads it again and starts the apps of new lines, stops those of lines gone, starts again those whose line changed (states carried by name from the store) and those stopped or uncertain. A request carries the module it was resolved with, and is not applied under another (503 stopped); link streams and local outlets look their input up again. Route authentication (3b) is designed apart.
* Trade stores with the WSL2 too: its bytes are the Pi's (029, research 114)eunhhu2026-10-046-8/+34
| | | | | | The same events make byte-identical stores on the x86_64 WSL2 (4 KiB pages) as on the Pi and the Mac, and a store made on either of the Pi and the WSL2 opens on the other and goes on the same.
* Say what running on an x86_64 WSL2 and an M3 Mac showed (research 114)eunhhu2026-10-022-1/+126
| | | | | | | | | The same modules and inputs give the same outputs, failures and store bytes on the Pi, an x86_64 WSL2 and an M3 Mac; other compilers found an argument read before it was set, and the core and store build on macOS again. WSL2's wall clock steps back about 2 s every 32 s. The Pi froze twice under a measuring tool that held its output: it now streams it, and experiments run under research/guard.py.
* Run Pure and aup on an x86_64 WSL2 and an M3 Mac against the Pi (029)eunhhu2026-10-0226-0/+3806
| | | | | | | | | | Every pure-run of aup's check (1,053 runs of 667 modules) gives the same output, errors and exit on the Pi, the WSL2 (x86_64, 4 KiB pages, gcc 13) and the Mac (Apple clang 21); the same events make byte-identical stores on the Pi and the Mac, and either opens the other's and goes on the same. Pure's checks pass one at a time in WSL (each_check.py); its wall clock is set back about 2 s every 32 s (clock.py). stores.py streams pure-run's output, whose size grows with the square of the events.
* Run experiments under a memory guard, so they cannot freeze the Pieunhhu2026-10-021-0/+110
| | | | | | | | | | | The Pi has 8 GiB, zram swap and no OOM daemon, and the user's systemd holds no memory controller: a run that outgrows its memory has every program's code evicted before the kernel kills anything, and the machine stops answering. It was reset twice so (a run held 13 GiB of output it should have streamed). guard.py runs a command in a session of its own and kills the session when it holds more than --most MiB (3072) or the machine has less than --floor MiB available (1024, a tmpfs filled counts), and stops what it leaves running.
* Build with clang, and the core and store on macOS, againeunhhu2026-10-017-8/+22
| | | | | | | | | | | | | | Apple clang 21 (an M3) and clang 19 refused what gcc let pass (research 029): - PURE_FIELD_REST was an enum of UINT32_MAX, past an int (C17): a macro. - a store's publish copied an entry rebuild left unset when the record was not found (the copy was never used): copied when it was made. - the log's image writer kept a count only Linux's sync_file_range uses. - ",{" + (row == from) read as adding an int to a string: said plainly. - the tests: mkdtemp, hidden by _POSIX_C_SOURCE on macOS, needs _DARWIN_C_SOURCE; a string split so that \x80 and a stay apart is bracketed; a count left over and a malloc measure only glibc has. The core's and the store's tests pass on the Mac.
* Take a timer window again when the wall clock jumped in iteunhhu2026-10-011-9/+17
| | | | | | | | | server_timers compares the times an app saw with the wall clock read around them. WSL2's clock is set back about 2 s every 32 s (research 029): a window holding such a step says nothing of the times seen, and failed the check there 4 times in 20. It is taken again (up to three times) when the wall clock and the monotonic one moved more than 50 ms apart over it; the report counts the windows taken again.
* Look at a wake write's result in the prototype, as fortified builds askeunhhu2026-10-013-4/+4
| | | | | | | Ubuntu's gcc defines _FORTIFY_SOURCE, which marks write's result as one to use, and a (void) cast does not quiet it: the prototype did not build there (research 029). Debian's does not define it; the Pi gives the same error with -D_FORTIFY_SOURCE=3.
* Read a list cell's place after finding its leaf, not beside iteunhhu2026-10-011-1/+2
| | | | | | | | pure_list_get passed leaf_of(list, at, &within) and within to cell_at in one call: C leaves the order of a call's arguments open, so within could be read before leaf_of set it. gcc 14 on AArch64 called leaf_of first; gcc 13 on x86_64 says within is used uninitialized (found building Pure there, research 029).
* Say what one sending loop showed (017 2b, research 113)eunhhu2026-10-013-8/+131
| | | | | | | The loop alone slowed four links to half: woken by every record queued, it chased the owner a record at a time. Pushing once a turn made links 1.6 times as fast, with threads or the loop alike; the loop's own cost is within the noise. Twenty outputs now add two threads, not 21.
* Measure wires on the sending loop, and a host's threads (028)eunhhu2026-10-016-7/+211
| | | | | | | | | Five rounds of 1,000 bursts by one wire, four wires, one link and four links: a thread each (966b82f), links on the loop (94e7bb8), wires on it too (5f5603c). Medians: one wire 774k, 782k, 779k sends/s; four wires 629k, 625k, 615k (the same CPU a send); one link 581k to 948k; four links 485k to 811k. A host whose app sends to ten links and ten wires has 24 threads before and 5 after (3 with no output).
* Deliver wires on the sending loop, a step at a time (017, 2b-2)eunhhu2026-10-017-291/+512
| | | | | | | | | | | | | A wire is now a sender of link/sending.h: its request goes on a non-blocking socket as far as the socket takes it (connect, TLS handshake, write, answer read), and its round waits for the socket or its deadline (10 s with nothing moved). The answer is read as it comes: the head whole, then a sized body counted down, a chunked one (few and small) parsed again as it grows, or a body to the close. The crypto table (version 6) takes the TLS handshake a step at a time, and reads and writes say what they wait for. The owner pushes a wire once a turn, as it does a link. A host whose app sends to ten links and ten wires has 5 threads where it had 24 (none: 3).
* Measure one sending loop against a thread a link, with and without push (028)eunhhu2026-10-017-0/+338
| | | | | | | | Five rounds of 1,000 bursts by one link, four links and one wire, four builds in turns: a thread a link (966b82f), those threads with push (threads-push.patch), the loop (99b2219), the loop with push (94e7bb8). Medians: one link 597k, 923k, 525k, 897k sends/s; four links 506k, 799k, 268k, 800k; the wire as it was, 789k to 794k.
* Take the sending loop out of the links, for the wires to join it (017, 2b)eunhhu2026-10-014-121/+227
| | | | | | | | | link/sending.c is the process's sending loop: a sender joins it with its round and the socket it waits on, and is rounded when its socket is ready, when pushed, and when its time comes. Links are its first senders; nothing they do changed. Events of a sender that left while the loop waited are let go (the epoll is level-triggered: the others' come again).
* Push a turn's sends to a link at once, and round only the links that want ↵eunhhu2026-10-015-23/+37
| | | | | | | | | | | | one (017, 2b-1) The sending loop was woken by every record queued while it slept: the owner queues one by one, the loop caught up and slept between them, and every link took a round on every wake. Four links sent 64k packets of 12 records where threads sent 40k of 20, and ran at 257k sends/s against 581k. The owner now queues a turn's sends and pushes each link once; the loop rounds a link only when its socket has answers, it was pushed, or its time came.