--- name: semop signature: 'semop KEY, OPSTRING' since: 5.0 status: documented categories: ["SysV IPC"] --- ```{index} single: semop; Perl built-in ``` *[SysV IPC](../perlfunc-by-category)* # semop Perform one or more System V semaphore operations atomically. `semop` is the workhorse of SysV semaphore use: it takes a semaphore set identifier (from [`semget`](semget)) and a packed list of operations, then asks the kernel to apply them all or none. Each operation names a single semaphore within the set, an amount to add to it (typically `-1` to wait, `+1` to signal), and a flag word. The kernel blocks the caller until every requested operation can proceed, then commits them as one unit. This is what makes `semop` usable as a mutual-exclusion or counting-resource primitive between unrelated processes. ## Synopsis ```perl semop $semid, $opstring ``` ## What you get back True on success, false on error with [`$!`](../perlvar) set. The truth value is the only result — `semop` communicates outcome by the side effect on the semaphore set, not through its return. Check it on every call: ```perl semop($semid, $ops) or die "semop failed: $!"; ``` A false return means no operation in `OPSTRING` was applied; SysV semantics guarantee all-or-nothing. ## Global state it touches Sets [`$!`](../perlvar) on failure, with the usual errno values from `semop(2)`: `EAGAIN` when `IPC_NOWAIT` was set and the operation would block, `EIDRM` when the semaphore set was removed while waiting, `EINTR` when a signal interrupted the wait, `EINVAL` for a bad semaphore number or identifier, `EACCES` for a permission mismatch against the set's mode. ## The operation structure `OPSTRING` is a concatenation of fixed-width records. Each record is three native-width `short` integers and is produced with [`pack`](pack): ```perl pack("s!3", $semnum, $semop, $semflag) ``` - `$semnum` — the index of the target semaphore within the set (`0` through `nsems-1`, where `nsems` is the size passed to [`semget`](semget)). - `$semop` — the amount to add to the semaphore's value. Negative values wait until the semaphore is at least `|$semop|` before subtracting; positive values add unconditionally and wake waiters; zero waits until the semaphore reaches zero. - `$semflag` — bitwise OR of `IPC_NOWAIT` (fail with `EAGAIN` instead of blocking) and `SEM_UNDO` (have the kernel reverse the operation if the process exits without undoing it — essential for lock-like uses where a crash must not leave the semaphore held). The length of `OPSTRING` implies the number of operations: the kernel processes `length($opstring) / sizeof(struct sembuf)` records. Passing two records packed end-to-end asks for two operations applied atomically. The `s!` in the template is the **native** `short`, which matches the `sembuf` layout on Linux. Plain `s` is network-order signed 16-bit and will silently mis-pack on any platform where `sizeof(short) != 2` or where endianness differs from network order. ## Examples Wait on (decrement by 1) semaphore 0 of the set in `$semid`: ```perl my $op = pack("s!3", 0, -1, 0); semop($semid, $op) or die "wait on semaphore failed: $!"; ``` Signal (increment by 1) the same semaphore: ```perl my $op = pack("s!3", 0, 1, 0); semop($semid, $op) or die "signal failed: $!"; ``` Try to acquire without blocking — fail fast if someone else holds it: ```perl use POSIX qw(EAGAIN); my $op = pack("s!3", 0, -1, IPC_NOWAIT); unless (semop($semid, $op)) { die "semop: $!" unless $! == EAGAIN; # contended; caller decides what to do } ``` Acquire semaphore 0 with automatic release on process exit — the pattern for crash-safe mutual exclusion: ```perl my $op = pack("s!3", 0, -1, SEM_UNDO); semop($semid, $op) or die "lock: $!"; # critical section my $release = pack("s!3", 0, 1, SEM_UNDO); semop($semid, $release) or die "unlock: $!"; ``` Two operations applied atomically — wait for semaphore 0 and semaphore 1 together, releasing neither if either would block: ```perl my $ops = pack("s!3", 0, -1, 0) . pack("s!3", 1, -1, 0); semop($semid, $ops) or die "paired wait: $!"; ``` Wait for semaphore 0 to reach zero — the "barrier" form, where `$semop` is zero rather than negative: ```perl my $op = pack("s!3", 0, 0, 0); semop($semid, $op) or die "barrier: $!"; ``` ## Edge cases - **Short `OPSTRING`**: the length must be an exact multiple of the `sembuf` record size. A truncated or padded string gives `EINVAL`. Always build the string from [`pack`](pack) calls, never by hand. - **Native width matters**: `s!3` not `s3`. A script written with plain `s` works on a 16-bit-short host by accident and breaks elsewhere. - **Zero-length `OPSTRING`**: asks for zero operations. The kernel accepts this and returns success without side effects — rarely what you want. Guard the call with `return unless length $ops`. - **Signal interruption**: a blocked `semop` returning with [`$!`](../perlvar) set to `EINTR` is not a bug — retry unless you installed a handler that wants to abort. Perl's default `$SIG{*}` handling does not automatically restart the syscall. - **`SEM_UNDO` with matched pairs**: the kernel tracks the cumulative adjustment per semaphore per process. Acquiring twice with `SEM_UNDO` then releasing once leaves an outstanding `-1` to be undone on exit. Pair every `-1` with a matching `+1` in normal flow; `SEM_UNDO` is insurance against crashes, not a substitute for releasing. - **Removed set while waiting**: if another process calls [`semctl`](semctl) with `IPC_RMID`, every blocked `semop` returns false with [`$!`](../perlvar) set to `EIDRM`. The semaphore identifier is invalid from that point on. - **Permissions**: `semop` checks alter-permission on the set. A process that can `semget` a read-only handle still cannot `semop` it. - **Large operation counts**: the kernel caps the number of operations per call at `SEMOPM` (typically 32). Building an `OPSTRING` with more records fails with `E2BIG`. ## Differences from upstream Fully compatible with upstream Perl 5.42. ## See also - [`semget`](semget) — obtain the identifier you pass as the first argument; the two are always used together - [`semctl`](semctl) — set or query semaphore values, and remove the set when you're done with it - [`pack`](pack) — build each `sembuf` record; `s!3` is the template you want - [`msgsnd`](msgsnd) — the message-queue counterpart when you need to pass data rather than signal availability - [`shmget`](shmget) — the shared-memory counterpart; semaphores are typically used to guard access to a SysV shared-memory segment - `IPC::SysV` — exports the `IPC_NOWAIT`, `SEM_UNDO`, and `IPC_RMID` constants used in `$semflag` and in [`semctl`](semctl) commands - `IPC::Semaphore` — object wrapper over `semget` / `semop` / `semctl` when a procedural interface feels too bare