Waiting for a key is not the same as time stopping
A console read that had nothing to hand over blocked in read(), and while it was blocked nothing told the devices any time had gone by. The whole machine stood still: the clock, the cursor, a disk settling, a note decaying. Measured, four real seconds of sitting at the prompt bought zero cycles. A window has never done this. consoleGatherLine and the key mode loop both spend a frame and then say so, and the comment on consoleWaited gives the reason - a display controller blinking a cursor does not stop because the processor is waiting on a key, and neither does a disk finishing a read. The terminal path simply never got it. How it showed was a note left in its release after a program exited. Frozen mid-decay, it came out a snippet at a time, one per keystroke, because a keystroke was the only thing that let the machine run at all. So a terminal read now polls a frame at a time and tells the devices about each one. Four seconds now buys 3.85 million cycles, which is the wall clock, which is what the window has always done. ONLY WHEN THERE IS REALLY A TERMINAL, the same rule the escape sequences and the erase character follow. A file or a pipe keeps blocking: every recorded test on this machine is reproducible because emulated time does not depend on how fast the other end of a pipe is writing, and that must stay true. The full suite is unchanged. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
41d65d6346
commit
7f96499438
@@ -386,6 +386,12 @@ static int (*inputHook)(int mayWait) = NULL;
|
||||
// cursor blinking at somebody who is thinking about what to type.
|
||||
#define CONSOLE_WAIT_CYCLES 16667
|
||||
|
||||
// The same span in milliseconds, for waiting on a terminal rather than on a window. The two
|
||||
// have to mean the same length of time: one is what the devices are told went by, the other
|
||||
// is how long was really spent, and a machine whose clock ran at a different rate from the
|
||||
// wall while it was waiting would be a machine whose music slowed down when nobody typed.
|
||||
#define CONSOLE_WAIT_MS 17
|
||||
|
||||
static unsigned long idleCycles = 0;
|
||||
|
||||
// The machine's clock as the devices last heard it. Kept here rather than passed about,
|
||||
@@ -487,6 +493,9 @@ void consoleSetInputHook(int (*hook)(int mayWait)) {
|
||||
// Answers a byte, CONSOLE_NOTHING_YET when nothing is waiting and it was told not to wait,
|
||||
// or CONSOLE_GONE at the end of input. Deliberately the same three answers a front end's
|
||||
// hook gives, so that everything above this treats a terminal and a window alike.
|
||||
// Whether standard input is really a terminal, defined below and wanted here.
|
||||
static int consoleTranslatingKeys(void);
|
||||
|
||||
static int consoleFromInput(int mayWait) {
|
||||
if (!mayWait) {
|
||||
struct pollfd waiting = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 };
|
||||
@@ -496,6 +505,37 @@ static int consoleFromInput(int mayWait) {
|
||||
}
|
||||
unsigned char byte;
|
||||
for (;;) {
|
||||
// ---- WAITING FOR A KEY IS NOT THE SAME AS TIME STOPPING ----
|
||||
//
|
||||
// This used to block in read, and while it was blocked nothing told the devices that
|
||||
// any time had gone by - so the whole machine stood still. A window has never done
|
||||
// that: consoleGatherLine and the key mode loop both spend a frame and then say so,
|
||||
// and the comment on consoleWaited gives the reason. A display controller blinking a
|
||||
// cursor does not stop because the processor is waiting on a key, and neither does a
|
||||
// disk finishing a read, or a synthesiser finishing a note.
|
||||
//
|
||||
// A note left in its release was the way this showed. Frozen mid-decay, it came out
|
||||
// a snippet at a time, one per keystroke, because a keystroke was the only thing that
|
||||
// let the machine run. Four seconds of sitting at the prompt bought no cycles at all.
|
||||
//
|
||||
// ONLY WHEN THERE IS REALLY A TERMINAL, which is the same rule the escape sequences
|
||||
// and the erase character follow. A file or a pipe must keep blocking: emulated time
|
||||
// would otherwise depend on how fast the other end of the pipe was writing, and every
|
||||
// recorded test on this machine is reproducible because it does not.
|
||||
if (consoleTranslatingKeys()) {
|
||||
struct pollfd waiting = { .fd = STDIN_FILENO, .events = POLLIN, .revents = 0 };
|
||||
const int ready = poll(&waiting, 1, CONSOLE_WAIT_MS);
|
||||
if (ready == 0) {
|
||||
consoleWaited();
|
||||
continue;
|
||||
}
|
||||
if (ready < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue;
|
||||
}
|
||||
return CONSOLE_GONE;
|
||||
}
|
||||
}
|
||||
const ssize_t got = read(STDIN_FILENO, &byte, 1);
|
||||
if (got == 1) {
|
||||
return byte;
|
||||
|
||||
Reference in New Issue
Block a user