From 4f01edb9230f58ff84b0dd892c931ec8ac9aad55 Mon Sep 17 00:00:00 2001 From: Brian Picciano Date: Tue, 13 Sep 2022 12:56:08 +0200 Subject: move src out of srv, clean up default.nix and Makefile --- src/pow/store.go | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) create mode 100644 src/pow/store.go (limited to 'src/pow/store.go') diff --git a/src/pow/store.go b/src/pow/store.go new file mode 100644 index 0000000..0b5e7d0 --- /dev/null +++ b/src/pow/store.go @@ -0,0 +1,92 @@ +package pow + +import ( + "errors" + "sync" + "time" + + "github.com/tilinna/clock" +) + +// ErrSeedSolved is used to indicate a seed has already been solved. +var ErrSeedSolved = errors.New("seed already solved") + +// Store is used to track information related to proof-of-work challenges and +// solutions. +type Store interface { + + // MarkSolved will return ErrSeedSolved if the seed was already marked. The + // seed will be cleared from the Store once expiresAt is reached. + MarkSolved(seed []byte, expiresAt time.Time) error + + Close() error +} + +type inMemStore struct { + clock clock.Clock + + m map[string]time.Time + l sync.Mutex + closeCh chan struct{} + spinLoopCh chan struct{} // only used by tests +} + +const inMemStoreGCPeriod = 5 * time.Second + +// NewMemoryStore initializes and returns an in-memory Store implementation. +func NewMemoryStore(clock clock.Clock) Store { + s := &inMemStore{ + clock: clock, + m: map[string]time.Time{}, + closeCh: make(chan struct{}), + spinLoopCh: make(chan struct{}, 1), + } + go s.spin(s.clock.NewTicker(inMemStoreGCPeriod)) + return s +} + +func (s *inMemStore) spin(ticker *clock.Ticker) { + defer ticker.Stop() + + for { + select { + case <-ticker.C: + now := s.clock.Now() + + s.l.Lock() + for seed, expiresAt := range s.m { + if !now.Before(expiresAt) { + delete(s.m, seed) + } + } + s.l.Unlock() + + case <-s.closeCh: + return + } + + select { + case s.spinLoopCh <- struct{}{}: + default: + } + } +} + +func (s *inMemStore) MarkSolved(seed []byte, expiresAt time.Time) error { + seedStr := string(seed) + + s.l.Lock() + defer s.l.Unlock() + + if _, ok := s.m[seedStr]; ok { + return ErrSeedSolved + } + + s.m[seedStr] = expiresAt + return nil +} + +func (s *inMemStore) Close() error { + close(s.closeCh) + return nil +} -- cgit v1.2.3