git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v7 02/38] api-lockfile: revise and expand the documentation

From
Michael Haggerty <mhagger@alum.mit.edu>
Date
Oct 1, 2014, 10:28 UTC
Message-ID
<1412159322-2622-3-git-send-email-mhagger@alum.mit.edu>
In-Reply-To
<1412159322-2622-1-git-send-email-mhagger@alum.mit.edu>

Document a couple more functions and the flags argument as used by hold_lock_file_for_update() and hold_lock_file_for_append(). Reorganize the document to make it more accessible.

Helped-by: Jonathan Nieder <jrnieder@gmail.com>
Helped-by: Junio Hamano <gitster@pobox.com>
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
 Documentation/technical/api-lockfile.txt | 221 +++++++++++++++++++++++--------
 1 file changed, 167 insertions(+), 54 deletions(-)
diff --git a/Documentation/technical/api-lockfile.txt b/Documentation/technical/api-lockfile.txt
index dd89404..99830f3 100644
--- a/Documentation/technical/api-lockfile.txt
+++ b/Documentation/technical/api-lockfile.txt
@@ -3,20 +3,125 @@ lockfile API
 
 The lockfile API serves two purposes:
 
-* Mutual exclusion.  When we write out a new index file, first
-  we create a new file `$GIT_DIR/index.lock`, write the new
-  contents into it, and rename it to the final destination
-  `$GIT_DIR/index`.  We try to create the `$GIT_DIR/index.lock`
-  file with O_EXCL so that we can notice and fail when somebody
-  else is already trying to update the index file.
-
-* Automatic cruft removal.  After we create the "lock" file, we
-  may decide to `die()`, and we would want to make sure that we
-  remove the file that has not been committed to its final
-  destination.  This is done by remembering the lockfiles we
-  created in a linked list and cleaning them up from an
-  `atexit(3)` handler.  Outstanding lockfiles are also removed
-  when the program dies on a signal.
+* 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.
+
+* 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 writing to the file
+  descriptor returned by those functions (also available via
+  `lock->fd`).
+
+When finished writing, the caller can:
+
+* Close the file descriptor and rename the lockfile to its final
+  destination by calling `commit_lock_file`.
+
+* 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`,
+  `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`,
+`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)` yourself!
+Otherwise the `struct lock_file` structure would still think that the
+file descriptor needs to be closed, and a later call to
+`commit_lock_file` or `rollback_lock_file` or program exit would
+result in duplicate calls to `close(2)`. Worse yet, if you `close(2)`
+and then later open another file descriptor for a completely different
+purpose, then a call to `commit_lock_file` or `rollback_lock_file`
+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()`.
+
+
+Flags
+-----
+
+The following flags can be passed to `hold_lock_file_for_update` or
+`hold_lock_file_for_append`:
+
+LOCK_NODEREF::
+
+	Usually symbolic links in the destination path are resolved
+	and the lockfile is created by adding ".lock" to the resolved
+	path. If `LOCK_NODEREF` 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
@@ -24,51 +129,59 @@ The functions
 
 hold_lock_file_for_update::
 
-	Take a pointer to `struct lock_file`, the filename of
-	the final destination (e.g. `$GIT_DIR/index`) and a flag
-	`die_on_error`.  Attempt to create a lockfile for the
-	destination and return the file descriptor for writing
-	to the file.  If `die_on_error` flag is true, it dies if
-	a lock is already taken for the file; otherwise it
-	returns a negative integer to the caller on failure.
+	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.
 
 commit_lock_file::
 
-	Take a pointer to the `struct lock_file` initialized
-	with an earlier call to `hold_lock_file_for_update()`,
-	close the file descriptor and rename the lockfile to its
-	final destination.  Returns 0 upon success, a negative
-	value on failure to close(2) or rename(2).
+	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 or a negative value on failure to `close(2)` or
+	`rename(2)`.
 
 rollback_lock_file::
 
-	Take a pointer to the `struct lock_file` initialized
-	with an earlier call to `hold_lock_file_for_update()`,
-	close the file descriptor and remove the lockfile.
+	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.
 
 close_lock_file::
-	Take a pointer to the `struct lock_file` initialized
-	with an earlier call to `hold_lock_file_for_update()`,
-	and close the file descriptor.  Returns 0 upon success,
-	a negative value on failure to close(2).
-
-Because the structure is used in an `atexit(3)` handler, its
-storage has to stay throughout the life of the program.  It
-cannot be an auto variable allocated on the stack.
-
-Call `commit_lock_file()` or `rollback_lock_file()` when you are
-done writing to the file descriptor.  If you do not call either
-and simply `exit(3)` from the program, an `atexit(3)` handler
-will close and remove the lockfile.
-
-If you need to close the file descriptor you obtained from
-`hold_lock_file_for_update` function yourself, do so by calling
-`close_lock_file()`.  You should never call `close(2)` yourself!
-Otherwise the `struct
-lock_file` structure still remembers that the file descriptor
-needs to be closed, and a later call to `commit_lock_file()` or
-`rollback_lock_file()` will result in duplicate calls to
-`close(2)`.  Worse yet, if you `close(2)`, open another file
-descriptor for completely different purpose, and then call
-`commit_lock_file()` or `rollback_lock_file()`, they may close
-that unrelated file descriptor.
+
+	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`, and close the file descriptor.
+	Return 0 upon success or a negative value on failure to
+	close(2). Usually `commit_lock_file` or `rollback_lock_file`
+	should be called after `close_lock_file`.
+
+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.
-- 
2.1.0
Previous: Michael HaggertyNext: Michael Haggerty
Message 3 of 43 in “Lockfile correctness and refactoring”
  1. 00/38 Lockfile correctness and refactoringMichael Haggerty, Oct 1, 2014
  2. 01/38 unable_to_lock_die(): rename function from unable_to_lock_index_die()Michael Haggerty, Oct 1, 2014
  3. 02/38 api-lockfile: revise and expand the documentationMichael Haggerty, Oct 1, 2014
  4. 03/38 close_lock_file(): exit (successfully) if file is already closedMichael Haggerty, Oct 1, 2014
  5. 04/38 rollback_lock_file(): do not clear filename redundantlyMichael Haggerty, Oct 1, 2014
  6. 05/38 rollback_lock_file(): exit early if lock is not activeMichael Haggerty, Oct 1, 2014
  7. 06/38 rollback_lock_file(): set fd to -1Michael Haggerty, Oct 1, 2014
  8. 07/38 lockfile: unlock file if lockfile permissions cannot be adjustedMichael Haggerty, Oct 1, 2014
  9. 08/38 hold_lock_file_for_append(): release lock on errorsMichael Haggerty, Oct 1, 2014
  10. 09/38 lock_file(): always initialize and register lock_file objectMichael Haggerty, Oct 1, 2014
  11. René ScharfeOct 1, 2014
  12. Michael HaggertyOct 1, 2014
  13. Junio C HamanoOct 1, 2014
  14. 10/38 lockfile.c: document the various states of lock_file objectsMichael Haggerty, Oct 1, 2014
  15. 11/38 cache.h: define constants LOCK_SUFFIX and LOCK_SUFFIX_LENMichael Haggerty, Oct 1, 2014
  16. 12/38 delete_ref_loose(): don't muck around in the lock_file's filenameMichael Haggerty, Oct 1, 2014
  17. 13/38 prepare_index(): declare return value to be (const char *)Michael Haggerty, Oct 1, 2014
  18. 14/38 lock_file(): exit early if lockfile cannot be openedMichael Haggerty, Oct 1, 2014
  19. 15/38 remove_lock_file(): call rollback_lock_file()Michael Haggerty, Oct 1, 2014
  20. 16/38 commit_lock_file(): inline temporary variableMichael Haggerty, Oct 1, 2014
  21. 17/38 commit_lock_file(): die() if called for unlocked lockfile objectMichael Haggerty, Oct 1, 2014
  22. 18/38 close_lock_file(): if close fails, roll backMichael Haggerty, Oct 1, 2014
  23. 19/38 commit_lock_file(): rollback lock file on failure to renameMichael Haggerty, Oct 1, 2014
  24. 20/38 api-lockfile: document edge casesMichael Haggerty, Oct 1, 2014
  25. 21/38 dump_marks(): remove a redundant call to rollback_lock_file()Michael Haggerty, Oct 1, 2014
  26. 22/38 git_config_set_multivar_in_file(): avoid call to rollback_lock_file()Michael Haggerty, Oct 1, 2014
  27. 23/38 lockfile: avoid transitory invalid statesMichael Haggerty, Oct 1, 2014
  28. 24/38 struct lock_file: declare some fields volatileMichael Haggerty, Oct 1, 2014
  29. 25/38 try_merge_strategy(): remove redundant lock_file allocationMichael Haggerty, Oct 1, 2014
  30. 26/38 try_merge_strategy(): use a statically-allocated lock_file objectMichael Haggerty, Oct 1, 2014
  31. 27/38 commit_lock_file(): use a strbuf to manage temporary spaceMichael Haggerty, Oct 1, 2014
  32. 28/38 Change lock_file::filename into a strbufMichael Haggerty, Oct 1, 2014
  33. 29/38 resolve_symlink(): use a strbuf for internal scratch spaceMichael Haggerty, Oct 1, 2014
  34. 30/38 resolve_symlink(): take a strbuf parameterMichael Haggerty, Oct 1, 2014
  35. 31/38 trim_last_path_component(): replace last_path_elm()Michael Haggerty, Oct 1, 2014
  36. 32/38 Extract a function commit_lock_file_to()Michael Haggerty, Oct 1, 2014
  37. 33/38 Rename LOCK_NODEREF to LOCK_NO_DEREFMichael Haggerty, Oct 1, 2014
  38. 34/38 lockfile.c: rename static functionsMichael Haggerty, Oct 1, 2014
  39. 35/38 get_locked_file_path(): new functionMichael Haggerty, Oct 1, 2014
  40. 36/38 hold_lock_file_for_append(): restore errno before returningMichael Haggerty, Oct 1, 2014
  41. 37/38 Move read_index() definition to read-cache.cMichael Haggerty, Oct 1, 2014
  42. 38/38 lockfile.h: extract new header file for the functions in lockfile.cMichael Haggerty, Oct 1, 2014
  43. Junio C HamanoOct 1, 2014

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.