lurkcoin-core/lurkcoin/databases/register.go

126 lines
3.1 KiB
Go

//
// lurkcoin
// Copyright © 2020 by luk3yx
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//
package databases
import (
"fmt"
"github.com/luk3yx/lurkcoin-core/lurkcoin"
"sort"
"strings"
"sync"
)
type databaseFactory func(location string, options map[string]string) (lurkcoin.Database, error)
var databaseTypes = make(map[string]databaseFactory)
// Registers a new database type.
// WARNING: This function is not goroutine-safe and should probably only be
// called from init().
func RegisterDatabaseType(name string, f databaseFactory) {
databaseTypes[strings.ToLower(name)] = f
}
// Opens a database. The options parameter can be nil.
func OpenDatabase(dbType, location string, options map[string]string) (lurkcoin.Database, error) {
f, exists := databaseTypes[strings.ToLower(dbType)]
if exists {
return f(location, options)
}
return nil, fmt.Errorf("Unknown database type: %v.", dbType)
}
func GetSupportedDatabaseTypes() []string {
res := make([]string, 0, len(databaseTypes))
for dbType := range databaseTypes {
res = append(res, dbType)
}
sort.Strings(res)
return res
}
// Generic database lock
type genericDbLock struct {
lock *sync.Mutex
locks map[string]*sync.Mutex
}
// Locks servers and returns a list of homogenised server names.
// It would probably be more efficient to just use one larger lock.
func (self *genericDbLock) Lock(names []string) []string {
ids := make([]string, len(names))
for i, name := range names {
ids[i] = lurkcoin.HomogeniseUsername(name)
}
// Ensure none of the servers are locked.
self.lock.Lock()
ok := false
for !ok {
ok = true
for _, name := range ids {
cachedServerLock, exists := self.locks[name]
if !exists {
continue
}
ok = false
self.lock.Unlock()
cachedServerLock.Lock()
cachedServerLock.Unlock()
self.lock.Lock()
break
}
}
defer self.lock.Unlock()
// Create locks so the above code does not have to make use of polling.
for _, name := range ids {
var lock sync.Mutex
lock.Lock()
self.locks[name] = &lock
}
return ids
}
// Unlocks
func (self *genericDbLock) UnlockIDs(ids []string) {
self.lock.Lock()
defer self.lock.Unlock()
for _, id := range ids {
self.locks[id].Unlock()
delete(self.locks, id)
}
}
func (self *genericDbLock) Unlock(servers []*lurkcoin.Server) {
self.lock.Lock()
defer self.lock.Unlock()
for _, server := range servers {
self.locks[server.UID].Unlock()
delete(self.locks, server.UID)
}
}
func newGenericDbLock() genericDbLock {
return genericDbLock{new(sync.Mutex), make(map[string]*sync.Mutex)}
}