diff options
author | 2021-11-13 12:29:08 +0100 | |
---|---|---|
committer | 2021-11-13 12:29:08 +0100 | |
commit | 829a934d23ab221049b4d54926305d8d5d64c9ad (patch) | |
tree | f4e382b289c113d3ba8a3c7a183507a5609c46c0 /vendor/codeberg.org/gruf/go-nowish | |
parent | smtp + email confirmation (#285) (diff) | |
download | gotosocial-829a934d23ab221049b4d54926305d8d5d64c9ad.tar.xz |
update dependencies (#296)
Diffstat (limited to 'vendor/codeberg.org/gruf/go-nowish')
-rw-r--r-- | vendor/codeberg.org/gruf/go-nowish/LICENSE | 9 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-nowish/README.md | 3 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-nowish/time.go | 141 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-nowish/timeout.go | 118 | ||||
-rw-r--r-- | vendor/codeberg.org/gruf/go-nowish/util.go | 10 |
5 files changed, 281 insertions, 0 deletions
diff --git a/vendor/codeberg.org/gruf/go-nowish/LICENSE b/vendor/codeberg.org/gruf/go-nowish/LICENSE new file mode 100644 index 000000000..b7c4417ac --- /dev/null +++ b/vendor/codeberg.org/gruf/go-nowish/LICENSE @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2021 gruf + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/codeberg.org/gruf/go-nowish/README.md b/vendor/codeberg.org/gruf/go-nowish/README.md new file mode 100644 index 000000000..4070e5013 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-nowish/README.md @@ -0,0 +1,3 @@ +a simple Go library with useful time utiities: +- Clock: a high performance clock giving a good "ish" representation of "now" (hence the name!) +- Timeout: a reusable structure for enforcing timeouts with a cancel diff --git a/vendor/codeberg.org/gruf/go-nowish/time.go b/vendor/codeberg.org/gruf/go-nowish/time.go new file mode 100644 index 000000000..674ff5669 --- /dev/null +++ b/vendor/codeberg.org/gruf/go-nowish/time.go @@ -0,0 +1,141 @@ +package nowish + +import ( + "sync" + "sync/atomic" + "time" + "unsafe" +) + +// Start returns a new Clock instance initialized and +// started with the provided precision, along with the +// stop function for it's underlying timer +func Start(precision time.Duration) (*Clock, func()) { + c := Clock{} + return &c, c.Start(precision) +} + +type Clock struct { + noCopy noCopy //nolint noCopy because a copy will fuck with atomics + + // format stores the time formatting style string + format string + + // valid indicates whether the current value stored in .Format is valid + valid uint32 + + // mutex protects writes to .Format, not because it would be unsafe, but + // because we want to minimize unnnecessary allocations + mutex sync.Mutex + + // nowfmt is an unsafe pointer to the last-updated time format string + nowfmt unsafe.Pointer + + // now is an unsafe pointer to the last-updated time.Time object + now unsafe.Pointer +} + +// Start starts the clock with the provided precision, the +// returned function is the stop function for the underlying timer +func (c *Clock) Start(precision time.Duration) func() { + // Create ticker from duration + tick := time.NewTicker(precision) + + // Set initial time + t := time.Now() + atomic.StorePointer(&c.now, unsafe.Pointer(&t)) + + // Set initial format + s := "" + atomic.StorePointer(&c.nowfmt, unsafe.Pointer(&s)) + + // If formatting string unset, set default + c.mutex.Lock() + if c.format == "" { + c.format = time.RFC822 + } + c.mutex.Unlock() + + // Start main routine + go c.run(tick) + + // Return stop fn + return tick.Stop +} + +// run is the internal clock ticking loop +func (c *Clock) run(tick *time.Ticker) { + for { + // Wait on tick + _, ok := <-tick.C + + // Channel closed + if !ok { + break + } + + // Update time + t := time.Now() + atomic.StorePointer(&c.now, unsafe.Pointer(&t)) + + // Invalidate format string + atomic.StoreUint32(&c.valid, 0) + } +} + +// Now returns a good (ish) estimate of the current 'now' time +func (c *Clock) Now() time.Time { + return *(*time.Time)(atomic.LoadPointer(&c.now)) +} + +// NowFormat returns the formatted "now" time, cached until next tick and "now" updates +func (c *Clock) NowFormat() string { + // If format still valid, return this + if atomic.LoadUint32(&c.valid) == 1 { + return *(*string)(atomic.LoadPointer(&c.nowfmt)) + } + + // Get mutex lock + c.mutex.Lock() + + // Double check still invalid + if atomic.LoadUint32(&c.valid) == 1 { + c.mutex.Unlock() + return *(*string)(atomic.LoadPointer(&c.nowfmt)) + } + + // Calculate time format + b := c.Now().AppendFormat( + make([]byte, 0, len(c.format)), + c.format, + ) + + // Update the stored value and set valid! + atomic.StorePointer(&c.nowfmt, unsafe.Pointer(&b)) + atomic.StoreUint32(&c.valid, 1) + + // Unlock and return + c.mutex.Unlock() + + // Note: + // it's safe to do this conversion here + // because this byte slice will never change. + // and we have the direct pointer to it, we're + // not requesting it atomicly via c.Format + return *(*string)(unsafe.Pointer(&b)) +} + +// SetFormat sets the time format string used by .NowFormat() +func (c *Clock) SetFormat(format string) { + // Get mutex lock + c.mutex.Lock() + + // Update time format + c.format = format + + // Invalidate current format string + atomic.StoreUint32(&c.valid, 0) + + // Unlock + c.mutex.Unlock() +} diff --git a/vendor/codeberg.org/gruf/go-nowish/timeout.go b/vendor/codeberg.org/gruf/go-nowish/timeout.go new file mode 100644 index 000000000..1097f5d3d --- /dev/null +++ b/vendor/codeberg.org/gruf/go-nowish/timeout.go @@ -0,0 +1,118 @@ +package nowish + +import ( + "sync" + "sync/atomic" + "time" +) + +// Timeout provides a reusable structure for enforcing timeouts with a cancel +type Timeout struct { + noCopy noCopy //nolint noCopy because a copy will mess with atomics + + tk *time.Timer // tk is the underlying timeout-timer + ch syncer // ch is the cancel synchronization channel + wg sync.WaitGroup // wg is the waitgroup to hold .Start() until timeout goroutine started + st timeoutState // st stores the current timeout state (and protects concurrent use) +} + +// NewTimeout returns a new Timeout instance +func NewTimeout() Timeout { + tk := time.NewTimer(time.Minute) + tk.Stop() // don't keep it running + return Timeout{ + tk: tk, + ch: make(syncer), + } +} + +func (t *Timeout) runTimeout(hook func()) { + t.wg.Add(1) + go func() { + cancelled := false + + // Signal started + t.wg.Done() + + select { + // Timeout reached + case <-t.tk.C: + if !t.st.stop() /* a sneaky cancel! */ { + t.ch.recv() + cancelled = true + defer t.ch.send() + } + + // Cancel called + case <-t.ch: + cancelled = true + defer t.ch.send() + } + + // Ensure timer stopped + if cancelled && !t.tk.Stop() { + <-t.tk.C + } + + // Defer reset state + defer t.st.reset() + + // If timed out call hook + if !cancelled { + hook() + } + }() + t.wg.Wait() +} + +// Start starts the timer with supplied timeout. If timeout is reached before +// cancel then supplied timeout hook will be called. Error may be called if +// Timeout is already running when this function is called +func (t *Timeout) Start(d time.Duration, hook func()) { + if !t.st.start() { + panic("nowish: timeout already started") + } + t.runTimeout(hook) + t.tk.Reset(d) +} + +// Cancel cancels the currently running timer. If a cancel is achieved, then +// this function will return after the timeout goroutine is finished +func (t *Timeout) Cancel() { + if !t.st.stop() { + return + } + t.ch.send() + t.ch.recv() +} + +// timeoutState provides a thread-safe timeout state mechanism +type timeoutState uint32 + +// start attempts to start the state, must be already reset, returns success +func (t *timeoutState) start() bool { + return atomic.CompareAndSwapUint32((*uint32)(t), 0, 1) +} + +// stop attempts to stop the state, must already be started, returns success +func (t *timeoutState) stop() bool { + return atomic.CompareAndSwapUint32((*uint32)(t), 1, 2) +} + +// reset is fairly self explanatory +func (t *timeoutState) reset() { + atomic.StoreUint32((*uint32)(t), 0) +} + +// syncer provides helpful receiver methods for a synchronization channel +type syncer (chan struct{}) + +// send blocks on sending an empty value down channel +func (s syncer) send() { + s <- struct{}{} +} + +// recv blocks on receiving (and dropping) empty value from channel +func (s syncer) recv() { + <-s +} diff --git a/vendor/codeberg.org/gruf/go-nowish/util.go b/vendor/codeberg.org/gruf/go-nowish/util.go new file mode 100644 index 000000000..31fe9050e --- /dev/null +++ b/vendor/codeberg.org/gruf/go-nowish/util.go @@ -0,0 +1,10 @@ +package nowish + +//nolint +type noCopy struct{} + +//nolint +func (*noCopy) Lock() {} + +//nolint +func (*noCopy) Unlock() {} |