Refactored the way freelists work.
commit
2d3078f8b9
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
|
@ -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)
|
||||
}
|
Loading…
Reference in New Issue