mirror of
https://github.com/kopia/kopia.git
synced 2026-01-28 08:18:58 -05:00
From https://github.com/google/gvisor/tree/master/tools/checklocks This will perform static verification that we're using `sync.Mutex`, `sync.RWMutex` and `atomic` correctly to guard access to certain fields. This was mostly just a matter of adding annotations to indicate which fields are guarded by which mutex. In a handful of places the code had to be refactored to allow static analyzer to do its job better or to not be confused by some constructs. In one place this actually uncovered a bug where a function was not releasing a lock properly in an error case. The check is part of `make lint` but can also be invoked by `make check-locks`.
177 lines
3.6 KiB
Go
177 lines
3.6 KiB
Go
package gather
|
|
|
|
import (
|
|
"io"
|
|
"sync"
|
|
|
|
"github.com/kopia/kopia/repo/logging"
|
|
)
|
|
|
|
var log = logging.Module("gather") // +checklocksignore
|
|
|
|
// WriteBuffer is a write buffer for content of unknown size that manages
|
|
// data in a series of byte slices of uniform size.
|
|
type WriteBuffer struct {
|
|
alloc *chunkAllocator
|
|
mu sync.Mutex
|
|
inner Bytes
|
|
}
|
|
|
|
// Close releases all memory allocated by this buffer.
|
|
func (b *WriteBuffer) Close() {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
if b.alloc != nil {
|
|
for _, s := range b.inner.Slices {
|
|
b.alloc.releaseChunk(s)
|
|
}
|
|
|
|
b.alloc = nil
|
|
}
|
|
|
|
b.inner.invalidate()
|
|
}
|
|
|
|
// MakeContiguous ensures the write buffer consists of exactly one contiguous single slice of the provided length
|
|
// and returns the slice.
|
|
func (b *WriteBuffer) MakeContiguous(length int) []byte {
|
|
b.Reset()
|
|
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
var v []byte
|
|
|
|
switch {
|
|
case length <= typicalContiguousAllocator.chunkSize:
|
|
// most commonly used allocator for default chunk size with max 8MB
|
|
b.alloc = typicalContiguousAllocator
|
|
v = b.allocChunk()[0:length]
|
|
|
|
case length <= maxContiguousAllocator.chunkSize:
|
|
b.alloc = maxContiguousAllocator
|
|
v = b.allocChunk()[0:length]
|
|
|
|
default:
|
|
v = make([]byte, length)
|
|
}
|
|
|
|
b.inner.Slices = [][]byte{v}
|
|
|
|
return v
|
|
}
|
|
|
|
// Reset resets buffer back to empty.
|
|
func (b *WriteBuffer) Reset() {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
if b.alloc != nil {
|
|
for _, s := range b.inner.Slices {
|
|
b.alloc.releaseChunk(s)
|
|
}
|
|
}
|
|
|
|
b.inner.invalidate()
|
|
|
|
b.alloc = nil
|
|
|
|
b.inner = Bytes{}
|
|
}
|
|
|
|
// Write implements io.Writer for appending to the buffer.
|
|
func (b *WriteBuffer) Write(data []byte) (n int, err error) {
|
|
b.Append(data)
|
|
return len(data), nil
|
|
}
|
|
|
|
// AppendSectionTo appends the section of the buffer to the provided slice and returns it.
|
|
func (b *WriteBuffer) AppendSectionTo(w io.Writer, offset, size int) error {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
return b.inner.AppendSectionTo(w, offset, size)
|
|
}
|
|
|
|
// Length returns the combined length of all slices.
|
|
func (b *WriteBuffer) Length() int {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
return b.inner.Length()
|
|
}
|
|
|
|
// ToByteSlice appends all bytes to the provided slice and returns it.
|
|
func (b *WriteBuffer) ToByteSlice() []byte {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
return b.inner.ToByteSlice()
|
|
}
|
|
|
|
// Bytes returns inner gather.Bytes.
|
|
func (b *WriteBuffer) Bytes() Bytes {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
return b.inner
|
|
}
|
|
|
|
// Append appends the specified slice of bytes to the buffer.
|
|
func (b *WriteBuffer) Append(data []byte) {
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
|
|
b.inner.assertValid()
|
|
|
|
if len(b.inner.Slices) == 0 {
|
|
b.inner.sliceBuf[0] = b.allocChunk()
|
|
b.inner.Slices = b.inner.sliceBuf[0:1]
|
|
}
|
|
|
|
for len(data) > 0 {
|
|
ndx := len(b.inner.Slices) - 1
|
|
remaining := cap(b.inner.Slices[ndx]) - len(b.inner.Slices[ndx])
|
|
|
|
if remaining == 0 {
|
|
b.inner.Slices = append(b.inner.Slices, b.allocChunk())
|
|
ndx = len(b.inner.Slices) - 1
|
|
remaining = cap(b.inner.Slices[ndx]) - len(b.inner.Slices[ndx])
|
|
}
|
|
|
|
chunkSize := remaining
|
|
if chunkSize > len(data) {
|
|
chunkSize = len(data)
|
|
}
|
|
|
|
b.inner.Slices[ndx] = append(b.inner.Slices[ndx], data[0:chunkSize]...)
|
|
data = data[chunkSize:]
|
|
}
|
|
}
|
|
|
|
func (b *WriteBuffer) allocChunk() []byte {
|
|
if b.alloc == nil {
|
|
b.alloc = defaultAllocator
|
|
}
|
|
|
|
return b.alloc.allocChunk()
|
|
}
|
|
|
|
// Dup creates a clone of the WriteBuffer.
|
|
func (b *WriteBuffer) Dup() *WriteBuffer {
|
|
dup := &WriteBuffer{}
|
|
|
|
b.mu.Lock()
|
|
defer b.mu.Unlock()
|
|
dup.alloc = b.alloc
|
|
dup.inner = FromSlice(b.inner.ToByteSlice())
|
|
|
|
return dup
|
|
}
|
|
|
|
// NewWriteBuffer creates new write buffer.
|
|
func NewWriteBuffer() *WriteBuffer {
|
|
return &WriteBuffer{}
|
|
}
|