Refactored the way freelists work.

geesefs-0-30-9
Aaron Jacobs 2015-07-28 15:53:53 +10:00
commit 2d3078f8b9
3 changed files with 86 additions and 37 deletions

View File

@ -27,6 +27,7 @@ import (
"golang.org/x/net/context"
"github.com/jacobsa/fuse/internal/buffer"
"github.com/jacobsa/fuse/internal/freelist"
"github.com/jacobsa/fuse/internal/fusekernel"
)
@ -71,18 +72,14 @@ type Connection struct {
mu sync.Mutex
// A freelist of InMessage structs, the allocation of which can be a hot spot
// for CPU usage. Each element is in an undefined state, and must be
// re-initialized.
//
// GUARDED_BY(mu)
messageFreelist []*buffer.InMessage
// A map from fuse "unique" request ID (*not* the op ID for logging used
// above) to a function that cancel's its associated context.
//
// GUARDED_BY(mu)
cancelFuncs map[uint64]func()
// Freelists, serviced by freelists.go.
inMessages freelist.Freelist // GUARDED_BY(mu)
}
// State that is maintained for each in-flight op. This is stuffed into the
@ -302,38 +299,11 @@ func (c *Connection) handleInterrupt(fuseID uint64) {
cancel()
}
// m.Init must be called.
func (c *Connection) allocateInMessage() (m *buffer.InMessage) {
c.mu.Lock()
defer c.mu.Unlock()
// Can we pull from the freelist?
l := len(c.messageFreelist)
if l != 0 {
m = c.messageFreelist[l-1]
c.messageFreelist = c.messageFreelist[:l-1]
return
}
// Otherwise, allocate a new one.
m = new(buffer.InMessage)
return
}
func (c *Connection) destroyInMessage(m *buffer.InMessage) {
c.mu.Lock()
defer c.mu.Unlock()
// Stick it on the freelist.
c.messageFreelist = append(c.messageFreelist, m)
}
// Read the next message from the kernel. The message must later be destroyed
// using destroyInMessage.
func (c *Connection) readMessage() (m *buffer.InMessage, err error) {
// Allocate a message.
m = c.allocateInMessage()
m = c.getInMessage()
// Loop past transient errors.
for {
@ -359,7 +329,7 @@ func (c *Connection) readMessage() (m *buffer.InMessage, err error) {
}
if err != nil {
c.destroyInMessage(m)
c.putInMessage(m)
m = nil
return
}
@ -459,7 +429,7 @@ func (c *Connection) Reply(ctx context.Context, opErr error) {
opID := state.opID
// Make sure we destroy the message when we're done.
defer c.destroyInMessage(m)
defer c.putInMessage(m)
// Clean up state for this op.
c.finishOp(m.Header().Opcode, m.Header().Unique)

39
freelists.go Normal file
View File

@ -0,0 +1,39 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 fuse
import (
"unsafe"
"github.com/jacobsa/fuse/internal/buffer"
)
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) getInMessage() (m *buffer.InMessage) {
c.mu.Lock()
m = (*buffer.InMessage)(c.inMessages.Get())
if m == nil {
m = new(buffer.InMessage)
}
c.mu.Unlock()
return
}
// LOCKS_EXCLUDED(c.mu)
func (c *Connection) putInMessage(x *buffer.InMessage) {
c.mu.Lock()
c.inMessages.Put(unsafe.Pointer(x))
c.mu.Unlock()
}

View File

@ -0,0 +1,40 @@
// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 freelist
import "unsafe"
// A freelist for arbitrary pointers. Not safe for concurrent access.
type Freelist struct {
list []unsafe.Pointer
}
// Get an element from the freelist, returning nil if empty.
func (fl *Freelist) Get() (p unsafe.Pointer) {
l := len(fl.list)
if l == 0 {
return
}
p = fl.list[l-1]
fl.list = fl.list[:l-1]
return
}
// Contribute an element back to the freelist.
func (fl *Freelist) Put(p unsafe.Pointer) {
fl.list = append(fl.list, p)
}