@@ -19,7 +19,6 @@ import ( | |||
"regexp" | |||
"sort" | |||
"strings" | |||
"sync" | |||
"time" | |||
"github.com/Unknwon/cae/zip" | |||
@@ -37,12 +36,15 @@ import ( | |||
"github.com/gogits/gogs/modules/markdown" | |||
"github.com/gogits/gogs/modules/process" | |||
"github.com/gogits/gogs/modules/setting" | |||
"github.com/gogits/gogs/modules/sync" | |||
) | |||
const ( | |||
_TPL_UPDATE_HOOK = "#!/usr/bin/env %s\n%s update $1 $2 $3 --config='%s'\n" | |||
) | |||
var repoWorkingPool = sync.NewSingleInstancePool() | |||
var ( | |||
ErrRepoFileNotExist = errors.New("Repository file does not exist") | |||
ErrRepoFileNotLoaded = errors.New("Repository file not loaded") | |||
@@ -1706,40 +1708,8 @@ func RewriteRepositoryUpdateHook() error { | |||
}) | |||
} | |||
// statusPool represents a pool of status with true/false. | |||
type statusPool struct { | |||
lock sync.RWMutex | |||
pool map[string]bool | |||
} | |||
// Start sets value of given name to true in the pool. | |||
func (p *statusPool) Start(name string) { | |||
p.lock.Lock() | |||
defer p.lock.Unlock() | |||
p.pool[name] = true | |||
} | |||
// Stop sets value of given name to false in the pool. | |||
func (p *statusPool) Stop(name string) { | |||
p.lock.Lock() | |||
defer p.lock.Unlock() | |||
p.pool[name] = false | |||
} | |||
// IsRunning checks if value of given name is set to true in the pool. | |||
func (p *statusPool) IsRunning(name string) bool { | |||
p.lock.RLock() | |||
defer p.lock.RUnlock() | |||
return p.pool[name] | |||
} | |||
// Prevent duplicate running tasks. | |||
var taskStatusPool = &statusPool{ | |||
pool: make(map[string]bool), | |||
} | |||
var taskStatusTable = sync.NewStatusTable() | |||
const ( | |||
_MIRROR_UPDATE = "mirror_update" | |||
@@ -1749,11 +1719,11 @@ const ( | |||
// MirrorUpdate checks and updates mirror repositories. | |||
func MirrorUpdate() { | |||
if taskStatusPool.IsRunning(_MIRROR_UPDATE) { | |||
if taskStatusTable.IsRunning(_MIRROR_UPDATE) { | |||
return | |||
} | |||
taskStatusPool.Start(_MIRROR_UPDATE) | |||
defer taskStatusPool.Stop(_MIRROR_UPDATE) | |||
taskStatusTable.Start(_MIRROR_UPDATE) | |||
defer taskStatusTable.Stop(_MIRROR_UPDATE) | |||
log.Trace("Doing: MirrorUpdate") | |||
@@ -1813,11 +1783,11 @@ func MirrorUpdate() { | |||
// GitFsck calls 'git fsck' to check repository health. | |||
func GitFsck() { | |||
if taskStatusPool.IsRunning(_GIT_FSCK) { | |||
if taskStatusTable.IsRunning(_GIT_FSCK) { | |||
return | |||
} | |||
taskStatusPool.Start(_GIT_FSCK) | |||
defer taskStatusPool.Stop(_GIT_FSCK) | |||
taskStatusTable.Start(_GIT_FSCK) | |||
defer taskStatusTable.Stop(_GIT_FSCK) | |||
log.Trace("Doing: GitFsck") | |||
@@ -1879,11 +1849,11 @@ func repoStatsCheck(checker *repoChecker) { | |||
} | |||
func CheckRepoStats() { | |||
if taskStatusPool.IsRunning(_CHECK_REPOs) { | |||
if taskStatusTable.IsRunning(_CHECK_REPOs) { | |||
return | |||
} | |||
taskStatusPool.Start(_CHECK_REPOs) | |||
defer taskStatusPool.Stop(_CHECK_REPOs) | |||
taskStatusTable.Start(_CHECK_REPOs) | |||
defer taskStatusTable.Stop(_CHECK_REPOs) | |||
log.Trace("Doing: CheckRepoStats") | |||
@@ -2275,11 +2245,6 @@ func (repo *Repository) GetForks() ([]*Repository, error) { | |||
// /_______ /\____ | |__||__| \___ / |__|____/\___ > | |||
// \/ \/ \/ \/ | |||
var repoWorkingPool = &workingPool{ | |||
pool: make(map[string]*sync.Mutex), | |||
count: make(map[string]int), | |||
} | |||
func (repo *Repository) LocalRepoPath() string { | |||
return path.Join(setting.AppDataPath, "tmp/local-repo", com.ToStr(repo.ID)) | |||
} |
@@ -12,19 +12,16 @@ import ( | |||
"path" | |||
"path/filepath" | |||
"strings" | |||
"sync" | |||
"github.com/Unknwon/com" | |||
"github.com/gogits/git-module" | |||
"github.com/gogits/gogs/modules/setting" | |||
"github.com/gogits/gogs/modules/sync" | |||
) | |||
var wikiWorkingPool = &workingPool{ | |||
pool: make(map[string]*sync.Mutex), | |||
count: make(map[string]int), | |||
} | |||
var wikiWorkingPool = sync.NewSingleInstancePool() | |||
// ToWikiPageURL formats a string to corresponding wiki URL name. | |||
func ToWikiPageURL(name string) string { |
@@ -1,47 +0,0 @@ | |||
// Copyright 2015 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package models | |||
import ( | |||
"sync" | |||
) | |||
// workingPool represents a pool of working status which makes sure | |||
// that only one instance of same task is performing at a time. | |||
// However, different type of tasks can performing at the same time. | |||
type workingPool struct { | |||
lock sync.Mutex | |||
pool map[string]*sync.Mutex | |||
count map[string]int | |||
} | |||
// CheckIn checks in a task and waits if others are running. | |||
func (p *workingPool) CheckIn(name string) { | |||
p.lock.Lock() | |||
lock, has := p.pool[name] | |||
if !has { | |||
lock = &sync.Mutex{} | |||
p.pool[name] = lock | |||
} | |||
p.count[name]++ | |||
p.lock.Unlock() | |||
lock.Lock() | |||
} | |||
// CheckOut checks out a task to let other tasks run. | |||
func (p *workingPool) CheckOut(name string) { | |||
p.lock.Lock() | |||
defer p.lock.Unlock() | |||
p.pool[name].Unlock() | |||
if p.count[name] == 1 { | |||
delete(p.pool, name) | |||
delete(p.count, name) | |||
} else { | |||
p.count[name]-- | |||
} | |||
} |
@@ -0,0 +1,67 @@ | |||
// Copyright 2016 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package sync | |||
import ( | |||
"sync" | |||
) | |||
// SingleInstancePool is a pool of non-identical instances | |||
// that only one instance with same identity is in the pool at a time. | |||
// In other words, only instances with different identities can exist | |||
// at the same time. | |||
// | |||
// This pool is particularly useful for performing tasks on same resource | |||
// on the file system in different goroutines. | |||
type SingleInstancePool struct { | |||
lock sync.Mutex | |||
// pool maintains locks for each instance in the pool. | |||
pool map[string]*sync.Mutex | |||
// count maintains the number of times an instance with same identity checks in | |||
// to the pool, and should be reduced to 0 (removed from map) by checking out | |||
// with same number of times. | |||
count map[string]int | |||
} | |||
// NewSingleInstancePool initializes and returns a new SingleInstancePool object. | |||
func NewSingleInstancePool() *SingleInstancePool { | |||
return &SingleInstancePool{ | |||
pool: make(map[string]*sync.Mutex), | |||
count: make(map[string]int), | |||
} | |||
} | |||
// CheckIn checks in an instance to the pool and hangs while instance | |||
// with same indentity is using the lock. | |||
func (p *SingleInstancePool) CheckIn(identity string) { | |||
p.lock.Lock() | |||
lock, has := p.pool[identity] | |||
if !has { | |||
lock = &sync.Mutex{} | |||
p.pool[identity] = lock | |||
} | |||
p.count[identity]++ | |||
p.lock.Unlock() | |||
lock.Lock() | |||
} | |||
// CheckOut checks out an instance from the pool and releases the lock | |||
// to let other instances with same identity to grab the lock. | |||
func (p *SingleInstancePool) CheckOut(identity string) { | |||
p.lock.Lock() | |||
defer p.lock.Unlock() | |||
p.pool[identity].Unlock() | |||
if p.count[identity] == 1 { | |||
delete(p.pool, identity) | |||
delete(p.count, identity) | |||
} else { | |||
p.count[identity]-- | |||
} | |||
} |
@@ -0,0 +1,49 @@ | |||
// Copyright 2016 The Gogs Authors. All rights reserved. | |||
// Use of this source code is governed by a MIT-style | |||
// license that can be found in the LICENSE file. | |||
package sync | |||
import ( | |||
"sync" | |||
) | |||
// StatusTable is a table maintains true/false values. | |||
// | |||
// This table is particularly useful for un/marking and checking values | |||
// in different goroutines. | |||
type StatusTable struct { | |||
lock sync.RWMutex | |||
pool map[string]bool | |||
} | |||
// NewStatusTable initializes and returns a new StatusTable object. | |||
func NewStatusTable() *StatusTable { | |||
return &StatusTable{ | |||
pool: make(map[string]bool), | |||
} | |||
} | |||
// Start sets value of given name to true in the pool. | |||
func (p *StatusTable) Start(name string) { | |||
p.lock.Lock() | |||
defer p.lock.Unlock() | |||
p.pool[name] = true | |||
} | |||
// Stop sets value of given name to false in the pool. | |||
func (p *StatusTable) Stop(name string) { | |||
p.lock.Lock() | |||
defer p.lock.Unlock() | |||
p.pool[name] = false | |||
} | |||
// IsRunning checks if value of given name is set to true in the pool. | |||
func (p *StatusTable) IsRunning(name string) bool { | |||
p.lock.RLock() | |||
defer p.lock.RUnlock() | |||
return p.pool[name] | |||
} |