From 660e7a178a9d72a03d13f704aca61726b4672232 Mon Sep 17 00:00:00 2001 From: Unknwon Date: Sun, 14 Aug 2016 17:44:20 -0700 Subject: [PATCH] modules/sync: move sync objects to independent module --- models/repo.go | 61 ++++++------------------- models/wiki.go | 7 +-- models/working_pool.go | 47 ------------------- modules/sync/single_instance_pool.go | 67 ++++++++++++++++++++++++++++ modules/sync/status_pool.go | 49 ++++++++++++++++++++ 5 files changed, 131 insertions(+), 100 deletions(-) delete mode 100644 models/working_pool.go create mode 100644 modules/sync/single_instance_pool.go create mode 100644 modules/sync/status_pool.go diff --git a/models/repo.go b/models/repo.go index 9e20b9fb2..22d211583 100644 --- a/models/repo.go +++ b/models/repo.go @@ -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)) } diff --git a/models/wiki.go b/models/wiki.go index 89e9fdaa3..7a286f4ba 100644 --- a/models/wiki.go +++ b/models/wiki.go @@ -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 { diff --git a/models/working_pool.go b/models/working_pool.go deleted file mode 100644 index c522de56a..000000000 --- a/models/working_pool.go +++ /dev/null @@ -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]-- - } -} diff --git a/modules/sync/single_instance_pool.go b/modules/sync/single_instance_pool.go new file mode 100644 index 000000000..4a00d5283 --- /dev/null +++ b/modules/sync/single_instance_pool.go @@ -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]-- + } +} diff --git a/modules/sync/status_pool.go b/modules/sync/status_pool.go new file mode 100644 index 000000000..f6a7f9495 --- /dev/null +++ b/modules/sync/status_pool.go @@ -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] +}