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

Remove 32-bit and 64-bit handlemaps.

The code confuses the race-detector, and the work-arounds to fool the
race-detector fools humans. No conclusive benchmark data supports
the usefulness of the handle maps.
parent f3c93d58
...@@ -170,9 +170,6 @@ type Options struct { ...@@ -170,9 +170,6 @@ type Options struct {
// uid/gid. // uid/gid.
*fuse.Owner *fuse.Owner
// If set, use a more portable, but slower inode number // This option exists for compatibility and is ignored.
// generation scheme. This will make inode numbers (exported
// back to callers) stay within int32, which is necessary for
// making stat() succeed in 32-bit programs.
PortableInodes bool PortableInodes bool
} }
...@@ -56,7 +56,7 @@ func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) { ...@@ -56,7 +56,7 @@ func NewFileSystemConnector(root Node, opts *Options) (c *FileSystemConnector) {
if opts == nil { if opts == nil {
opts = NewOptions() opts = NewOptions()
} }
c.inodeMap = newHandleMap(opts.PortableInodes) c.inodeMap = newPortableHandleMap()
c.rootNode = newInode(true, root) c.rootNode = newInode(true, root)
// Make sure we don't reuse generation numbers. // Make sure we don't reuse generation numbers.
......
package nodefs package nodefs
import ( import (
"fmt"
"log" "log"
"sync" "sync"
"unsafe"
) )
// HandleMap translates objects in Go space to 64-bit handles that can // HandleMap translates objects in Go space to 64-bit handles that can
...@@ -132,220 +130,3 @@ func (m *portableHandleMap) Has(h uint64) bool { ...@@ -132,220 +130,3 @@ func (m *portableHandleMap) Has(h uint64) bool {
m.RUnlock() m.RUnlock()
return ok return ok
} }
// 32 bits version of HandleMap
type int32HandleMap struct {
mutex sync.Mutex
handles map[uint32]*handled
}
func (m *int32HandleMap) Register(obj *handled) (handle uint64) {
m.mutex.Lock()
h := uint32(uintptr(unsafe.Pointer(obj)))
if obj.count == 0 {
m.handles[h] = obj
obj.handle = uint64(h)
}
handle = uint64(h)
obj.count++
m.mutex.Unlock()
return uint64(handle)
}
func (m *int32HandleMap) Has(h uint64) bool {
m.mutex.Lock()
ok := m.handles[uint32(h)] != nil
m.mutex.Unlock()
return ok
}
func (m *int32HandleMap) Handle(obj *handled) uint64 {
if obj.count == 0 {
return 0
}
h := uint32(uintptr(unsafe.Pointer(obj)))
return uint64(h)
}
func (m *int32HandleMap) Count() int {
m.mutex.Lock()
c := len(m.handles)
m.mutex.Unlock()
return c
}
func (m *int32HandleMap) Forget(handle uint64, count int) (forgotten bool, obj *handled) {
obj = m.Decode(handle)
m.mutex.Lock()
obj.count -= count
if obj.count == 0 {
obj.check = 0
delete(m.handles, uint32(handle))
forgotten = true
} else if obj.count < 0 {
log.Panicf("underflow: handle %d count %d, obj %d", handle, count, obj.count)
}
obj.handle = 0
m.mutex.Unlock()
return forgotten, obj
}
func (m *int32HandleMap) Decode(handle uint64) *handled {
val := (*handled)(unsafe.Pointer(uintptr(handle & ((1 << 32) - 1))))
return val
}
func newInt32HandleMap() *int32HandleMap {
return &int32HandleMap{
handles: make(map[uint32]*handled),
}
}
// 64 bits version of HandleMap. It uses the free bits on x64_64
// (16+3) 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.
type int64HandleMap struct {
mutex sync.Mutex
handles map[uint64]*handled
nextFree uint32
}
func (m *int64HandleMap) verify() {
if !paranoia {
return
}
m.mutex.Lock()
defer m.mutex.Unlock()
for k, v := range m.handles {
if m.Decode(k) != v {
panic("handle map out of sync")
}
}
}
func newInt64HandleMap() *int64HandleMap {
return &int64HandleMap{
handles: make(map[uint64]*handled),
nextFree: 1, // to make tests easier.
}
}
// NewHandleMap creates a new HandleMap. If verify is given, we
// use remaining bits in the handle to store sanity check bits.
func newHandleMap(portable bool) (hm handleMap) {
if portable {
return newPortableHandleMap()
}
var obj *handled
switch unsafe.Sizeof(obj) {
case 8:
return newInt64HandleMap()
case 4:
return newInt32HandleMap()
default:
log.Fatalf("Unknown size.")
}
return nil
}
func (m *int64HandleMap) Count() int {
m.mutex.Lock()
c := len(m.handles)
m.mutex.Unlock()
return c
}
func (m *int64HandleMap) Register(obj *handled) (handle uint64) {
defer m.verify()
m.mutex.Lock()
if obj.count == 0 {
handle = uint64(uintptr(unsafe.Pointer(obj)))
rest := (handle &^ (1<<48 - 1))
if rest != 0 {
panic("more than 48 bits in address")
}
if handle&0x7 != 0 {
panic("unaligned ptr")
}
handle >>= 3
check := m.nextFree
m.nextFree++
m.nextFree = m.nextFree & (1<<(64-48+3) - 1)
handle |= uint64(check) << (48 - 3)
if obj.check != 0 {
panic(_ALREADY_MSG)
}
obj.check = check
obj.handle = handle
m.handles[handle] = obj
} else {
handle = m.handle(obj)
}
obj.count++
m.mutex.Unlock()
return handle
}
func (m *int64HandleMap) handle(obj *handled) (handle uint64) {
if obj.count == 0 {
return 0
}
handle = uint64(uintptr(unsafe.Pointer(obj)))
handle >>= 3
handle |= uint64(obj.check) << (48 - 3)
return handle
}
func (m *int64HandleMap) Handle(obj *handled) (handle uint64) {
m.mutex.Lock()
m.mutex.Unlock()
return m.handle(obj)
}
func (m *int64HandleMap) Forget(handle uint64, count int) (forgotten bool, obj *handled) {
defer m.verify()
obj = m.Decode(handle)
m.mutex.Lock()
obj.count -= count
if obj.count == 0 {
delete(m.handles, handle)
obj.check = 0
obj.handle = 0
forgotten = true
} else if obj.count < 0 {
log.Panicf("underflow: handle %d count %d, %d", handle, count, obj.count)
}
m.mutex.Unlock()
return forgotten, obj
}
func (m *int64HandleMap) Has(handle uint64) bool {
m.mutex.Lock()
ok := m.handles[handle] != nil
m.mutex.Unlock()
return ok
}
func (m *int64HandleMap) Decode(handle uint64) (val *handled) {
ptrBits := uintptr(handle & (1<<45 - 1))
check := uint32(handle >> 45)
val = (*handled)(unsafe.Pointer(ptrBits << 3))
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
}
...@@ -3,7 +3,6 @@ package nodefs ...@@ -3,7 +3,6 @@ package nodefs
import ( import (
"strings" "strings"
"testing" "testing"
"unsafe"
) )
func markSeen(t *testing.T, substr string) { func markSeen(t *testing.T, substr string) {
...@@ -17,26 +16,11 @@ func markSeen(t *testing.T, substr string) { ...@@ -17,26 +16,11 @@ func markSeen(t *testing.T, substr string) {
} }
} }
func TestHandleMapUnaligned(t *testing.T) {
if unsafe.Sizeof(t) < 8 {
t.Log("skipping test for 32 bits")
return
}
hm := newHandleMap(false)
b := make([]byte, 100)
v := (*handled)(unsafe.Pointer(&b[1]))
defer markSeen(t, "unaligned")
hm.Register(v)
t.Error("Unaligned register did not panic")
}
func TestHandleMapLookupCount(t *testing.T) { func TestHandleMapLookupCount(t *testing.T) {
for _, portable := range []bool{true, false} { for _, portable := range []bool{true, false} {
t.Log("portable:", portable) t.Log("portable:", portable)
v := new(handled) v := new(handled)
hm := newHandleMap(portable) hm := newPortableHandleMap()
h1 := hm.Register(v) h1 := hm.Register(v)
h2 := hm.Register(v) h2 := hm.Register(v)
...@@ -75,39 +59,36 @@ func TestHandleMapLookupCount(t *testing.T) { ...@@ -75,39 +59,36 @@ func TestHandleMapLookupCount(t *testing.T) {
} }
func TestHandleMapBasic(t *testing.T) { func TestHandleMapBasic(t *testing.T) {
for _, portable := range []bool{true, false} { v := new(handled)
t.Log("portable:", portable) hm := newPortableHandleMap()
v := new(handled) h := hm.Register(v)
hm := newHandleMap(portable) t.Logf("Got handle 0x%x", h)
h := hm.Register(v) if !hm.Has(h) {
t.Logf("Got handle 0x%x", h) t.Fatal("Does not have handle")
if !hm.Has(h) { }
t.Fatal("Does not have handle") if hm.Handle(v) != h {
} t.Fatalf("handle mismatch, got %x want %x", hm.Handle(v), h)
if hm.Handle(v) != h { }
t.Fatalf("handle mismatch, got %x want %x", hm.Handle(v), h) if hm.Decode(h) != v {
} t.Fatal("address mismatch")
if hm.Decode(h) != v { }
t.Fatal("address mismatch") if hm.Count() != 1 {
} t.Fatal("count error")
if hm.Count() != 1 { }
t.Fatal("count error") hm.Forget(h, 1)
} if hm.Count() != 0 {
hm.Forget(h, 1) t.Fatal("count error")
if hm.Count() != 0 { }
t.Fatal("count error") if hm.Has(h) {
} t.Fatal("Still has handle")
if hm.Has(h) { }
t.Fatal("Still has handle") if v.check != 0 {
} t.Errorf("forgotten object still has a check.")
if v.check != 0 {
t.Errorf("forgotten object still has a check.")
}
} }
} }
func TestHandleMapMultiple(t *testing.T) { func TestHandleMapMultiple(t *testing.T) {
hm := newHandleMap(false) hm := newPortableHandleMap()
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
v := &handled{} v := &handled{}
h := hm.Register(v) h := hm.Register(v)
...@@ -119,17 +100,3 @@ func TestHandleMapMultiple(t *testing.T) { ...@@ -119,17 +100,3 @@ func TestHandleMapMultiple(t *testing.T) {
} }
} }
} }
func TestHandleMapCheckFail(t *testing.T) {
if unsafe.Sizeof(t) < 8 {
t.Log("skipping test for 32 bits")
return
}
defer markSeen(t, "check mismatch")
v := new(handled)
hm := newHandleMap(false)
h := hm.Register(v)
hm.Decode(h | (uint64(1) << 63))
t.Error("Borked decode did not panic")
}
...@@ -178,7 +178,7 @@ func (n *Inode) rmChild(name string) (ch *Inode) { ...@@ -178,7 +178,7 @@ func (n *Inode) rmChild(name string) (ch *Inode) {
// Can only be called on untouched root inodes. // Can only be called on untouched root inodes.
func (n *Inode) mountFs(opts *Options) { func (n *Inode) mountFs(opts *Options) {
n.mountPoint = &fileSystemMount{ n.mountPoint = &fileSystemMount{
openFiles: newHandleMap(false), openFiles: newPortableHandleMap(),
mountInode: n, mountInode: n,
options: opts, options: opts,
} }
......
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