sysl

Module

sysl.signal

Signals as flags: a program asks to be told that one arrived, and asks again later whether it has.

Import import sysl.signal
Requires
requires { os }

No sysl code ever runs inside a signal handler, and that is the whole design. A handler runs between two instructions of whatever the program was doing, so a body that allocates, releases a counted reference or takes a lock is one that can deadlock or corrupt the very thing it interrupted – and that is nearly every sysl body. So watch installs the target’s handler, whose one act is to count the arrival with a lock-free atomic, and the program reads the count where it chooses: at the top of a loop, between two jobs, before a prompt. It is the shape of Rust’s signal-hook flag and Go’s os/signal without the channel, and it is the one shape that is safe everywhere.

The numbers are the target’s own. TSTP, CONT, CHLD, USR1 and USR2 are different numbers on Linux and on a BSD, so each constant here is written per platform rather than once; a program that writes signal.TSTP means what the machine it was compiled for means. A target with no C library takes Linux’s numbering, which is the one a kernel written for a POSIX-shaped user space usually copies; one that numbers differently passes its own integers – every function here takes a plain int, the type sysl.process‘s Child.kill and Status.Signalled speak too.

Everything it asks of the machine goes through the hooks of sysl.signal.sys, which the library answers over sigaction on a POSIX host and a program answers itself, with an @export per hook, on a target with no C library.

Index

ALRM CHLD CONT HUP INT KILL PIPE QUIT STOP TERM TSTP TTIN TTOU USR1 USR2 WINCH ignore raise restore_default send watch Watch Drop for Watch

Constants

ALRM

const ALRM: int = 14

Alarm: a timer the program set has run out.

CHLD

const CHLD: int = 20

A child of the program stopped or ended.

CONT

const CONT: int = 19

Continue: resumes a stopped program.

HUP

const HUP: int = 1

Hangup: the terminal the program was started from has gone.

INT

const INT: int = 2

Interrupt: Ctrl-C at the terminal.

KILL

const KILL: int = 9

Kill: ends the program and cannot be watched, ignored or caught. send is the only thing here that takes it.

PIPE

const PIPE: int = 13

A write into a pipe or socket nobody is reading any more.

QUIT

const QUIT: int = 3

Quit: Ctrl-\ at the terminal, which by default also leaves a core file.

STOP

const STOP: int = 17

Stop: stops the program and, like KILL, cannot be watched, ignored or caught.

TERM

const TERM: int = 15

Terminate: asks the program to stop, which Child.kill sends by default.

TSTP

const TSTP: int = 18

Terminal stop: Ctrl-Z at the terminal.

TTIN

const TTIN: int = 21

Ttin: a background job tried to read from its terminal.

TTOU

const TTOU: int = 22

Ttou: a background job tried to write to its terminal, or change it.

USR1

const USR1: int = 30

User-defined signal 1: no meaning but the one a program gives it.

USR2

const USR2: int = 31

User-defined signal 2.

WINCH

const WINCH: int = 28

Window change: the terminal was resized.

Functions

ignore

ignore(sig: int) -> Result[unit, IoError]

Makes sig do nothing when it arrives – what a shell does with TTOU, TTIN, TSTP and INT for itself, so that only the job in the foreground hears them.

Refused as EINVAL where watch would be, and as IoError.Other(16) (EBUSY) for a signal a Watch is alive for: the watch owns the disposition until it is dropped.

raise

raise(sig: int) -> Result[unit, IoError]

Sends sig to this program itself – to the thread that calls it – and, unless the signal is blocked, has it handled before returning: a watched one is counted, an ignored one discarded, and one left at its default does what the default does. Refused as send is.

restore_default

restore_default(sig: int) -> Result[unit, IoError]

Gives sig back the action the system takes when nobody has said otherwise – what a shell does for a child it is about to start, which inherits the parent’s ignores. Refused as ignore is.

send

send(pid: i64, sig: int) -> Result[unit, IoError]

Sends sig to the process pid, read as POSIX’s kill(2) reads it: zero is this program’s process group, and a negative number the group -pid. A sig of zero sends nothing and answers whether one could have been sent.

A pid that names no process is IoError.Other(3) (ESRCH); one this program may not signal is NotPermitted; a signal number this platform does not have is EINVAL, refused before anything is sent. Child.kill is the same call aimed at a child sysl.process started.

watch

watch(sig: int) -> Result[&Watch, IoError]

Starts counting sig for this program, answering the Watch to ask about it.

Refused as IoError.Other(22) (EINVAL) for a number this platform does not have, and for KILL and STOP, which nothing may catch.

Types

Watch

struct Watch
    private sig: int
    private seen: i64

A signal being counted for this program, from watch.

take is the whole interface: it answers whether the signal has arrived since the last take – or since the watch began – and marks every arrival so far as seen. Arrivals that come between two takes are one true, not several, which is what a flag is; an arrival during a take is never lost, only left for the next one, since what is compared is a count that only goes up.

Two watches of one signal each see it. Each keeps the count it last saw, so a library watching WINCH for its own reasons takes nothing from the program that watches it too.

Dropping the last watch of a signal puts back what was there before the first – the default, an ignore a parent left, or a handler C code installed. It is a counted reference so that the drop happens once, where the last holder lets go of it.

MemberSignatureDescription
signalsignal(self) -> intThe signal this watch counts.
taketake(*self) -> boolWhether the signal has arrived since the last take, marking it seen.

Implementations

Drop for Watch

impl Drop for Watch

Search

Esc
↑↓ to navigate ↵ to open Esc to close