Add Lockfile.AssertLocked and AssertLockedForWriting

This will replace Lockfile.Locked for the sanity-checking
purposes.

It is, just like Locked never was, NOT a way for a caller
to check the current state. Hence the panic() instead of an
error return.

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
Miloslav Trmač 2022-10-19 00:26:02 +02:00
parent db44035e3c
commit 5791b9d6af
3 changed files with 56 additions and 0 deletions

View File

@ -42,6 +42,14 @@ type Locker interface {
// Locked() checks if lock is locked for writing by a thread in this process
Locked() bool
// AssertLocked() can be used by callers that _know_ that they hold the lock (for reading or writing), for sanity checking.
// It might do nothing at all, or it may panic if the caller is not the owner of this lock.
AssertLocked()
// AssertLocked() can be used by callers that _know_ that they hold the lock locked for writing, for sanity checking.
// It might do nothing at all, or it may panic if the caller is not the owner of this lock for writing.
AssertLockedForWriting()
}
var (

View File

@ -220,6 +220,35 @@ func (l *lockfile) Locked() bool {
return l.locked && (l.locktype == unix.F_WRLCK)
}
func (l *lockfile) AssertLocked() {
// DO NOT provide a variant that returns the value of l.locked.
//
// If the caller does not hold the lock, l.locked might nevertheless be true because another goroutine does hold it, and
// we cant tell the difference.
//
// Hence, this “AssertLocked” method, which exists only for sanity checks.
// Dont even bother with l.stateMutex: The caller is expected to hold the lock, and in that case l.locked is constant true
// with no possible writers.
// If the caller does not hold the lock, we are violating the locking/memory model anyway, and accessing the data
// without the lock is more efficient for callers, and potentially more visible to lock analysers for incorrect callers.
if !l.locked {
panic("internal error: lock is not held by the expected owner")
}
}
func (l *lockfile) AssertLockedForWriting() {
// DO NOT provide a variant that returns the current lock state.
//
// The same caveats as for AssertLocked apply equally.
l.AssertLocked()
// Like AssertLocked, dont even bother with l.stateMutex.
if l.locktype != unix.F_WRLCK {
panic("internal error: lock is not held for writing")
}
}
// Touch updates the lock file with the UID of the user.
func (l *lockfile) Touch() error {
l.stateMutex.Lock()

View File

@ -51,6 +51,25 @@ func (l *lockfile) Locked() bool {
return l.locked
}
func (l *lockfile) AssertLocked() {
// DO NOT provide a variant that returns the value of l.locked.
//
// If the caller does not hold the lock, l.locked might nevertheless be true because another goroutine does hold it, and
// we cant tell the difference.
//
// Hence, this “AssertLocked” method, which exists only for sanity checks.
if !l.locked {
panic("internal error: lock is not held by the expected owner")
}
}
func (l *lockfile) AssertLockedForWriting() {
// DO NOT provide a variant that returns the current lock state.
//
// The same caveats as for AssertLocked apply equally.
l.AssertLocked() // The current implementation does not distinguish between read and write locks.
}
func (l *lockfile) Modified() (bool, error) {
return false, nil
}