func (rl *inMemoryRepoLock) Unlock(repoName string) error {
	rl.mutex.Lock()
	defer rl.mutex.Unlock()
	locked, exists := rl.dataMap[repoName]
	if !exists {
		return fmt.Errorf("repository %q not found", repoName)
	}
	if locked {
		delete(rl.dataMap, repoName)
	}
	return nil
}
Unlock releases the lock for a repository or returns an error if the specified name doesn't
exist.
package sshd
import (
	"errors"
	"fmt"
	"sync"
	"time"
)
var (
	errAlreadyLocked = errors.New("already locked")
)
type RepositoryLock interface {
	Lock(repoName string) error
	Unlock(repoName string) error
	Timeout() time.Duration
}
func wrapInLock(lck RepositoryLock, repoName string, fn func() error) error {
	if err := lck.Lock(repoName); err != nil {
		return errAlreadyLocked
	}
	timer := time.NewTimer(lck.Timeout())
	defer timer.Stop()
	doneCh := make(chan struct{})
	fnCh := make(chan error)
	go func() {
		err := fn()
		select {
		case fnCh <- err:
		case <-doneCh:
		}
	}()
	defer lck.Unlock(repoName)
	select {
	case <-timer.C:
		defer close(doneCh)
		return fmt.Errorf("%s lock exceeded timeout", repoName)
	case err := <-fnCh:
		return err
	}
}
func NewInMemoryRepositoryLock(timeout time.Duration) RepositoryLock {
	return &inMemoryRepoLock{
		mutex:   &sync.RWMutex{},
		dataMap: make(map[string]bool),
		timeout: timeout,
	}
}
type inMemoryRepoLock struct {
	mutex   *sync.RWMutex
	dataMap map[string]bool
	timeout time.Duration
}
func (rl *inMemoryRepoLock) Lock(repoName string) error {
	rl.mutex.Lock()
	defer rl.mutex.Unlock()
	_, exists := rl.dataMap[repoName]
	if !exists {
		rl.dataMap[repoName] = true
		return nil
	}
	return fmt.Errorf("repository %q already locked", repoName)
}