platform: modularize api/gui, add docs-tests-web foundation, and refresh root config
This commit is contained in:
94
selective-vpn-api/app/transport_iface_orchestrator_lock.go
Normal file
94
selective-vpn-api/app/transport_iface_orchestrator_lock.go
Normal file
@@ -0,0 +1,94 @@
|
||||
package app
|
||||
|
||||
import (
|
||||
"sort"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type transportIfaceOrchestrator struct {
|
||||
mu sync.Mutex
|
||||
locks map[string]*sync.Mutex
|
||||
}
|
||||
|
||||
var transportIfaceRuntime = newTransportIfaceOrchestrator()
|
||||
|
||||
func newTransportIfaceOrchestrator() *transportIfaceOrchestrator {
|
||||
return &transportIfaceOrchestrator{
|
||||
locks: map[string]*sync.Mutex{},
|
||||
}
|
||||
}
|
||||
|
||||
func (o *transportIfaceOrchestrator) lockFor(ifaceID string) *sync.Mutex {
|
||||
key := normalizeTransportIfaceID(ifaceID)
|
||||
o.mu.Lock()
|
||||
defer o.mu.Unlock()
|
||||
if m, ok := o.locks[key]; ok {
|
||||
return m
|
||||
}
|
||||
m := &sync.Mutex{}
|
||||
o.locks[key] = m
|
||||
return m
|
||||
}
|
||||
|
||||
func withTransportIfaceLock(ifaceID string, fn func()) {
|
||||
m := transportIfaceRuntime.lockFor(ifaceID)
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
fn()
|
||||
}
|
||||
|
||||
func withTransportIfaceLocks(ifaceIDs []string, fn func()) {
|
||||
ids := normalizeTransportIfaceLockIDs(ifaceIDs)
|
||||
if len(ids) == 0 {
|
||||
fn()
|
||||
return
|
||||
}
|
||||
locks := make([]*sync.Mutex, 0, len(ids))
|
||||
for _, ifaceID := range ids {
|
||||
locks = append(locks, transportIfaceRuntime.lockFor(ifaceID))
|
||||
}
|
||||
for _, m := range locks {
|
||||
m.Lock()
|
||||
}
|
||||
defer func() {
|
||||
for i := len(locks) - 1; i >= 0; i-- {
|
||||
locks[i].Unlock()
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}
|
||||
|
||||
func normalizeTransportIfaceLockIDs(ifaceIDs []string) []string {
|
||||
if len(ifaceIDs) == 0 {
|
||||
return nil
|
||||
}
|
||||
seen := map[string]struct{}{}
|
||||
out := make([]string, 0, len(ifaceIDs))
|
||||
for _, raw := range ifaceIDs {
|
||||
id := normalizeTransportIfaceID(raw)
|
||||
if _, ok := seen[id]; ok {
|
||||
continue
|
||||
}
|
||||
seen[id] = struct{}{}
|
||||
out = append(out, id)
|
||||
}
|
||||
sort.Strings(out)
|
||||
return out
|
||||
}
|
||||
|
||||
func resolveTransportClientIfaceID(clientID string) (string, bool) {
|
||||
id := sanitizeID(clientID)
|
||||
if id == "" {
|
||||
return "", false
|
||||
}
|
||||
transportMu.Lock()
|
||||
st := loadTransportClientsState()
|
||||
idx := findTransportClientIndex(st.Items, id)
|
||||
if idx < 0 {
|
||||
transportMu.Unlock()
|
||||
return "", false
|
||||
}
|
||||
ifaceID := normalizeTransportIfaceID(st.Items[idx].IfaceID)
|
||||
transportMu.Unlock()
|
||||
return ifaceID, true
|
||||
}
|
||||
Reference in New Issue
Block a user