Commit a3b1fcbf authored by Han-Wen Nienhuys's avatar Han-Wen Nienhuys

Add HandleMap and Handled for generic and checked int64 handle

conversion.
parent 0d29657a
......@@ -9,7 +9,8 @@ MANUAL_GOFILES=api.go \
default.go \
direntry.go\
files.go \
fuse.go\
fuse.go \
handle.go \
latencymap.go \
lockingfs.go \
loggingfs.go \
......
package fuse
import (
"fmt"
"unsafe"
"sync"
)
// HandleMap translates objects in Go space to 64-bit handles that can
// be given out to -say- the linux kernel. It uses the free bits
// (16+3 on x64_64 and 32 on x86) to do an extra sanity check on the
// data. (Thanks to Russ Cox for this suggestion). In addition, it
// stores the object in a map, so the Go runtime will not garbage
// collect it.
//
// To use it, include Handled as first member of the structure
// you wish to export.
//
// This structure is thread-safe.
type HandleMap struct {
mutex sync.Mutex
handles map[uint64]*Handled
nextFree uint32
}
func (me *HandleMap) verify() {
if !paranoia {
return
}
me.mutex.Lock()
defer me.mutex.Unlock()
for k, v := range me.handles {
if DecodeHandle(k) != v {
panic("handle map out of sync")
}
}
}
func NewHandleMap() *HandleMap {
return &HandleMap{
handles: make(map[uint64]*Handled),
nextFree: 1, // to make tests easier.
}
}
type Handled struct {
check uint32
}
func (me *HandleMap) Count() int {
me.mutex.Lock()
defer me.mutex.Unlock()
return len(me.handles)
}
func (me *HandleMap) Register(obj *Handled) (handle uint64) {
if obj.check != 0 {
panic("Object already has a handle.")
}
me.mutex.Lock()
defer me.mutex.Unlock()
handle = uint64(uintptr(unsafe.Pointer(obj)))
check := me.nextFree
me.nextFree++
if unsafe.Sizeof(obj) == 8 {
me.nextFree = me.nextFree & (1 << (64 - 48 + 3) -1)
rest := (handle &^ (1<<48 - 1)) | (handle & (1<<3 -1))
if rest != 0 {
panic("unaligned ptr or more than 48 bits in address")
}
handle >>= 3
handle |= uint64(obj.check) << (64 - 48 + 3)
}
if unsafe.Sizeof(obj) == 4 {
rest := (handle & 0x3)
if rest != 0 {
panic("unaligned ptr")
}
handle |= uint64(check) << 32
}
obj.check = check
me.handles[handle] = obj
return handle
}
func (me *HandleMap) Forget(handle uint64) (val *Handled) {
val = DecodeHandle(handle)
me.mutex.Lock()
defer me.mutex.Unlock()
val.check = 0
me.handles[handle] = nil, false
return val
}
func DecodeHandle(handle uint64) (val *Handled) {
var check uint32
if unsafe.Sizeof(val) == 8 {
ptrBits := uintptr(handle & (1<<45-1))
check = uint32(handle >> 45)
val = (*Handled)(unsafe.Pointer(ptrBits<<3))
}
if unsafe.Sizeof(val) == 4 {
check = uint32(handle >> 32)
val = (*Handled)(unsafe.Pointer(uintptr(handle & ((1<<32)-1))))
}
if val.check != check {
msg := fmt.Sprintf("handle check mismatch; handle has 0x%x, object has 0x%x",
check, val.check)
panic(msg)
}
return val
}
package fuse
import (
"log"
"strings"
"testing"
"unsafe"
)
func markSeen(substr string) {
if r := recover(); r != nil {
s := r.(string)
if strings.Contains(s, substr) {
log.Println("expected recovery from: ", r)
} else {
panic(s)
}
}
}
func TestHandleMapDoubleRegister(t *testing.T) {
log.Println("TestDoubleRegister")
defer markSeen("already has a handle")
hm := NewHandleMap()
hm.Register(&Handled{})
v := &Handled{}
hm.Register(v)
hm.Register(v)
t.Error("Double register did not panic")
}
func TestHandleMapUnaligned(t *testing.T) {
hm := NewHandleMap()
b := make([]byte, 100)
v := (*Handled)(unsafe.Pointer(&b[1]))
defer markSeen("unaligned")
hm.Register(v)
t.Error("Unaligned register did not panic")
}
func TestHandleMapPointerLayout(t *testing.T) {
if unsafe.Sizeof(t) == 4 {
return
}
hm := NewHandleMap()
bogus := uint64(1) << uint32((8*(unsafe.Sizeof(t) - 1)))
p := uintptr(bogus)
v := (*Handled)(unsafe.Pointer(p))
defer markSeen("48")
hm.Register(v)
t.Error("bogus register did not panic")
}
func TestHandleMapBasic(t *testing.T) {
v := new(Handled)
hm := NewHandleMap()
h := hm.Register(v)
log.Printf("Got handle 0x%x", h)
if DecodeHandle(h) != v {
t.Fatal("address mismatch")
}
if hm.Count() != 1 {
t.Fatal("count error")
}
hm.Forget(h)
if hm.Count() != 0 {
t.Fatal("count error")
}
}
func TestHandleMapMultiple(t *testing.T) {
hm := NewHandleMap()
for i := 0; i < 10; i++ {
v := &Handled{}
h := hm.Register(v)
if DecodeHandle(h) != v {
t.Fatal("address mismatch")
}
if hm.Count() != i + 1 {
t.Fatal("count error")
}
}
}
func TestHandleMapCheckFail(t *testing.T) {
defer markSeen("check mismatch")
v := new(Handled)
hm := NewHandleMap()
h := hm.Register(v)
DecodeHandle(h | (uint64(1)<<63))
t.Error("Borked decode did not panic")
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment