1060 lines
30 KiB
Go
1060 lines
30 KiB
Go
// Copyright 2018 The gVisor Authors.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package lock
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
)
|
|
|
|
type entry struct {
|
|
Lock
|
|
LockRange
|
|
}
|
|
|
|
func equals(e0, e1 []entry) bool {
|
|
if len(e0) != len(e1) {
|
|
return false
|
|
}
|
|
for i := range e0 {
|
|
for k := range e0[i].Lock.Readers {
|
|
if !e1[i].Lock.Readers[k] {
|
|
return false
|
|
}
|
|
}
|
|
for k := range e1[i].Lock.Readers {
|
|
if !e0[i].Lock.Readers[k] {
|
|
return false
|
|
}
|
|
}
|
|
if !reflect.DeepEqual(e0[i].LockRange, e1[i].LockRange) {
|
|
return false
|
|
}
|
|
if e0[i].Lock.HasWriter != e1[i].Lock.HasWriter {
|
|
return false
|
|
}
|
|
if e0[i].Lock.Writer != e1[i].Lock.Writer {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// fill a LockSet with consecutive region locks. Will panic if
|
|
// LockRanges are not consecutive.
|
|
func fill(entries []entry) LockSet {
|
|
l := LockSet{}
|
|
for _, e := range entries {
|
|
gap := l.FindGap(e.LockRange.Start)
|
|
if !gap.Ok() {
|
|
panic("cannot insert into existing segment")
|
|
}
|
|
l.Insert(gap, e.LockRange, e.Lock)
|
|
}
|
|
return l
|
|
}
|
|
|
|
func TestCanLockEmpty(t *testing.T) {
|
|
l := LockSet{}
|
|
|
|
// Expect to be able to take any locks given that the set is empty.
|
|
eof := l.FirstGap().End()
|
|
r := LockRange{0, eof}
|
|
if !l.canLock(1, ReadLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", ReadLock, r, 1)
|
|
}
|
|
if !l.canLock(2, ReadLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", ReadLock, r, 2)
|
|
}
|
|
if !l.canLock(1, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", WriteLock, r, 1)
|
|
}
|
|
if !l.canLock(2, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", WriteLock, r, 2)
|
|
}
|
|
}
|
|
|
|
func TestCanLock(t *testing.T) {
|
|
// + -------------- + ---------- + -------------- + --------- +
|
|
// | Readers 1 & 2 | Readers 1 | Readers 1 & 3 | Writer 1 |
|
|
// + ------------- + ---------- + -------------- + --------- +
|
|
// 0 1024 2048 3072 4096
|
|
l := fill([]entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
|
LockRange: LockRange{1024, 2048},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true, 3: true}},
|
|
LockRange: LockRange{2048, 3072},
|
|
},
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 1},
|
|
LockRange: LockRange{3072, 4096},
|
|
},
|
|
})
|
|
|
|
// Now that we have a mildly interesting layout, try some checks on different
|
|
// ranges, uids, and lock types.
|
|
//
|
|
// Expect to be able to extend the read lock, despite the writer lock, because
|
|
// the writer has the same uid as the requested read lock.
|
|
r := LockRange{0, 8192}
|
|
if !l.canLock(1, ReadLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", ReadLock, r, 1)
|
|
}
|
|
// Expect to *not* be able to extend the read lock since there is an overlapping
|
|
// writer region locked by someone other than the uid.
|
|
if l.canLock(2, ReadLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got true, want false", ReadLock, r, 2)
|
|
}
|
|
// Expect to be able to extend the read lock if there are only other readers in
|
|
// the way.
|
|
r = LockRange{64, 3072}
|
|
if !l.canLock(2, ReadLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", ReadLock, r, 2)
|
|
}
|
|
// Expect to be able to set a read lock beyond the range of any existing locks.
|
|
r = LockRange{4096, 10240}
|
|
if !l.canLock(2, ReadLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", ReadLock, r, 2)
|
|
}
|
|
|
|
// Expect to not be able to take a write lock with other readers in the way.
|
|
r = LockRange{0, 8192}
|
|
if l.canLock(1, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got true, want false", WriteLock, r, 1)
|
|
}
|
|
// Expect to be able to extend the write lock for the same uid.
|
|
r = LockRange{3072, 8192}
|
|
if !l.canLock(1, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", WriteLock, r, 1)
|
|
}
|
|
// Expect to not be able to overlap a write lock for two different uids.
|
|
if l.canLock(2, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got true, want false", WriteLock, r, 2)
|
|
}
|
|
// Expect to be able to set a write lock that is beyond the range of any
|
|
// existing locks.
|
|
r = LockRange{8192, 10240}
|
|
if !l.canLock(2, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", WriteLock, r, 2)
|
|
}
|
|
// Expect to be able to upgrade a read lock (any portion of it).
|
|
r = LockRange{1024, 2048}
|
|
if !l.canLock(1, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", WriteLock, r, 1)
|
|
}
|
|
r = LockRange{1080, 2000}
|
|
if !l.canLock(1, WriteLock, r) {
|
|
t.Fatalf("canLock type %d for range %v and uid %d got false, want true", WriteLock, r, 1)
|
|
}
|
|
}
|
|
|
|
func TestSetLock(t *testing.T) {
|
|
tests := []struct {
|
|
// description of test.
|
|
name string
|
|
|
|
// LockSet entries to pre-fill.
|
|
before []entry
|
|
|
|
// Description of region to lock:
|
|
//
|
|
// start is the file offset of the lock.
|
|
start uint64
|
|
// end is the end file offset of the lock.
|
|
end uint64
|
|
// uid of lock attempter.
|
|
uid UniqueID
|
|
// lock type requested.
|
|
lockType LockType
|
|
|
|
// success is true if taking the above
|
|
// lock should succeed.
|
|
success bool
|
|
|
|
// Expected layout of the set after locking
|
|
// if success is true.
|
|
after []entry
|
|
}{
|
|
{
|
|
name: "set zero length ReadLock on empty set",
|
|
start: 0,
|
|
end: 0,
|
|
uid: 0,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
},
|
|
{
|
|
name: "set zero length WriteLock on empty set",
|
|
start: 0,
|
|
end: 0,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: true,
|
|
},
|
|
{
|
|
name: "set ReadLock on empty set",
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "set WriteLock on empty set",
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: true,
|
|
// + ----------------------------------------- +
|
|
// | Writer 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "set ReadLock on WriteLock same uid",
|
|
// + ----------------------------------------- +
|
|
// | Writer 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 0,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// + ----------- + --------------------------- +
|
|
// | Readers 0 | Writer 0 |
|
|
// + ----------- + --------------------------- +
|
|
// 0 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "set WriteLock on ReadLock same uid",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: true,
|
|
// + ----------- + --------------------------- +
|
|
// | Writer 0 | Readers 0 |
|
|
// + ----------- + --------------------------- +
|
|
// 0 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "set ReadLock on WriteLock different uid",
|
|
// + ----------------------------------------- +
|
|
// | Writer 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 1,
|
|
lockType: ReadLock,
|
|
success: false,
|
|
},
|
|
{
|
|
name: "set WriteLock on ReadLock different uid",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 1,
|
|
lockType: WriteLock,
|
|
success: false,
|
|
},
|
|
{
|
|
name: "split ReadLock for overlapping lock at start 0",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 1,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// + -------------- + --------------------------- +
|
|
// | Readers 0 & 1 | Readers 0 |
|
|
// + -------------- + --------------------------- +
|
|
// 0 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "split ReadLock for overlapping lock at non-zero start",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 4096,
|
|
end: 8192,
|
|
uid: 1,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// + ---------- + -------------- + ----------- +
|
|
// | Readers 0 | Readers 0 & 1 | Readers 0 |
|
|
// + ---------- + -------------- + ----------- +
|
|
// 0 4096 8192 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{4096, 8192},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{8192, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "fill front gap with ReadLock",
|
|
// + --------- + ---------------------------- +
|
|
// | gap | Readers 0 |
|
|
// + --------- + ---------------------------- +
|
|
// 0 1024 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 8192,
|
|
uid: 0,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "fill end gap with ReadLock",
|
|
// + ---------------------------- +
|
|
// | Readers 0 |
|
|
// + ---------------------------- +
|
|
// 0 4096
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
},
|
|
start: 1024,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// Note that this is not merged after lock does a Split. This is
|
|
// fine because the two locks will still *behave* as one. In other
|
|
// words we can fragment any lock all we want and semantically it
|
|
// makes no difference.
|
|
//
|
|
// + ----------- + --------------------------- +
|
|
// | Readers 0 | Readers 0 |
|
|
// + ----------- + --------------------------- +
|
|
// 0 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "fill gap with ReadLock and split",
|
|
// + --------- + ---------------------------- +
|
|
// | gap | Readers 0 |
|
|
// + --------- + ---------------------------- +
|
|
// 0 1024 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 1,
|
|
lockType: ReadLock,
|
|
success: true,
|
|
// + --------- + ------------- + ------------- +
|
|
// | Reader 1 | Readers 0 & 1 | Reader 0 |
|
|
// + ----------+ ------------- + ------------- +
|
|
// 0 1024 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{1024, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "upgrade ReadLock to WriteLock for single uid fill gap",
|
|
// + ------------- + --------- + --- + ------------- +
|
|
// | Readers 0 & 1 | Readers 0 | gap | Readers 0 & 2 |
|
|
// + ------------- + --------- + --- + ------------- +
|
|
// 0 1024 2048 4096 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, 2048},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
start: 1024,
|
|
end: 4096,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: true,
|
|
// + ------------- + -------- + ------------- +
|
|
// | Readers 0 & 1 | Writer 0 | Readers 0 & 2 |
|
|
// + ------------- + -------- + ------------- +
|
|
// 0 1024 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{1024, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "upgrade ReadLock to WriteLock for single uid keep gap",
|
|
// + ------------- + --------- + --- + ------------- +
|
|
// | Readers 0 & 1 | Readers 0 | gap | Readers 0 & 2 |
|
|
// + ------------- + --------- + --- + ------------- +
|
|
// 0 1024 2048 4096 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, 2048},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
start: 1024,
|
|
end: 3072,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: true,
|
|
// + ------------- + -------- + --- + ------------- +
|
|
// | Readers 0 & 1 | Writer 0 | gap | Readers 0 & 2 |
|
|
// + ------------- + -------- + --- + ------------- +
|
|
// 0 1024 3072 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{1024, 3072},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "fail to upgrade ReadLock to WriteLock with conflicting Reader",
|
|
// + ------------- + --------- +
|
|
// | Readers 0 & 1 | Readers 0 |
|
|
// + ------------- + --------- +
|
|
// 0 1024 2048
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, 2048},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 2048,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: false,
|
|
},
|
|
{
|
|
name: "take WriteLock on whole file if all uids are the same",
|
|
// + ------------- + --------- + --------- + ---------- +
|
|
// | Writer 0 | Readers 0 | Readers 0 | Readers 0 |
|
|
// + ------------- + --------- + --------- + ---------- +
|
|
// 0 1024 2048 4096 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{1024, 2048},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{2048, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
lockType: WriteLock,
|
|
success: true,
|
|
// We do not manually merge locks. Semantically a fragmented lock
|
|
// held by the same uid will behave as one lock so it makes no difference.
|
|
//
|
|
// + ------------- + ---------------------------- +
|
|
// | Writer 0 | Writer 0 |
|
|
// + ------------- + ---------------------------- +
|
|
// 0 1024 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{1024, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
l := fill(test.before)
|
|
|
|
r := LockRange{Start: test.start, End: test.end}
|
|
success := l.lock(test.uid, test.lockType, r)
|
|
var got []entry
|
|
for seg := l.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
|
|
got = append(got, entry{
|
|
Lock: seg.Value(),
|
|
LockRange: seg.Range(),
|
|
})
|
|
}
|
|
|
|
if success != test.success {
|
|
t.Errorf("%s: setlock(%v, %+v, %d, %d) got success %v, want %v", test.name, test.before, r, test.uid, test.lockType, success, test.success)
|
|
continue
|
|
}
|
|
|
|
if success {
|
|
if !equals(got, test.after) {
|
|
t.Errorf("%s: got set %+v, want %+v", test.name, got, test.after)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUnlock(t *testing.T) {
|
|
tests := []struct {
|
|
// description of test.
|
|
name string
|
|
|
|
// LockSet entries to pre-fill.
|
|
before []entry
|
|
|
|
// Description of region to unlock:
|
|
//
|
|
// start is the file start of the lock.
|
|
start uint64
|
|
// end is the end file start of the lock.
|
|
end uint64
|
|
// uid of lock holder.
|
|
uid UniqueID
|
|
|
|
// Expected layout of the set after unlocking.
|
|
after []entry
|
|
}{
|
|
{
|
|
name: "unlock zero length on empty set",
|
|
start: 0,
|
|
end: 0,
|
|
uid: 0,
|
|
},
|
|
{
|
|
name: "unlock on empty set (no-op)",
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
},
|
|
{
|
|
name: "unlock uid not locked (no-op)",
|
|
// + --------------------------- +
|
|
// | Readers 1 & 2 |
|
|
// + --------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 1024,
|
|
end: 4096,
|
|
uid: 0,
|
|
// + --------------------------- +
|
|
// | Readers 1 & 2 |
|
|
// + --------------------------- +
|
|
// 0 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock ReadLock over entire file",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
},
|
|
{
|
|
name: "unlock WriteLock over entire file",
|
|
// + ----------------------------------------- +
|
|
// | Writer 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
},
|
|
{
|
|
name: "unlock partial ReadLock (start)",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 0,
|
|
// + ------ + --------------------------- +
|
|
// | gap | Readers 0 |
|
|
// +------- + --------------------------- +
|
|
// 0 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock partial WriteLock (start)",
|
|
// + ----------------------------------------- +
|
|
// | Writer 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: 4096,
|
|
uid: 0,
|
|
// + ------ + --------------------------- +
|
|
// | gap | Writer 0 |
|
|
// +------- + --------------------------- +
|
|
// 0 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock partial ReadLock (end)",
|
|
// + ----------------------------------------- +
|
|
// | Readers 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 4096,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
// + --------------------------- +
|
|
// | Readers 0 |
|
|
// +---------------------------- +
|
|
// 0 4096
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true}},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock partial WriteLock (end)",
|
|
// + ----------------------------------------- +
|
|
// | Writer 0 |
|
|
// + ----------------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 4096,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
// + --------------------------- +
|
|
// | Writer 0 |
|
|
// +---------------------------- +
|
|
// 0 4096
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 4096},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock for single uid",
|
|
// + ------------- + --------- + ------------------- +
|
|
// | Readers 0 & 1 | Writer 0 | Readers 0 & 1 & 2 |
|
|
// + ------------- + --------- + ------------------- +
|
|
// 0 1024 4096 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{1024, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
start: 0,
|
|
end: LockEOF,
|
|
uid: 0,
|
|
// + --------- + --- + --------------- +
|
|
// | Readers 1 | gap | Readers 1 & 2 |
|
|
// + --------- + --- + --------------- +
|
|
// 0 1024 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock subsection locked",
|
|
// + ------------------------------- +
|
|
// | Readers 0 & 1 & 2 |
|
|
// + ------------------------------- +
|
|
// 0 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
|
LockRange: LockRange{0, LockEOF},
|
|
},
|
|
},
|
|
start: 1024,
|
|
end: 4096,
|
|
uid: 0,
|
|
// + ----------------- + ------------- + ----------------- +
|
|
// | Readers 0 & 1 & 2 | Readers 1 & 2 | Readers 0 & 1 & 2 |
|
|
// + ----------------- + ------------- + ----------------- +
|
|
// 0 1024 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true, 2: true}},
|
|
LockRange: LockRange{1024, 4096},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true, 2: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock mid-gap to increase gap",
|
|
// + --------- + ----- + ------------------- +
|
|
// | Writer 0 | gap | Readers 0 & 1 |
|
|
// + --------- + ----- + ------------------- +
|
|
// 0 1024 4096 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
start: 8,
|
|
end: 2048,
|
|
uid: 0,
|
|
// + --------- + ----- + ------------------- +
|
|
// | Writer 0 | gap | Readers 0 & 1 |
|
|
// + --------- + ----- + ------------------- +
|
|
// 0 8 4096 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 8},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "unlock split region on uid mid-gap",
|
|
// + --------- + ----- + ------------------- +
|
|
// | Writer 0 | gap | Readers 0 & 1 |
|
|
// + --------- + ----- + ------------------- +
|
|
// 0 1024 4096 max uint64
|
|
before: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{4096, LockEOF},
|
|
},
|
|
},
|
|
start: 2048,
|
|
end: 8192,
|
|
uid: 0,
|
|
// + --------- + ----- + --------- + ------------- +
|
|
// | Writer 0 | gap | Readers 1 | Readers 0 & 1 |
|
|
// + --------- + ----- + --------- + ------------- +
|
|
// 0 1024 4096 8192 max uint64
|
|
after: []entry{
|
|
{
|
|
Lock: Lock{HasWriter: true, Writer: 0},
|
|
LockRange: LockRange{0, 1024},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{1: true}},
|
|
LockRange: LockRange{4096, 8192},
|
|
},
|
|
{
|
|
Lock: Lock{Readers: map[UniqueID]bool{0: true, 1: true}},
|
|
LockRange: LockRange{8192, LockEOF},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, test := range tests {
|
|
l := fill(test.before)
|
|
|
|
r := LockRange{Start: test.start, End: test.end}
|
|
l.unlock(test.uid, r)
|
|
var got []entry
|
|
for seg := l.FirstSegment(); seg.Ok(); seg = seg.NextSegment() {
|
|
got = append(got, entry{
|
|
Lock: seg.Value(),
|
|
LockRange: seg.Range(),
|
|
})
|
|
}
|
|
if !equals(got, test.after) {
|
|
t.Errorf("%s: got set %+v, want %+v", test.name, got, test.after)
|
|
}
|
|
}
|
|
}
|