Post #147
2.74K
This post (sticker, poll or similar) has no web preview. Open in Telegram
- ❤ 19
- ✍ 3
IG @igoroutine
Showing posts older than #148 · Back to latest
This post (sticker, poll or similar) has no web preview. Open in Telegram
Forwarded from igoroutine.courses
func (c *cronImpl) Run(ctx context.Context, action func(), next func() time.Duration) {
var t *time.Timer
t = time.AfterFunc(next(), func() {
select {
case <-ctx.Done():
return
default:
action()
t.Reset(next())
}
})
<-ctx.Done()
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second*10)
defer cancel()
c := cronImpl{}
c.Run(ctx,
func() {
fmt.Println("Hello, @igoroutine")
},
func() time.Duration {
return time.Second
},
)
}
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Hello, @igoroutine
Program exited 0.
go test -v -race ./...
func TestCron(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
c := New()
var n atomic.Int64
next := func() time.Duration {
if n.Add(1) <= 5 {
return 0
}
return 1 * time.Second
}
var calls atomic.Int64
ctx, cancel := context.WithCancel(t.Context())
done := make(chan struct{})
go func() {
defer close(done)
c.Run(ctx, func() {
calls.Add(1)
}, next)
}()
synctest.Wait()
require.Greater(t, calls.Load(), int64(0))
cancel()
synctest.Wait()
select {
case <-done:
default:
t.Fatalf("Run did not exit after cancel")
}
})
}
=== RUN TestCron
--- PASS: TestCron (0.00s)
PASS
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x1009927fc]
go test -v -count=10000 ./...
PASS
ok 0.787s
go test -v -race -count=100 ./...
PASS
ok 4.079s
count=1000000 и видим вот это:
WARNING: DATA RACE
Read at 0x00c0000024a0 by goroutine 3543:
cron.go:30 +0x90 // t.Reset(next())
Previous write at 0x00c0000024a0 by goroutine 3541:
cron.go:24 +0x194 // t = time.AfterFunc(next() ...
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x2 addr=0x0 pc=0x1050ae7fc]
func AfterFunc(d Duration, f func()) *Timer {
return newTimer(when(d), 0, goFunc, f, nil)
}
func goFunc(arg any, seq uintptr, delta int64) {
go arg.(func())()
}
d в этом коде
t = time.AfterFunc(next(), func() {
...
t.Reset(next())
})
Forwarded from igoroutine.courses
Состояние во время исполнения, когда две операции над переменной происходят из разных горутин без синхронизации, при этом хотя бы одна из них является записью.
Состояние во время исполнения, когда две конфликтующие операции над переменной происходят из разных горутин без синхронизации.
Состояние во время исполнения, когда две конфликтующие операции над переменной нельзя разделить отношением happens before
type Cron interface {
Run(ctx context.Context, action func(), next func() time.Duration)
}
action, ожидая перед каждым запуском next() времени. После отмены контекста не должно быть вызовов action.
func (c *cronImpl) Run(ctx context.Context, action func(), next func() time.Duration) {
var t *time.Timer
t = time.AfterFunc(next(), func() {
select {
case <-ctx.Done():
return
default:
action()
t.Reset(next())
}
})
<-ctx.Done()
}
func TestCron(t *testing.T) {
synctest.Test(t, func(t *testing.T) {
c := New()
var n atomic.Int64
next := func() time.Duration {
if n.Add(1) <= 5 {
return 0
}
return 1 * time.Second
}
var calls atomic.Int64
ctx, cancel := context.WithCancel(t.Context())
t.Cleanup(func() {
cancel()
})
done := make(chan struct{})
go func() {
defer close(done)
c.Run(ctx, func() {
calls.Add(1)
}, next)
}()
synctest.Wait()
require.Greater(t, calls.Load(), int64(0))
cancel()
synctest.Wait()
select {
case <-done:
default:
t.Fatalf("Run did not exit after cancel")
}
})
}
Forwarded from Yandex for Backend


















