{"name":"core/marker.nu","source":"// stdlib/core/marker.nu — Send / Sync, the thread-safety marker traits, Frozen and Resource.\n//\n// Two questions a thread boundary asks about a type, and nothing else:\n//\n//   Send — may a value of this type MOVE to another thread?\n//   Sync — may two threads reach ONE value of this type at once?\n//\n// Both are marker traits: no methods, no runtime representation, no\n// dispatch. A `% Send T { }` block is not code — it is an assertion,\n// NURL's spelling of Rust's `unsafe impl Send for T`, and outside the\n// standard library it is written that way: `unsafe % Send T { }`\n// (docs/MEMORY.md §3; `nurlc --unsafe-report` lists it).\n//\n// You rarely write either. The compiler DERIVES both structurally over\n// a type's whole graph — struct fields, enum payloads, generic\n// arguments, aggregate members, closure captures — and checks them\n// where a value actually crosses a thread boundary:\n//\n//   ( thread_spawn f )   every value `f` captures must be Send\n//   ( spawn f )          same (fibers migrate across M:N workers)\n//   ( chan_send ch v )   `v` must be Send\n//   ( Arc T )            T must be Send AND Sync — an Arc exists to be\n//                        shared, so its payload faces the harder question\n//   ( RwLocked T )       T must be Send AND Sync — several readers reach it\n//                        at once; a Locked's, a Channel's or a Pool's T\n//                        must be Send, even where a `% Sync` marker on the\n//                        container stops the walk (it vouches for the\n//                        container, never for what it holds)\n//\n// A handle whose copies are one state (a library handle over a counted\n// block — a PNCounter, a TcpConn, a Cell — not asserted Sync) is Send, but\n// only with every owner of that state: a copy left on this side would be\n// changed by two threads with no lock. No type says how many copies a\n// value has, so the crossing asks at run time: `( mem_send_check [T] x\n// what )`, in thread_spawn, spawn, chan_send and a Locked's operations,\n// panics unless x carries every owner of each such handle it reaches\n// (docs/MEMORY.md §6.5).\n//\n// The derivation has three levels, worst-wins across the graph:\n//\n//   Send + Sync    the default — scalars, strings, handles, structs of them\n//   Send, !Sync    `Cell` — a raw byte buffer with unsynchronised writes\n//   !Send, !Sync   `Rc`   — a non-atomic refcount; two threads touching\n//                           the count is a data race on the count itself\n//\n// Those two leaves are language-level facts and live in the compiler.\n// Everything else follows from them by construction: `( Vec ( Rc i ) )`,\n// `( Arc ( Rc i ) )`, `( Box ( Rc i ) )`, a struct with an `( Rc i )`\n// field, an enum variant carrying one, an option holding one, and a\n// closure capturing any of those are all !Send for the same one reason,\n// and the diagnostic names that reason rather than the spelling.\n//\n// A trait object is the one type whose graph cannot be walked: a\n// `%Job` boxes whichever type implements `Job`, and its type does not\n// say which. Its trait answers for it instead. `% Job [T] : Send { … }`\n// holds every type implementing `Job` to Send where its impl is, and\n// then a `%Job` may move; `: Send Sync` lets one be shared as well.\n// Without the promise a trait object stays on the thread that made it.\n//\n// ── When you DO write one ──────────────────────────────────────────\n//\n// The derivation is structural, so it is wrong in exactly two ways,\n// and there is one marker for each.\n//\n// **The compiler is too pessimistic.** A type whose innards look unsafe\n// but which is safe by construction — a lock, a channel, an atomic\n// handle. `Mutex` is literally `{ Cell c }`, and a bare `Cell` is !Sync,\n// yet a Mutex is the thing that MAKES its contents shareable. Assert it:\n//\n//     % Sync Mutex { }        // and `% Send Mutex { }`\n//\n// An explicit impl STOPS the walk at that type: nothing inside is\n// examined, because you have taken responsibility for it. That is why a\n// program's own positive marker is `unsafe % Send T { }` /\n// `unsafe % Sync T { }`: without the prefix it is an error.\n//\n// **The compiler is too optimistic.** A type that derives clean but\n// wraps thread-hostile state the compiler cannot see — an FFI handle.\n// A `sqlite3*` is an `s` and a `FILE*` is an `s`, and `s` is Send;\n// neither connection is. Say so:\n//\n//     : Db { s handle }\n//     % NotSend Db { }        // never crosses a thread boundary\n//\n// `NotSend` / `NotSync` propagate exactly like the built-in leaves: a\n// struct holding a `Db`, a `( Vec Db )`, a closure capturing one are\n// all rejected at the boundary, naming `Db`.\n//\n// A negative marker always wins over a positive one on the same type —\n// if both are written, the type is rejected. That is the safe direction\n// for a contradiction to resolve in. A negative marker only adds checks,\n// so it is ordinary safe code: no `unsafe`.\n//\n// ── As a bound ─────────────────────────────────────────────────────\n//\n// Because they are ordinary traits, they work as ordinary bounds, and\n// the bound is satisfied by the DERIVATION — not by hunting for an\n// impl. `[T: Send]` accepts `i`, `s`, `( Vec i )` and rejects\n// `( Rc i )`:\n//\n//     @ run_on_worker [T: Send] ( Channel T ) ch T v → b {\n//         ^ ( chan_send [T] ch v )\n//     }\n//\n// ── What this does NOT prove ───────────────────────────────────────\n//\n// Send/Sync answer \"may this value cross?\", never \"is this program\n// race-free\". Two threads mutating one `( Vec i )` they both hold a\n// handle to is a race that no marker forbids — `Vec` is Send and Sync,\n// and correctly so, because sharing it read-only is fine. That race is\n// caught separately, at the mutation, by the shared-mutation check\n// (`docs/MEMORY.md` §6.5). The two checks are complementary and\n// neither subsumes the other.\n//\n// Like every other check in this compiler the derivation can only MISS\n// (an unmarked FFI handle, a type graph deeper than the walk's cap),\n// never invent: it is a sound lint, not a proof (`docs/MEMORY.md` §6.3).\n\n// A value of `T` may be moved to another thread.\npub % Send [T] {}\n\n// Two threads may reach one value of `T` at the same time.\npub % Sync [T] {}\n\n// `T` must never cross a thread boundary, whatever its fields suggest.\npub % NotSend [T] {}\n\n// Two threads must never reach one `T` at the same time.\npub % NotSync [T] {}\n\n// Frozen — what a value of `T` lends is read-only. A function that takes\n// a `T` and hands back something it holds (an accessor) lends a view of\n// it, and no call may change that view or anything reached through it:\n// not a container mutator, not `slice_put`, not a helper of the program's\n// that does, at any depth (docs/MEMORY.md §6.5). An `Arc` is frozen by\n// construction — its payload is read by every thread holding a clone\n// without a lock. Mark a handle of your own the same way when what it\n// lends is shared with other threads reading at the same time: a read\n// guard of a reader-writer lock (stdlib/std/rwlock.nu) is the shape —\n//\n//     % Frozen RGuard { }\n//\n// Unlike Send and Sync it only adds checks: the compiler follows every\n// view the handle lends and rejects a change to one where it is made, so\n// a safe module may mark a type of its own (no `unsafe`). What is trusted\n// is the other direction — a handle of the standard library's that lends\n// shared data and is NOT marked would lend it mutable.\npub % Frozen [T] {}\n\n// Resource — a type whose drop is observable outside the program: it\n// closes a file descriptor, a socket, a database connection, reaps a child\n// process. Not a thread-safety question: the Rc cycle collector asks it. A\n// cycle of Rc handles that can hold a Resource is released the moment it\n// becomes unreachable (docs/MEMORY.md §7.7), so a file in a graph closes\n// where the last handle into the graph went — not whenever the collector\n// next runs, which a program opening files in a loop would notice as\n// running out of descriptors. Everything that owns a Resource (a struct\n// field, an element, a payload, an Rc it holds) is one by structure; only\n// the OS handles themselves carry the mark:\n//\n//     % Resource File { }\n//\n// Mark a type of your own the same way when its drop does something the\n// outside world sees (an FFI close, a flush to a socket). Freeing memory,\n// releasing a lock or a count is not that: a Mutex is not a Resource.\npub % Resource [T] {}\n","bytes":8770}