From d05dad6c59ebb15675adee0c9b58692da5485730 Mon Sep 17 00:00:00 2001 From: Daniel Smith Date: Mon, 27 Oct 2014 17:51:48 -0700 Subject: [PATCH] Add runner utility. --- pkg/util/runner.go | 58 +++++++++++++++++++++++++++++++++++++++++ pkg/util/runner_test.go | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) create mode 100644 pkg/util/runner.go create mode 100644 pkg/util/runner_test.go diff --git a/pkg/util/runner.go b/pkg/util/runner.go new file mode 100644 index 00000000000..85cd7e21975 --- /dev/null +++ b/pkg/util/runner.go @@ -0,0 +1,58 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "sync" +) + +// Runner is an abstraction to make it easy to start and stop groups of things that can be +// described by a single function which waits on a channel close to exit. +type Runner struct { + lock sync.Mutex + loopFuncs []func(stop chan struct{}) + stop *chan struct{} +} + +// NewRunner makes a runner for the given function(s). The function(s) should loop until +// the channel is closed. +func NewRunner(f ...func(stop chan struct{})) *Runner { + return &Runner{loopFuncs: f} +} + +// Start begins running. +func (r *Runner) Start() { + r.lock.Lock() + defer r.lock.Unlock() + if r.stop == nil { + c := make(chan struct{}) + r.stop = &c + for i := range r.loopFuncs { + go r.loopFuncs[i](*r.stop) + } + } +} + +// Stop stops running. +func (r *Runner) Stop() { + r.lock.Lock() + defer r.lock.Unlock() + if r.stop != nil { + close(*r.stop) + r.stop = nil + } +} diff --git a/pkg/util/runner_test.go b/pkg/util/runner_test.go new file mode 100644 index 00000000000..0639edc52d2 --- /dev/null +++ b/pkg/util/runner_test.go @@ -0,0 +1,55 @@ +/* +Copyright 2014 Google Inc. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package util + +import ( + "fmt" + "sync" + "testing" +) + +func TestRunner(t *testing.T) { + var ( + lock sync.Mutex + events []string + funcs []func(chan struct{}) + ) + done := make(chan struct{}, 20) + for i := 0; i < 10; i++ { + iCopy := i + funcs = append(funcs, func(c chan struct{}) { + lock.Lock() + events = append(events, fmt.Sprintf("%v starting\n", iCopy)) + lock.Unlock() + <-c + lock.Lock() + events = append(events, fmt.Sprintf("%v stopping\n", iCopy)) + lock.Unlock() + done <- struct{}{} + }) + } + + r := NewRunner(funcs...) + r.Start() + r.Stop() + for i := 0; i < 10; i++ { + <-done + } + if len(events) != 20 { + t.Errorf("expected 20 events, but got:\n%v\n", events) + } +}