Compare commits
4 Commits
Author | SHA1 | Date | |
---|---|---|---|
bdf5b53c20
|
|||
496c4e4f59
|
|||
deab986caf
|
|||
9d9a6f1c6e
|
@@ -7,3 +7,11 @@ func FormatBool(v bool, strTrue string, strFalse string) string {
|
|||||||
return strFalse
|
return strFalse
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Conditional[T any](v bool, resTrue T, resFalse T) T {
|
||||||
|
if v {
|
||||||
|
return resTrue
|
||||||
|
} else {
|
||||||
|
return resFalse
|
||||||
|
}
|
||||||
|
}
|
||||||
|
98
syncext/atomic.go
Normal file
98
syncext/atomic.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package dataext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AtomicBool struct {
|
||||||
|
v int32
|
||||||
|
waiter chan bool // unbuffered
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewAtomicBool(value bool) *AtomicBool {
|
||||||
|
if value {
|
||||||
|
return &AtomicBool{v: 0, waiter: make(chan bool)}
|
||||||
|
} else {
|
||||||
|
return &AtomicBool{v: 1, waiter: make(chan bool)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AtomicBool) Get() bool {
|
||||||
|
return atomic.LoadInt32(&a.v) == 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AtomicBool) Set(value bool) {
|
||||||
|
if value {
|
||||||
|
atomic.StoreInt32(&a.v, 1)
|
||||||
|
} else {
|
||||||
|
atomic.StoreInt32(&a.v, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case a.waiter <- value:
|
||||||
|
// message sent
|
||||||
|
default:
|
||||||
|
// no receiver on channel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AtomicBool) Wait(waitFor bool) {
|
||||||
|
if a.Get() == waitFor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if v, ok := ReadChannelWithTimeout(a.waiter, 128*time.Millisecond); ok {
|
||||||
|
if v == waitFor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if a.Get() == waitFor {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *AtomicBool) WaitWithContext(ctx context.Context, waitFor bool) error {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Get() == waitFor {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
timeOut := 128 * time.Millisecond
|
||||||
|
|
||||||
|
if dl, ok := ctx.Deadline(); ok {
|
||||||
|
timeOutMax := dl.Sub(time.Now())
|
||||||
|
if timeOutMax <= 0 {
|
||||||
|
timeOut = 0
|
||||||
|
} else if 0 < timeOutMax && timeOutMax < timeOut {
|
||||||
|
timeOut = timeOutMax
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if v, ok := ReadChannelWithTimeout(a.waiter, timeOut); ok {
|
||||||
|
if v == waitFor {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := ctx.Err(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.Get() == waitFor {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
syncext/channel.go
Normal file
14
syncext/channel.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package dataext
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
func ReadChannelWithTimeout[T any](c chan T, timeout time.Duration) (T, bool) {
|
||||||
|
afterCh := time.After(timeout)
|
||||||
|
select {
|
||||||
|
case rv := <-c:
|
||||||
|
return rv, true
|
||||||
|
case <-afterCh:
|
||||||
|
return *new(T), false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
121
syncext/channel_test.go
Normal file
121
syncext/channel_test.go
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
package dataext
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTimeoutReadBuffered(t *testing.T) {
|
||||||
|
c := make(chan int, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
c <- 112
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 100*time.Millisecond)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Error("Read success, but should timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeoutReadBigBuffered(t *testing.T) {
|
||||||
|
c := make(chan int, 128)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
c <- 112
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 100*time.Millisecond)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Error("Read success, but should timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestTimeoutReadUnbuffered(t *testing.T) {
|
||||||
|
c := make(chan int)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(200 * time.Millisecond)
|
||||||
|
c <- 112
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 100*time.Millisecond)
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
t.Error("Read success, but should timeout")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoTimeoutAfterStartReadBuffered(t *testing.T) {
|
||||||
|
c := make(chan int, 1)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
c <- 112
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 100*time.Millisecond)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Error("Read timeout, but should have succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoTimeoutAfterStartReadBigBuffered(t *testing.T) {
|
||||||
|
c := make(chan int, 128)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
c <- 112
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 100*time.Millisecond)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Error("Read timeout, but should have succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoTimeoutAfterStartReadUnbuffered(t *testing.T) {
|
||||||
|
c := make(chan int)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Millisecond)
|
||||||
|
c <- 112
|
||||||
|
}()
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 100*time.Millisecond)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Error("Read timeout, but should have succeeded")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoTimeoutBeforeStartReadBuffered(t *testing.T) {
|
||||||
|
c := make(chan int, 1)
|
||||||
|
|
||||||
|
c <- 112
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 10*time.Millisecond)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Error("Read timeout, but should have succeeded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNoTimeoutBeforeStartReadBigBuffered(t *testing.T) {
|
||||||
|
c := make(chan int, 128)
|
||||||
|
|
||||||
|
c <- 112
|
||||||
|
|
||||||
|
_, ok := ReadChannelWithTimeout(c, 10*time.Millisecond)
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
t.Error("Read timeout, but should have succeeded")
|
||||||
|
}
|
||||||
|
}
|
@@ -2,59 +2,36 @@ package timeext
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromSeconds(v int) time.Duration {
|
func FromNanoseconds[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(int64(v) * int64(time.Second))
|
return time.Duration(int64(float64(v) * float64(time.Nanosecond)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromSecondsInt32(v int32) time.Duration {
|
func FromMicroseconds[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(int64(v) * int64(time.Second))
|
return time.Duration(int64(float64(v) * float64(time.Microsecond)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromSecondsInt64(v int64) time.Duration {
|
func FromMilliseconds[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(v * int64(time.Second))
|
return time.Duration(int64(float64(v) * float64(time.Millisecond)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromSecondsFloat32(v float32) time.Duration {
|
func FromSeconds[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(int64(v * float32(time.Second)))
|
return time.Duration(int64(float64(v) * float64(time.Second)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromSecondsFloat64(v float64) time.Duration {
|
func FromMinutes[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(int64(v * float64(time.Second)))
|
return time.Duration(int64(float64(v) * float64(time.Minute)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromSecondsFloat(v float64) time.Duration {
|
func FromHours[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(int64(v * float64(time.Second)))
|
return time.Duration(int64(float64(v) * float64(time.Hour)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func FromMinutes(v int) time.Duration {
|
func FromDays[T langext.NumberConstraint](v T) time.Duration {
|
||||||
return time.Duration(int64(v) * int64(time.Minute))
|
return time.Duration(int64(float64(v) * float64(24) * float64(time.Hour)))
|
||||||
}
|
|
||||||
|
|
||||||
func FromMinutesFloat(v float64) time.Duration {
|
|
||||||
return time.Duration(int64(v * float64(time.Minute)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromMinutesFloat64(v float64) time.Duration {
|
|
||||||
return time.Duration(int64(v * float64(time.Minute)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromHoursFloat64(v float64) time.Duration {
|
|
||||||
return time.Duration(int64(v * float64(time.Hour)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromDays(v int) time.Duration {
|
|
||||||
return time.Duration(int64(v) * int64(24) * int64(time.Hour))
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromMilliseconds(v int) time.Duration {
|
|
||||||
return time.Duration(int64(v) * int64(time.Millisecond))
|
|
||||||
}
|
|
||||||
|
|
||||||
func FromMillisecondsFloat(v float64) time.Duration {
|
|
||||||
return time.Duration(int64(v * float64(time.Millisecond)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func FormatNaturalDurationEnglish(iv time.Duration) string {
|
func FormatNaturalDurationEnglish(iv time.Duration) string {
|
||||||
|
Reference in New Issue
Block a user