offpath

package
v0.7.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 5, 2026 License: Apache-2.0 Imports: 2 Imported by: 0

Documentation

Overview

Package offpath holds the two shapes of work that must happen but must not happen HERE — on the path a person is waiting on.

THE PATH IS THE THING BEING PROTECTED. Between a person pressing Enter and the first word arriving, and again between a batch of tools finishing and the next request leaving, this program does a surprising amount of work that nobody asked for at that instant: it runs `git status` to find out whether the tree moved, it writes a counter file to record that something was looked up, it stamps a lookup file so a picker can sort. Each of those is milliseconds on a small machine and seconds on a big one, each was written where it was needed rather than where it was cheap, and together they are the difference between a surface that feels immediate and one that does not.

There are exactly two answers, and this package is both of them so that nobody writes a third:

  • Reading — a fact somebody will want SOON, gathered beside the work instead of in front of it, and taken when it is wanted with a bound on how long the taker will wait.
  • Write — a write a read path OWES but must not perform, coalesced and performed behind the path, with one door (Write.Settle) for whoever has to know it landed.

NEITHER IS A QUEUE AND NEITHER IS A WORKER POOL. Both are one goroutine per live thing, started when there is something to do and gone when there is not, because the work they carry is one command or one small file — and a pool would be a second scheduler for a program that already has one.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Reading

type Reading[T any] struct {
	// contains filtered or unexported fields
}

Reading is a fact being gathered somewhere else.

THE LAW IT KEEPS: the gathering starts when Take is called and the caller pays nothing for it until Reading.Settle, which waits only as long as the caller says it may. A gathering that is not finished by then is NOT cancelled and NOT thrown away — it stays on the same Reading and the next Settle takes it for free. So a reading that is fast is exact, and a reading that is slow arrives late rather than making somebody wait; what it must never do is put its own duration in front of a person.

IT HOLDS ONE VALUE AND IS TAKEN ONCE. A settled Reading is spent: the caller has the fact and the Reading has nothing left to give. Take another.

func Take

func Take[T any](gather func() T) *Reading[T]

Take starts gathering. The function runs on a goroutine of its own and must not touch anything the path it was started from is holding a lock on — it is running BESIDE that path, which is the whole point.

func (*Reading[T]) Settle

func (r *Reading[T]) Settle(within time.Duration) (T, bool)

Settle takes the fact if it is here, waiting at most `within` for it.

The second return says whether the fact arrived. A false is not a failure: it is "not yet", and the same Reading answers true on a later Settle.

func (*Reading[T]) Wait

func (r *Reading[T]) Wait() T

Wait takes the fact however long it takes. It is for a caller that genuinely cannot go on without it — and a caller on a person's path is never that caller.

type Write

type Write struct {
	// contains filtered or unexported fields
}

Write is a write that is owed and is performed behind the path that owed it.

THE LAW IT KEEPS: Write.Owe never blocks and never writes. It records that the thing behind it has changed and makes sure exactly one goroutine is performing the write; a hundred Owes while one write is running collapse into ONE further write, because what is being written is the current state and not a log of changes.

AND IT IS NOT A TIMER. The write starts immediately, so the window in which a process could die holding an owed write is the length of one write rather than the length of somebody's chosen delay. Write.Settle closes even that, for the exit doors and for every test that reads the file back.

func Deferred

func Deferred(perform func()) *Write

Deferred names a write and what performs it. The function is called with nothing held by this type, so it is free to take whatever lock the thing it writes is guarded by.

func (*Write) Owe

func (w *Write) Owe()

Owe records that the write is due. It returns at once, always.

func (*Write) Settle

func (w *Write) Settle()

Settle waits until nothing is owed and nothing is being written.

IT IS THE EXIT DOOR AND THE TEST'S DOOR, and it is the reason a deferred write is not a lost write: whoever closes a session calls it, and whoever asserts on the file calls it. Nothing on a person's path does.

IT DRAINS AND IT DOES NOT CLOSE. A Write settled is a Write ready, and an Write.Owe after a Settle starts the performer again exactly as the first one did. That is deliberate, because the exit door and the test's door are the same door and only one of them is the end of anything: a test settles in the middle of a life, reads the file back, and goes on to owe three more writes with the same object. A door that latched shut on its first use would have needed a second door for that, and the two would have drifted.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL