Merge branch 'lh/send-email-hide-x-mailer'
[gitweb.git] / Documentation / technical / api-lockfile.txt
index 73ac1025fd92516899a1b4ef25eedbc9fe845c1f..93b5f23e4c8a1bb45dc26d5f9cec402df7f57ee4 100644 (file)
 lockfile API
 ============
 
-Talk about <lockfile.c>, things like:
+The lockfile API serves two purposes:
 
-* lockfile lifetime -- atexit(3) looks at them, do not put them on the
-  stack;
-* hold_lock_file_for_update()
-* commit_lock_file()
-* rollback_rock_file()
+* Mutual exclusion and atomic file updates. When we want to change a
+  file, we create a lockfile `<filename>.lock`, write the new file
+  contents into it, and then rename the lockfile to its final
+  destination `<filename>`. We create the `<filename>.lock` file with
+  `O_CREAT|O_EXCL` so that we can notice and fail if somebody else has
+  already locked the file, then atomically rename the lockfile to its
+  final destination to commit the changes and unlock the file.
 
-(JC, Dscho, Shawn)
+* Automatic cruft removal. If the program exits after we lock a file
+  but before the changes have been committed, we want to make sure
+  that we remove the lockfile. This is done by remembering the
+  lockfiles we have created in a linked list and setting up an
+  `atexit(3)` handler and a signal handler that clean up the
+  lockfiles. This mechanism ensures that outstanding lockfiles are
+  cleaned up if the program exits (including when `die()` is called)
+  or if the program dies on a signal.
+
+Please note that lockfiles only block other writers. Readers do not
+block, but they are guaranteed to see either the old contents of the
+file or the new contents of the file (assuming that the filesystem
+implements `rename(2)` atomically).
+
+
+Calling sequence
+----------------
+
+The caller:
+
+* Allocates a `struct lock_file` either as a static variable or on the
+  heap, initialized to zeros. Once you use the structure to call the
+  `hold_lock_file_*` family of functions, it belongs to the lockfile
+  subsystem and its storage must remain valid throughout the life of
+  the program (i.e. you cannot use an on-stack variable to hold this
+  structure).
+
+* Attempts to create a lockfile by passing that variable and the path
+  of the final destination (e.g. `$GIT_DIR/index`) to
+  `hold_lock_file_for_update` or `hold_lock_file_for_append`.
+
+* Writes new content for the destination file by either:
+
+  * writing to the file descriptor returned by the `hold_lock_file_*`
+    functions (also available via `lock->fd`).
+
+  * calling `fdopen_lock_file` to get a `FILE` pointer for the open
+    file and writing to the file using stdio.
+
+When finished writing, the caller can:
+
+* Close the file descriptor and rename the lockfile to its final
+  destination by calling `commit_lock_file` or `commit_lock_file_to`.
+
+* Close the file descriptor and remove the lockfile by calling
+  `rollback_lock_file`.
+
+* Close the file descriptor without removing or renaming the lockfile
+  by calling `close_lock_file`, and later call `commit_lock_file`,
+  `commit_lock_file_to`, `rollback_lock_file`, or `reopen_lock_file`.
+
+Even after the lockfile is committed or rolled back, the `lock_file`
+object must not be freed or altered by the caller. However, it may be
+reused; just pass it to another call of `hold_lock_file_for_update` or
+`hold_lock_file_for_append`.
+
+If the program exits before you have called one of `commit_lock_file`,
+`commit_lock_file_to`, `rollback_lock_file`, or `close_lock_file`, an
+`atexit(3)` handler will close and remove the lockfile, rolling back
+any uncommitted changes.
+
+If you need to close the file descriptor you obtained from a
+`hold_lock_file_*` function yourself, do so by calling
+`close_lock_file`. You should never call `close(2)` or `fclose(3)`
+yourself! Otherwise the `struct lock_file` structure would still think
+that the file descriptor needs to be closed, and a commit or rollback
+would result in duplicate calls to `close(2)`. Worse yet, if you close
+and then later open another file descriptor for a completely different
+purpose, then a commit or rollback might close that unrelated file
+descriptor.
+
+
+Error handling
+--------------
+
+The `hold_lock_file_*` functions return a file descriptor on success
+or -1 on failure (unless `LOCK_DIE_ON_ERROR` is used; see below). On
+errors, `errno` describes the reason for failure. Errors can be
+reported by passing `errno` to one of the following helper functions:
+
+unable_to_lock_message::
+
+       Append an appropriate error message to a `strbuf`.
+
+unable_to_lock_error::
+
+       Emit an appropriate error message using `error()`.
+
+unable_to_lock_die::
+
+       Emit an appropriate error message and `die()`.
+
+Similarly, `commit_lock_file`, `commit_lock_file_to`, and
+`close_lock_file` return 0 on success. On failure they set `errno`
+appropriately, do their best to roll back the lockfile, and return -1.
+
+
+Flags
+-----
+
+The following flags can be passed to `hold_lock_file_for_update` or
+`hold_lock_file_for_append`:
+
+LOCK_NO_DEREF::
+
+       Usually symbolic links in the destination path are resolved
+       and the lockfile is created by adding ".lock" to the resolved
+       path. If `LOCK_NO_DEREF` is set, then the lockfile is created
+       by adding ".lock" to the path argument itself. This option is
+       used, for example, when locking a symbolic reference, which
+       for backwards-compatibility reasons can be a symbolic link
+       containing the name of the referred-to-reference.
+
+LOCK_DIE_ON_ERROR::
+
+       If a lock is already taken for the file, `die()` with an error
+       message. If this option is not specified, trying to lock a
+       file that is already locked returns -1 to the caller.
+
+
+The functions
+-------------
+
+hold_lock_file_for_update::
+
+       Take a pointer to `struct lock_file`, the path of the file to
+       be locked (e.g. `$GIT_DIR/index`) and a flags argument (see
+       above). Attempt to create a lockfile for the destination and
+       return the file descriptor for writing to the file.
+
+hold_lock_file_for_append::
+
+       Like `hold_lock_file_for_update`, but before returning copy
+       the existing contents of the file (if any) to the lockfile and
+       position its write pointer at the end of the file.
+
+fdopen_lock_file::
+
+       Associate a stdio stream with the lockfile. Return NULL
+       (*without* rolling back the lockfile) on error. The stream is
+       closed automatically when `close_lock_file` is called or when
+       the file is committed or rolled back.
+
+get_locked_file_path::
+
+       Return the path of the file that is locked by the specified
+       lock_file object. The caller must free the memory.
+
+commit_lock_file::
+
+       Take a pointer to the `struct lock_file` initialized with an
+       earlier call to `hold_lock_file_for_update` or
+       `hold_lock_file_for_append`, close the file descriptor, and
+       rename the lockfile to its final destination. Return 0 upon
+       success. On failure, roll back the lock file and return -1,
+       with `errno` set to the value from the failing call to
+       `close(2)` or `rename(2)`. It is a bug to call
+       `commit_lock_file` for a `lock_file` object that is not
+       currently locked.
+
+commit_lock_file_to::
+
+       Like `commit_lock_file()`, except that it takes an explicit
+       `path` argument to which the lockfile should be renamed. The
+       `path` must be on the same filesystem as the lock file.
+
+rollback_lock_file::
+
+       Take a pointer to the `struct lock_file` initialized with an
+       earlier call to `hold_lock_file_for_update` or
+       `hold_lock_file_for_append`, close the file descriptor and
+       remove the lockfile. It is a NOOP to call
+       `rollback_lock_file()` for a `lock_file` object that has
+       already been committed or rolled back.
+
+close_lock_file::
+
+       Take a pointer to the `struct lock_file` initialized with an
+       earlier call to `hold_lock_file_for_update` or
+       `hold_lock_file_for_append`. Close the file descriptor (and
+       the file pointer if it has been opened using
+       `fdopen_lock_file`). Return 0 upon success. On failure to
+       `close(2)`, return a negative value and roll back the lock
+       file. Usually `commit_lock_file`, `commit_lock_file_to`, or
+       `rollback_lock_file` should eventually be called if
+       `close_lock_file` succeeds.
+
+reopen_lock_file::
+
+       Re-open a lockfile that has been closed (using
+       `close_lock_file`) but not yet committed or rolled back. This
+       can be used to implement a sequence of operations like the
+       following:
+
+       * Lock file.
+
+       * Write new contents to lockfile, then `close_lock_file` to
+         cause the contents to be written to disk.
+
+       * Pass the name of the lockfile to another program to allow it
+         (and nobody else) to inspect the contents you wrote, while
+         still holding the lock yourself.
+
+       * `reopen_lock_file` to reopen the lockfile. Make further
+         updates to the contents.
+
+       * `commit_lock_file` to make the final version permanent.