| @@ -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] | |||
| } | |||