TGViewer
Igor Panasyuk | IGORoutine Programming Igor Panasyuk | IGORoutine Programming @igoroutine · 2.22K subscribers
Post #144 1.87K

Forwarded from igoroutine.courses

😢😢 От этого data race не спасает даже race detector

Data race это частный случай race condition. Определять термин data race на самом деле можно по-разному.

Самое простое определение:
Состояние во время исполнения, когда две операции над переменной происходят из разных горутин без синхронизации, при этом хотя бы одна из них является записью.

Чуть более сложное:
Состояние во время исполнения, когда две конфликтующие операции над переменной происходят из разных горутин без синхронизации.

И самое сложное на основании формализма из Go memory model:
Состояние во время исполнения, когда две конфликтующие операции над переменной нельзя разделить отношением happens before


Казалось бы, да ну их, есть прекрасный инструмент race detector, который инструментриует программу при компиляции и позволяет во время исполнения находить data race.

Но, внезапно, выясняется, что он не всегда может найти проблему. Это напрямую следует из его устройства.

Пример:

Мейнтейнеры Go пытались реализовать такой интерфейс:


type Cron interface {
Run(ctx context.Context, action func(), next func() time.Duration)
}


Реализация должна вызывать action, ожидая перед каждым запуском next() времени. После отмены контекста не должно быть вызовов action.

Такой подход на практике очень часто используется, например:

• HTTP ретраи
• Service discovery
• Adaptive polling
• Keepalive / heartbeat
• Любые задачи с backoff

Сходу у них получилась следующая реализация:


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")
}
})
}


Которые успешно проходят даже с race detector'ом.

Но в этой реализации действительно есть data race, и race detector его не находит.

👉👉 Пишите ваши предположения в комментариях, пост с правильным ответом выйдет в ближайшее время.

Ссылка с примером использования: https://go.dev/play/p/Fq-yNa0wXWj

P.S. Пожалуйста, те, кто проходил курс и решал это задание на практике, не пишите сразу правильный ответ)
  • 🤔 8
  • 👍 2
  • ❤ 1
  • ✍ 1
More from @igoroutine
  1. Sep 29, 2026Отправляешь очередной промпт нейронке и ловишь себя на мысли: «Я - какой-то ненастоящий пр…
  2. Jul 13, 2026Доброго вечера и хорошей недели)) На ней выйдут два новых видео. Рассвет сегодня не заснял…
  3. Jul 6, 2026Доброе утро и хорошей недели
  4. Jul 4, 2026AI заменит образование? В последнее время в индустрии очень остро стоит вопрос о смысле тр…
  5. Jul 1, 2026Без этих знаний твой Go-вайбкод положит прод | Пакет sync изнутри Давно обещал выпустить п…
  6. Jun 29, 2026Доброе утро и хорошей недели
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →