S6-S6LOCK(7)
s6-s6lock β C interface to timed locks π
okay so bestie, let me tell you about s6-s6lock, a page from the Miscellaneous Information Manual. it's giving "I want locks but I don't want to wait forever" energy, and honestly? iconic.
β¨ NAME β¨
s6-s6lock β C interface to timed locks π
πΈ DESCRIPTION πΈ
so like, Unix natively provides locks, right? but the locking primitives are synchronous, which is lowkey a vibe killer β either they're unbounded in execution time or they require polling. and nobody has time for that. s6-s6lock provides poll-free locks that can timeout during attempted acquisition. slay.
check the s6/s6lock.h header for the prototypes. the functions documented here are often simplified macros, for instance relying on the STAMP global variable to hold the current time. fully reentrant functions with more control options are usually available. iykyk.
given the nature of the s6-s6lock library, it makes sense to use an s6lockd(8) s6-local-service(7) concurrently accessed by several applications using such locks to gate shared resources.
if you're not using ans6lockd(8)s6-local-service(7), make sure your application is not disturbed by children it doesn't know it has. using nonblocking waits, ignoring pids you don't know, and using a self-pipe[1] if your application is built around an event loop, are good programming practices. π«Ά
π¦ Starting and ending a session π¦
s6lock_t a = S6LOCK_ZERO ;
tain_t deadline ;
tain_now_g() ;
tain_addsec_g(&deadline, 2)
char const *path = S6LOCK_IPCPATH ;
s6lock_start_g(&a, path, &deadline) ;
// char const *lockdir = "/tmp/lock" ;
// s6lock_startf_g(&a, lockdir, &deadline) ;
s6lock_start_g() starts a session by connecting to an s6lockd(8) service listening on path. the working directory is set by the administrator of the service.
s6lock_startf_g() starts a session with an s6lockd(8) process as a child, using lockdir as its working directory.
a is an s6lock_t structure that must be declared in the stack and initialized to S6LOCK_ZERO. if the session initialization fails, the function returns 0 and errno is set; else the function returns 1. periodt.
if the absolute time deadline is reached and the function has not returned yet, it immediately returns 0 with errno set to ETIMEDOUT. only local interprocess communications are involved; unless your system is heavily overloaded, the function should return near-instantly. one or two seconds of delay between the current time and deadline should be enough: if the function takes more than that to return, then there is a problem with the underlying processes. π
you can have more than one session open in parallel, by declaring several distinct s6lock_t structures and calling s6lock_startf_g() (or s6lock_start_g()) more than once. however, one single session can handle virtually as many concurrent locks as your application needs, so opening several sessions is only useful if you need to acquire locks in various distinct lock directories.
s6lock_end(&a);
s6lock_end() frees all the resources used by the session. the a structure is then reusable for another session. no cap.
π Acquiring and releasing locks π
uint16_t id ;
char const *file = "lockfile" ;
tain_t limit ;
tain_t deadline ;
int r = s6lock_acquire_sh_g (&a, &id, file, &limit, &deadline) ;
/* int r = s6lock_acquire_ex_g (&a, &id, file, &limit, &deadline) ; */
r = s6lock_release_g(&a, id, &deadline) ;
s6lock_acquire_sh_g() instructs the s6lockd(8) daemon, related to the open session represented by the a handle, to try and acquire a shared lock on the file file located under that daemon's working directory (typically /var/lock). file will be interpreted as relative to the daemon's working directory even if it starts with a slash; however, slashes in the middle of file are likely to result in an error. π
limit and deadline are two absolute dates. deadline is a deadline for the execution of the function: if by deadline the function has not returned, then it instantly returns 0 and sets errno to ETIMEDOUT. the function is normally near-instantaneous, so deadline can be very close in the future and serves only as a protection against malicious servers. limit is the acquisition deadline: if by limit the daemon still has not been able to acquire a lock on file, then it will report a timeout to the client. the way that works is honestly so clean.
the function returns 1 in case of success, or 0 if an error occurs, with errno set to a suitable value. if it succeeds, then a 16-bit number is stored into *id; this number serves as an identifier for this lock. π
s6lock_acquire_ex_g() works just like s6lock_acquire_sh_g(), except that the daemon tries to acquire an exclusive lock.
s6lock_release_g() releases the lock identified by id. it normally returns 1. it can return 0 with errno set to a suitable value if it fails. id is not valid after the corresponding lock has been released. the function normally returns instantly, with deadline as a safeguard.
π§ Asynchronously waiting for locks π§
(from now on, the functions are listed with their prototypes instead of usage examples.)
int s6lock_fd(s6lock_t const *a)β returns a file descriptor toselecton for reading. do notread(2)it though. like, just don't. π ββοΈint s6lock_update(s6lock_t *a)β call this function whenever the fd checks readability: it will update a's internal structures with information from thes6lockd(8)daemon. it returns-1if an error occurs; in case of success, it returns the number of identifiers for which something happened.
when s6lock_update() returns, genalloc_s(uint16_t, &a->list) points to an array of genalloc_len(uint16_t, &a->list) 16-bit unsigned integers. those integers are ids waiting to be passed to s6lock_check(). π
int s6lock_check(s6lock_t *a, uint16_t id, char *what)β checks whether the lock identified by id has been acquired. use after a call tos6lock_update().- if an error occurred, returns
-1and setserrno. the error number may have been transmitted froms6lockd(8). - if the lock has not been acquired yet, returns
0. - if the lock has been acquired, returns
1. slay.
- if an error occurred, returns
β Synchronously waiting for locks β
int s6lock_wait_or_g(s6lock_t *a, uint16_t const *idlist, unsigned int n, tain_t const *deadline)β synchronously waits for one of the locks represented by the array pointed to by idlist of length n to be acquired. returnsβ1if it fails, or a nonnegative number on success, which is the index in idlist of the acquired lock's id. if no result has been obtained by deadline, the function returnsβ1 ETIMEDOUT.
-ζ int s6lock_wait_and_g(s6lock_t *a, uint16_t const *idlist, unsigned int n, tain_t const *deadline) β synchronously waits for all of the locks represented by the array pointed to by idlist of length n to be acquired. returns β1 if it fails and 0 if it succeeds. if no result has been obtained by deadline, the function returns β1 ETIMEDOUT. bestie, that's the whole point. π
π₯ SEE ALSO π₯
[1] https://skarnet.org/software/skalibs/libstddjb/selfpipe.html
this man page is ported from the authoritative documentation at: https://skarnet.org/software/s6/libs6/s6lock.html π¦
π AUTHORS π
- Laurent Bercot
- Alexis
<flexibeant@gmail.com>(man page port)
originally from Debian, dated September 29, 2021 β and honestly, the vibes are immaculate. go forth and lock responsibly, bestie. πβ¨π
π¦ where this page comes from
- nixpkgs unstable: s6 2.15.1.0
- nixpkgs unstable: s6-man-pages 2.14.0.1.4
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms