blob: d897d61e63912f3de5f87a91434ce4ec104c5a6c [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package node
import (
"sync"
"time"
"thinfs/fs"
)
// Dcache destribes a cache of directories
type Dcache struct {
sync.Mutex
entries map[uint32]*dcacheEntry // Map of "Directory ID" --> Directory node
}
// TODO(smklein): By sharding based on keys, this can become much more finely-grained.
// dcacheEntry represents the "value" in the key / value dcache structure
type dcacheEntry struct {
n DirectoryNode
refs int
}
// Init initializes dcache structure
func (d *Dcache) Init() {
d.entries = make(map[uint32]*dcacheEntry)
}
// AllEntries returns all nodes in the dcache
//
// NOT thread-safe
func (d *Dcache) AllEntries() []DirectoryNode {
var nodes []DirectoryNode
for k := range d.entries {
nodes = append(nodes, d.entries[k].n)
}
return nodes
}
// CreateOrAcquire either
// (1) Creates a new node and places it in the dcache (ref = 1), or
// (2) Acquires a node which is already in the dcache (ref++)
// Use this function when access is needed to a Directory which may or may not be open.
//
// Thread-safe
func (d *Dcache) CreateOrAcquire(m *Metadata, id uint32, mtime time.Time) (DirectoryNode, error) {
d.Lock()
defer d.Unlock()
newNode := d.acquire(id)
if newNode == nil {
// Add the directory to the dcache if it's not there already
var err error
newNode, err = NewDirectory(m, id, mtime)
if err != nil {
return nil, err
}
d.insert(id, newNode)
}
return newNode, nil
}
// Lookup (and acquire) the directory node, if it exists
//
// Thread-safe
func (d *Dcache) Lookup(id uint32) (DirectoryNode, error) {
d.Lock()
defer d.Unlock()
node := d.acquire(id)
if node == nil {
return nil, fs.ErrNotFound
}
return node, nil
}
// Insert adds a node to the dcache
// Precondition: Key does NOT exist in dcache
//
// Thread-safe
func (d *Dcache) Insert(id uint32, n DirectoryNode) {
d.Lock()
d.insert(id, n)
d.Unlock()
}
// Acquire increments the number of references to a node in the dcache
// Precondition: Key exists in dcache
//
// Thread-safe
func (d *Dcache) Acquire(id uint32) {
d.Lock()
if n := d.acquire(id); n == nil {
panic("Node did not exist in dcache")
}
d.Unlock()
}
// Release decreases a reference to a directory in the dcache
// Precondition: Key exists in dcache
//
// Thread-safe
func (d *Dcache) Release(id uint32) {
d.Lock()
defer d.Unlock()
entry, ok := d.entries[id]
if !ok {
panic("Releasing node which does not exist in dcache")
}
entry.refs--
if entry.refs < 0 {
panic("Invalid refcounting")
} else if entry.refs == 0 {
delete(d.entries, id)
}
}
// Transfer moves count references from one ID to another.
func (d *Dcache) Transfer(src, dst uint32, count int) {
d.Lock()
defer d.Unlock()
srcEntry, srcOk := d.entries[src]
dstEntry, dstOk := d.entries[dst]
if !srcOk || !dstOk {
panic("Invalid src/dst; cannot transfer")
}
srcEntry.refs -= count
if srcEntry.refs < 0 {
panic("Invalid refcounting")
} else if srcEntry.refs == 0 {
delete(d.entries, src)
}
dstEntry.refs += count
}
func (d *Dcache) acquire(id uint32) DirectoryNode {
entry, ok := d.entries[id]
if ok {
entry.refs++
return entry.n
}
return nil
}
func (d *Dcache) insert(id uint32, n DirectoryNode) {
if _, ok := d.entries[id]; ok {
panic("Directory already exists in dcache")
}
d.entries[id] = &dcacheEntry{
n: n,
refs: 1,
}
}