mirror of
https://github.com/containers/podman.git
synced 2025-12-09 07:09:03 +08:00
83 lines
2.7 KiB
Go
83 lines
2.7 KiB
Go
package lockfile
|
|
|
|
import (
|
|
"bytes"
|
|
cryptorand "crypto/rand"
|
|
"encoding/binary"
|
|
"os"
|
|
"sync/atomic"
|
|
"time"
|
|
)
|
|
|
|
// LastWrite is an opaque identifier of the last write to some *LockFile.
|
|
// It can be used by users of a *LockFile to determine if the lock indicates changes
|
|
// since the last check.
|
|
//
|
|
// Never construct a LastWrite manually; only accept it from *LockFile methods, and pass it back.
|
|
type LastWrite struct {
|
|
// Never modify fields of a LastWrite object; it has value semantics.
|
|
state []byte // Contents of the lock file.
|
|
}
|
|
|
|
var lastWriterIDCounter uint64 // Private state for newLastWriterID
|
|
|
|
const lastWriterIDSize = 64 // This must be the same as len(stringid.GenerateRandomID)
|
|
// newLastWrite returns a new "last write" ID.
|
|
// The value must be different on every call, and also differ from values
|
|
// generated by other processes.
|
|
func newLastWrite() LastWrite {
|
|
// The ID is (PID, time, per-process counter, random)
|
|
// PID + time represents both a unique process across reboots,
|
|
// and a specific time within the process; the per-process counter
|
|
// is an extra safeguard for in-process concurrency.
|
|
// The random part disambiguates across process namespaces
|
|
// (where PID values might collide), serves as a general-purpose
|
|
// extra safety, _and_ is used to pad the output to lastWriterIDSize,
|
|
// because other versions of this code exist and they don't work
|
|
// efficiently if the size of the value changes.
|
|
pid := os.Getpid()
|
|
tm := time.Now().UnixNano()
|
|
counter := atomic.AddUint64(&lastWriterIDCounter, 1)
|
|
|
|
res := make([]byte, lastWriterIDSize)
|
|
binary.LittleEndian.PutUint64(res[0:8], uint64(tm))
|
|
binary.LittleEndian.PutUint64(res[8:16], counter)
|
|
binary.LittleEndian.PutUint32(res[16:20], uint32(pid))
|
|
if n, err := cryptorand.Read(res[20:lastWriterIDSize]); err != nil || n != lastWriterIDSize-20 {
|
|
panic(err) // This shouldn't happen
|
|
}
|
|
|
|
return LastWrite{
|
|
state: res,
|
|
}
|
|
}
|
|
|
|
// serialize returns bytes to write to the lock file to represent the specified write.
|
|
func (lw LastWrite) serialize() []byte {
|
|
if lw.state == nil {
|
|
panic("LastWrite.serialize on an uninitialized object")
|
|
}
|
|
return lw.state
|
|
}
|
|
|
|
// Equals returns true if lw matches other
|
|
func (lw LastWrite) equals(other LastWrite) bool {
|
|
if lw.state == nil {
|
|
panic("LastWrite.equals on an uninitialized object")
|
|
}
|
|
if other.state == nil {
|
|
panic("LastWrite.equals with an uninitialized counterparty")
|
|
}
|
|
return bytes.Equal(lw.state, other.state)
|
|
}
|
|
|
|
// newLastWriteFromData returns a LastWrite corresponding to data that came from a previous LastWrite.serialize
|
|
func newLastWriteFromData(serialized []byte) LastWrite {
|
|
if serialized == nil {
|
|
panic("newLastWriteFromData with nil data")
|
|
}
|
|
return LastWrite{
|
|
state: serialized,
|
|
}
|
|
}
|