From 3845b0f1246df8ead334792766419b46de379d2a Mon Sep 17 00:00:00 2001 From: Aaron Jacobs Date: Tue, 28 Jul 2015 12:59:44 +1000 Subject: [PATCH] Use freelist.Freelist for buffer.InMessage. --- connection.go | 44 +++++++------------------------------------- freelists.go | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 37 deletions(-) create mode 100644 freelists.go diff --git a/connection.go b/connection.go index 102a07d..2d83372 100644 --- a/connection.go +++ b/connection.go @@ -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) diff --git a/freelists.go b/freelists.go new file mode 100644 index 0000000..cae6eee --- /dev/null +++ b/freelists.go @@ -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() +}