Commit 879558b9 authored by Abiola Ibrahim's avatar Abiola Ibrahim

Git: code refactor. replace Sleep with Ticker

parent ee059c09
...@@ -87,7 +87,7 @@ No new changes.` ...@@ -87,7 +87,7 @@ No new changes.`
} }
// stop background thread monitor // stop background thread monitor
git.Monitor.StopAndWait(repo.URL, 1) git.Services.Stop(repo.URL, 1)
} }
......
...@@ -33,8 +33,9 @@ var initMutex = sync.Mutex{} ...@@ -33,8 +33,9 @@ var initMutex = sync.Mutex{}
// Logger is used to log errors; if nil, the default log.Logger is used. // Logger is used to log errors; if nil, the default log.Logger is used.
var Logger *log.Logger var Logger *log.Logger
// Monitor listens for halt signal to stop repositories from auto pulling. // Services holds all git pulling services and provides the function to
var Monitor = &monitor{} // stop them.
var Services = &services{}
// logger is an helper function to retrieve the available logger // logger is an helper function to retrieve the available logger
func logger() *log.Logger { func logger() *log.Logger {
......
...@@ -5,106 +5,81 @@ import ( ...@@ -5,106 +5,81 @@ import (
"time" "time"
) )
// RepoService is the repository service that runs in background and // repoService is the service that runs in background and periodically
// periodic pull from the repository. // pull from the repository.
type RepoService struct { type repoService struct {
repo *Repo repo *Repo
ticker *time.Ticker // ticker to tick at intervals
running bool // whether service is running. running bool // whether service is running.
halt chan struct{} // channel to notify service to halt and stop pulling. halt chan struct{} // channel to notify service to halt and stop pulling.
exit chan struct{} // channel to notify on exit.
} }
// Start starts a new RepoService in background and adds it to monitor. // Start starts a new background service to pull periodically.
func Start(repo *Repo) { func Start(repo *Repo) {
service := &RepoService{ service := &repoService{
repo, repo,
time.NewTicker(repo.Interval),
true, true,
make(chan struct{}), make(chan struct{}),
make(chan struct{}),
} }
go func(s *repoService) {
// start service
go func(s *RepoService) {
for { for {
// if service is halted select {
if !s.running { case <-s.ticker.C:
// notify exit channel
service.exit <- struct{}{}
break
}
time.Sleep(repo.Interval)
err := repo.Pull() err := repo.Pull()
if err != nil { if err != nil {
logger().Println(err) logger().Println(err)
} }
case <-s.halt:
s.ticker.Stop()
return
}
} }
}(service) }(service)
// add to monitor to enable halting // add to services to make it stoppable
Monitor.add(service) Services.add(service)
} }
// monitor monitors running services (RepoService) // services stores all repoServices
// and can halt them. type services struct {
type monitor struct { services []*repoService
services []*RepoService
sync.Mutex sync.Mutex
} }
// add adds a new service to the monitor. // add adds a new service to list of services.
func (m *monitor) add(service *RepoService) { func (s *services) add(r *repoService) {
m.Lock() s.Lock()
defer m.Unlock() defer s.Unlock()
m.services = append(m.services, service)
// start a goroutine to listen for halt signal s.services = append(s.services, r)
service.running = true
go func(r *RepoService) {
<-r.halt
r.running = false
}(service)
} }
// Stop stops at most `limit` currently running services that is pulling from git repo at // Stop stops at most `limit` running services pulling from git repo at
// repoURL. It returns list of exit channels for the services. A wait for message on the // repoURL. It waits until the service is terminated before returning.
// channels guarantees exit. If limit is less than zero, it is ignored. // If limit is less than zero, it is ignored.
// TODO find better ways to identify repos // TODO find better ways to identify repos
func (m *monitor) Stop(repoURL string, limit int) []chan struct{} { func (s *services) Stop(repoURL string, limit int) {
m.Lock() s.Lock()
defer m.Unlock() defer s.Unlock()
var chans []chan struct{}
// locate services // locate repos
for i, j := 0, 0; i < len(m.services) && ((limit >= 0 && j < limit) || limit < 0); i++ { for i, j := 0, 0; i < len(s.services) && ((limit >= 0 && j < limit) || limit < 0); i++ {
s := m.services[i] service := s.services[i]
if s.repo.URL == repoURL { if service.repo.URL == repoURL {
// send halt signal // send halt signal
s.halt <- struct{}{} service.halt <- struct{}{}
chans = append(chans, s.exit) s.services[i] = nil
j++ j++
m.services[i] = nil
} }
} }
// remove them from services list // remove them from repos list
services := m.services[:0] services := s.services[:0]
for _, s := range m.services { for _, s := range s.services {
if s != nil { if s != nil {
services = append(services, s) services = append(services, s)
} }
} }
m.services = services s.services = services
return chans
}
// StopAndWait is similar to stop but it waits for the services to terminate before
// returning.
func (m *monitor) StopAndWait(repoUrl string, limit int) {
chans := m.Stop(repoUrl, limit)
for _, c := range chans {
<-c
}
} }
...@@ -16,45 +16,45 @@ func Test(t *testing.T) { ...@@ -16,45 +16,45 @@ func Test(t *testing.T) {
repo := &Repo{URL: "git@github.com", Interval: time.Second} repo := &Repo{URL: "git@github.com", Interval: time.Second}
Start(repo) Start(repo)
if len(Monitor.services) != 1 { if len(Services.services) != 1 {
t.Errorf("Expected 1 service, found %v", len(Monitor.services)) t.Errorf("Expected 1 service, found %v", len(Services.services))
} }
Monitor.StopAndWait(repo.URL, 1) Services.Stop(repo.URL, 1)
if len(Monitor.services) != 0 { if len(Services.services) != 0 {
t.Errorf("Expected 1 service, found %v", len(Monitor.services)) t.Errorf("Expected 1 service, found %v", len(Services.services))
} }
repos := make([]*Repo, 5) repos := make([]*Repo, 5)
for i := 0; i < 5; i++ { for i := 0; i < 5; i++ {
repos[i] = &Repo{URL: fmt.Sprintf("test%v", i), Interval: time.Second * 2} repos[i] = &Repo{URL: fmt.Sprintf("test%v", i), Interval: time.Second * 2}
Start(repos[i]) Start(repos[i])
if len(Monitor.services) != i+1 { if len(Services.services) != i+1 {
t.Errorf("Expected %v service(s), found %v", i+1, len(Monitor.services)) t.Errorf("Expected %v service(s), found %v", i+1, len(Services.services))
} }
} }
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
Monitor.StopAndWait(repos[0].URL, 1) Services.Stop(repos[0].URL, 1)
if len(Monitor.services) != 4 { if len(Services.services) != 4 {
t.Errorf("Expected %v service(s), found %v", 4, len(Monitor.services)) t.Errorf("Expected %v service(s), found %v", 4, len(Services.services))
} }
repo = &Repo{URL: "git@github.com", Interval: time.Second} repo = &Repo{URL: "git@github.com", Interval: time.Second}
Start(repo) Start(repo)
if len(Monitor.services) != 5 { if len(Services.services) != 5 {
t.Errorf("Expected %v service(s), found %v", 5, len(Monitor.services)) t.Errorf("Expected %v service(s), found %v", 5, len(Services.services))
} }
repo = &Repo{URL: "git@github.com", Interval: time.Second * 2} repo = &Repo{URL: "git@github.com", Interval: time.Second * 2}
Start(repo) Start(repo)
if len(Monitor.services) != 6 { if len(Services.services) != 6 {
t.Errorf("Expected %v service(s), found %v", 6, len(Monitor.services)) t.Errorf("Expected %v service(s), found %v", 6, len(Services.services))
} }
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
Monitor.StopAndWait(repo.URL, -1) Services.Stop(repo.URL, -1)
if len(Monitor.services) != 4 { if len(Services.services) != 4 {
t.Errorf("Expected %v service(s), found %v", 4, len(Monitor.services)) t.Errorf("Expected %v service(s), found %v", 4, len(Services.services))
} }
} }
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment