Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

chore(scaletest): add barrier synchronization primitive#19903

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Closed
ethanndickson wants to merge1 commit intomainfromethan/scaletest-barrier
Closed
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletionsscaletest/harness/barrier.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
package harness

import (
"sync/atomic"
)

type Barrier struct {
count atomic.Int64
done chan struct{}
}

// NewBarrier creates a new barrier that will block until `size` calls to Wait
// or `Cancel` have been made. It's the caller's responsibility to ensure this
// eventually happens.
func NewBarrier(size int) *Barrier {
b := &Barrier{
done: make(chan struct{}),
}
b.count.Store(int64(size))
return b
}

// Wait blocks until the barrier count reaches zero.
func (b *Barrier) Wait() {
b.Cancel()
<-b.done
Copy link
MemberAuthor

@ethanndicksonethanndicksonSep 22, 2025
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I contemplated having API consumers pass a ctx intoWait, and then selecting on that to support individualWait timeouts.

I opted against this on the basis that:

  • I already needed to add theCancel function, to handle user creation failure.
  • Other goroutines can just call thatCancel function in a defer, such as when their own timeouts fire.

The outcome being that this line won't block forever if the API is used properly.

}

// Cancel decrements the barrier count, unblocking other Wait calls if it
// reaches zero.
func (b *Barrier) Cancel() {
if b.count.Add(-1) == 0 {
close(b.done)
}
}
75 changes: 75 additions & 0 deletionsscaletest/harness/barrier_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
package harness_test

import (
"sync"
"testing"

"github.com/coder/coder/v2/scaletest/harness"
"github.com/coder/coder/v2/testutil"
)

func TestBarrier_MultipleRunners(t *testing.T) {
t.Parallel()
const numRunners = 3

ctx := testutil.Context(t, testutil.WaitShort)
barrier := harness.NewBarrier(numRunners)

var wg sync.WaitGroup
wg.Add(numRunners)

done := make(chan struct{})

for range numRunners {
go func() {
defer wg.Done()
barrier.Wait()
}()
}

go func() {
wg.Wait()
close(done)
}()

select {
case <-done:
return
case <-ctx.Done():
t.Fatal("barrier should have released all runners")
}
}

func TestBarrier_Cancel(t *testing.T) {
t.Parallel()
const numRunners = 3

ctx := testutil.Context(t, testutil.WaitShort)
barrier := harness.NewBarrier(numRunners)

var wg sync.WaitGroup
wg.Add(numRunners - 1)

done := make(chan struct{})

for range numRunners - 1 {
go func() {
defer wg.Done()
barrier.Wait()
}()
}

barrier.Cancel()

go func() {
wg.Wait()
close(done)
}()

select {
case <-done:
return
case <-ctx.Done():
t.Fatal("barrier should have released after cancel")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

we havetestutil.RequireReceive() for this since it comes up all the time.

}
}
Loading

[8]ページ先頭

©2009-2025 Movatter.jp