From cd847affb79ea6438c9721635724efc6f58e2215 Mon Sep 17 00:00:00 2001 From: Junegunn Choi Date: Mon, 12 Jan 2015 12:56:17 +0900 Subject: Reorganize source code --- src/util/atomicbool.go | 32 ++++++++++++++++++ src/util/atomicbool_test.go | 17 ++++++++++ src/util/eventbox.go | 80 +++++++++++++++++++++++++++++++++++++++++++++ src/util/eventbox_test.go | 61 ++++++++++++++++++++++++++++++++++ src/util/util.go | 56 +++++++++++++++++++++++++++++++ src/util/util_test.go | 22 +++++++++++++ 6 files changed, 268 insertions(+) create mode 100644 src/util/atomicbool.go create mode 100644 src/util/atomicbool_test.go create mode 100644 src/util/eventbox.go create mode 100644 src/util/eventbox_test.go create mode 100644 src/util/util.go create mode 100644 src/util/util_test.go (limited to 'src/util') diff --git a/src/util/atomicbool.go b/src/util/atomicbool.go new file mode 100644 index 00000000..9e1bdc8f --- /dev/null +++ b/src/util/atomicbool.go @@ -0,0 +1,32 @@ +package util + +import "sync" + +// AtomicBool is a boxed-class that provides synchronized access to the +// underlying boolean value +type AtomicBool struct { + mutex sync.Mutex + state bool +} + +// NewAtomicBool returns a new AtomicBool +func NewAtomicBool(initialState bool) *AtomicBool { + return &AtomicBool{ + mutex: sync.Mutex{}, + state: initialState} +} + +// Get returns the current boolean value synchronously +func (a *AtomicBool) Get() bool { + a.mutex.Lock() + defer a.mutex.Unlock() + return a.state +} + +// Set updates the boolean value synchronously +func (a *AtomicBool) Set(newState bool) bool { + a.mutex.Lock() + defer a.mutex.Unlock() + a.state = newState + return a.state +} diff --git a/src/util/atomicbool_test.go b/src/util/atomicbool_test.go new file mode 100644 index 00000000..1feff79c --- /dev/null +++ b/src/util/atomicbool_test.go @@ -0,0 +1,17 @@ +package util + +import "testing" + +func TestAtomicBool(t *testing.T) { + if !NewAtomicBool(true).Get() || NewAtomicBool(false).Get() { + t.Error("Invalid initial value") + } + + ab := NewAtomicBool(true) + if ab.Set(false) { + t.Error("Invalid return value") + } + if ab.Get() { + t.Error("Invalid state") + } +} diff --git a/src/util/eventbox.go b/src/util/eventbox.go new file mode 100644 index 00000000..568ad9f7 --- /dev/null +++ b/src/util/eventbox.go @@ -0,0 +1,80 @@ +package util + +import "sync" + +// EventType is the type for fzf events +type EventType int + +// Events is a type that associates EventType to any data +type Events map[EventType]interface{} + +// EventBox is used for coordinating events +type EventBox struct { + events Events + cond *sync.Cond + ignore map[EventType]bool +} + +// NewEventBox returns a new EventBox +func NewEventBox() *EventBox { + return &EventBox{ + events: make(Events), + cond: sync.NewCond(&sync.Mutex{}), + ignore: make(map[EventType]bool)} +} + +// Wait blocks the goroutine until signaled +func (b *EventBox) Wait(callback func(*Events)) { + b.cond.L.Lock() + defer b.cond.L.Unlock() + + if len(b.events) == 0 { + b.cond.Wait() + } + + callback(&b.events) +} + +// Set turns on the event type on the box +func (b *EventBox) Set(event EventType, value interface{}) { + b.cond.L.Lock() + defer b.cond.L.Unlock() + b.events[event] = value + if _, found := b.ignore[event]; !found { + b.cond.Broadcast() + } +} + +// Clear clears the events +// Unsynchronized; should be called within Wait routine +func (events *Events) Clear() { + for event := range *events { + delete(*events, event) + } +} + +// Peak peaks at the event box if the given event is set +func (b *EventBox) Peak(event EventType) bool { + b.cond.L.Lock() + defer b.cond.L.Unlock() + _, ok := b.events[event] + return ok +} + +// Watch deletes the events from the ignore list +func (b *EventBox) Watch(events ...EventType) { + b.cond.L.Lock() + defer b.cond.L.Unlock() + for _, event := range events { + delete(b.ignore, event) + } +} + +// Unwatch adds the events to the ignore list +func (b *EventBox) Unwatch(events ...EventType) { + b.cond.L.Lock() + defer b.cond.L.Unlock() + for _, event := range events { + b.ignore[event] = true + } +} diff --git a/src/util/eventbox_test.go b/src/util/eventbox_test.go new file mode 100644 index 00000000..5a9dc302 --- /dev/null +++ b/src/util/eventbox_test.go @@ -0,0 +1,61 @@ +package util + +import "testing" + +// fzf events +const ( + EvtReadNew EventType = iota + EvtReadFin + EvtSearchNew + EvtSearchProgress + EvtSearchFin + EvtClose +) + +func TestEventBox(t *testing.T) { + eb := NewEventBox() + + // Wait should return immediately + ch := make(chan bool) + + go func() { + eb.Set(EvtReadNew, 10) + ch <- true + <-ch + eb.Set(EvtSearchNew, 10) + eb.Set(EvtSearchNew, 15) + eb.Set(EvtSearchNew, 20) + eb.Set(EvtSearchProgress, 30) + ch <- true + <-ch + eb.Set(EvtSearchFin, 40) + ch <- true + <-ch + }() + + count := 0 + sum := 0 + looping := true + for looping { + <-ch + eb.Wait(func(events *Events) { + for _, value := range *events { + switch val := value.(type) { + case int: + sum += val + looping = sum < 100 + } + } + events.Clear() + }) + ch <- true + count++ + } + + if count != 3 { + t.Error("Invalid number of events", count) + } + if sum != 100 { + t.Error("Invalid sum", sum) + } +} diff --git a/src/util/util.go b/src/util/util.go new file mode 100644 index 00000000..14833c04 --- /dev/null +++ b/src/util/util.go @@ -0,0 +1,56 @@ +package util + +// #include +import "C" + +import ( + "os" + "time" +) + +// Max returns the largest integer +func Max(first int, items ...int) int { + max := first + for _, item := range items { + if item > max { + max = item + } + } + return max +} + +// Max32 returns the largest 32-bit integer +func Max32(first int32, second int32) int32 { + if first > second { + return first + } + return second +} + +// Constrain limits the given integer with the upper and lower bounds +func Constrain(val int, min int, max int) int { + if val < min { + return min + } + if val > max { + return max + } + return val +} + +// DurWithin limits the given time.Duration with the upper and lower bounds +func DurWithin( + val time.Duration, min time.Duration, max time.Duration) time.Duration { + if val < min { + return min + } + if val > max { + return max + } + return val +} + +// IsTty returns true is stdin is a terminal +func IsTty() bool { + return int(C.isatty(C.int(os.Stdin.Fd()))) != 0 +} diff --git a/src/util/util_test.go b/src/util/util_test.go new file mode 100644 index 00000000..06cfd4f2 --- /dev/null +++ b/src/util/util_test.go @@ -0,0 +1,22 @@ +package util + +import "testing" + +func TestMax(t *testing.T) { + if Max(-2, 5, 1, 4, 3) != 5 { + t.Error("Invalid result") + } +} + +func TestContrain(t *testing.T) { + if Constrain(-3, -1, 3) != -1 { + t.Error("Expected", -1) + } + if Constrain(2, -1, 3) != 2 { + t.Error("Expected", 2) + } + + if Constrain(5, -1, 3) != 3 { + t.Error("Expected", 3) + } +} -- cgit v1.2.3