minio/lock-rpc-server.go

146 lines
3.8 KiB
Go
Raw Normal View History

/*
* Minio Cloud Storage, (C) 2016 Minio, Inc.
*
* 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 main
import (
"fmt"
"net/rpc"
"path"
"strings"
"sync"
router "github.com/gorilla/mux"
)
const lockRPCPath = "/minio/lock"
type lockServer struct {
rpcPath string
mutex sync.Mutex
// e.g, when a Lock(name) is held, map[string][]bool{"name" : []bool{true}}
// when one or more RLock() is held, map[string][]bool{"name" : []bool{false, false}}
lockMap map[string][]bool
}
/// Distributed lock handlers
// LockHandler - rpc handler for lock operation.
func (l *lockServer) Lock(name *string, reply *bool) error {
l.mutex.Lock()
defer l.mutex.Unlock()
_, ok := l.lockMap[*name]
if !ok {
*reply = true
l.lockMap[*name] = []bool{true}
return nil
}
*reply = false
return nil
}
// UnlockHandler - rpc handler for unlock operation.
func (l *lockServer) Unlock(name *string, reply *bool) error {
l.mutex.Lock()
defer l.mutex.Unlock()
_, ok := l.lockMap[*name]
if !ok {
return fmt.Errorf("Unlock attempted on an un-locked entity: %s", *name)
}
*reply = true
delete(l.lockMap, *name)
return nil
}
func (l *lockServer) RLock(name *string, reply *bool) error {
l.mutex.Lock()
defer l.mutex.Unlock()
locksHeld, ok := l.lockMap[*name]
if !ok {
// First read-lock to be held on *name.
l.lockMap[*name] = []bool{false}
} else {
// Add an entry for this read lock.
l.lockMap[*name] = append(locksHeld, false)
}
return nil
}
func (l *lockServer) RUnlock(name *string, reply *bool) error {
l.mutex.Lock()
defer l.mutex.Unlock()
locksHeld, ok := l.lockMap[*name]
if !ok {
return fmt.Errorf("RUnlock attempted on an un-locked entity: %s", *name)
}
if len(locksHeld) > 1 {
// Remove one of the read locks held.
locksHeld = locksHeld[1:]
l.lockMap[*name] = locksHeld
} else {
// Delete the map entry since this is the last read lock held
// on *name.
delete(l.lockMap, *name)
}
return nil
}
// Initialize distributed lock.
func initDistributedNSLock(mux *router.Router, serverConfig serverCmdConfig) {
lockServers := newLockServers(serverConfig)
registerStorageLockers(mux, lockServers)
}
// Create one lock server for every local storage rpc server.
func newLockServers(serverConfig serverCmdConfig) (lockServers []*lockServer) {
// Initialize posix storage API.
exports := serverConfig.disks
ignoredExports := serverConfig.ignoredDisks
// Save ignored disks in a map
skipDisks := make(map[string]bool)
for _, ignoredExport := range ignoredExports {
skipDisks[ignoredExport] = true
}
for _, export := range exports {
if skipDisks[export] {
continue
}
if isLocalStorage(export) {
if idx := strings.LastIndex(export, ":"); idx != -1 {
export = export[idx+1:]
}
lockServers = append(lockServers, &lockServer{
rpcPath: export,
mutex: sync.Mutex{},
lockMap: make(map[string][]bool),
})
}
}
return lockServers
}
// registerStorageLockers - register locker rpc handlers for net/rpc library clients
func registerStorageLockers(mux *router.Router, lockServers []*lockServer) {
for _, lockServer := range lockServers {
lockRPCServer := rpc.NewServer()
lockRPCServer.RegisterName("Dsync", lockServer)
lockRouter := mux.PathPrefix(reservedBucket).Subrouter()
lockRouter.Path(path.Join("/lock", lockServer.rpcPath)).Handler(lockRPCServer)
}
}