S6-S6LOCK(7)

NAME
s6-s6lock - C interface to timed locks

DESCRIPTION
Unix natively provides locks, but the locking primitives are synchronous, so either they are unbounded in execution time or they require polling. s6-s6lock provides poll-free locks that can timeout during attempted acquisition.

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.

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 an s6lockd(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.

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.

  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 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 to select on for reading. Do not read(2) it though.

int s6lock_update(s6lock_t *a)

    Call this function whenever the fd checks readability: it will update a' s internal structures with information from the s6lockd(8) daemon. It returns -1 if 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 to s6lock_update().

    •   If an error occurred, returns -1 and sets errno. The error number may have been transmitted from s6lockd(8).

    •   If the lock has not been acquired yet, returns 0.

    •   If the lock has been acquired, returns 1.

  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 -1 if 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.

SEE ALSO
s6-accessrules(7), s6-fdholder(7), s6-ftrigr(7), s6-ftrigw(7), s6-libs6(7)

[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 <flexibeast@gmail.com> (man page port)