vendor tsgo
This commit is contained in:
52
tools/tsgo/internal/project/background/queue.go
Normal file
52
tools/tsgo/internal/project/background/queue.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package background
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Queue manages background tasks execution
|
||||
type Queue struct {
|
||||
wg sync.WaitGroup
|
||||
mu sync.RWMutex
|
||||
closed bool
|
||||
}
|
||||
|
||||
// NewQueue creates a new background queue for managing background tasks execution.
|
||||
func NewQueue() *Queue {
|
||||
return &Queue{}
|
||||
}
|
||||
|
||||
func (q *Queue) Enqueue(ctx context.Context, fn func(context.Context)) {
|
||||
q.mu.RLock()
|
||||
if q.closed {
|
||||
q.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
q.mu.RUnlock()
|
||||
|
||||
// Don't start new tasks if context is already cancelled
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
|
||||
q.wg.Go(func() {
|
||||
// Check context again before executing
|
||||
if ctx.Err() != nil {
|
||||
return
|
||||
}
|
||||
fn(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
// Wait waits for all active tasks to complete.
|
||||
// It does not prevent new tasks from being enqueued while waiting.
|
||||
func (q *Queue) Wait() {
|
||||
q.wg.Wait()
|
||||
}
|
||||
|
||||
func (q *Queue) Close() {
|
||||
q.mu.Lock()
|
||||
q.closed = true
|
||||
q.mu.Unlock()
|
||||
}
|
||||
91
tools/tsgo/internal/project/background/queue_test.go
Normal file
91
tools/tsgo/internal/project/background/queue_test.go
Normal file
@@ -0,0 +1,91 @@
|
||||
package background_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/microsoft/typescript-go/internal/project/background"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestQueue(t *testing.T) {
|
||||
t.Parallel()
|
||||
t.Run("BasicEnqueue", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := background.NewQueue()
|
||||
defer q.Close()
|
||||
|
||||
executed := false
|
||||
q.Enqueue(context.Background(), func(ctx context.Context) {
|
||||
executed = true
|
||||
})
|
||||
|
||||
q.Wait()
|
||||
|
||||
assert.Check(t, executed)
|
||||
})
|
||||
|
||||
t.Run("MultipleTasksExecution", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := background.NewQueue()
|
||||
defer q.Close()
|
||||
|
||||
var counter atomic.Int64
|
||||
numTasks := 10
|
||||
|
||||
for range numTasks {
|
||||
q.Enqueue(context.Background(), func(ctx context.Context) {
|
||||
counter.Add(1)
|
||||
})
|
||||
}
|
||||
|
||||
q.Wait()
|
||||
|
||||
assert.Equal(t, counter.Load(), int64(numTasks))
|
||||
})
|
||||
|
||||
t.Run("NestedEnqueue", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := background.NewQueue()
|
||||
defer q.Close()
|
||||
|
||||
var executed []string
|
||||
var mu sync.Mutex
|
||||
|
||||
q.Enqueue(context.Background(), func(ctx context.Context) {
|
||||
mu.Lock()
|
||||
executed = append(executed, "parent")
|
||||
mu.Unlock()
|
||||
|
||||
q.Enqueue(ctx, func(childCtx context.Context) {
|
||||
mu.Lock()
|
||||
executed = append(executed, "child")
|
||||
mu.Unlock()
|
||||
})
|
||||
})
|
||||
|
||||
q.Wait()
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
|
||||
assert.Equal(t, len(executed), 2)
|
||||
})
|
||||
|
||||
t.Run("ClosedQueueRejectsNewTasks", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
q := background.NewQueue()
|
||||
q.Close()
|
||||
|
||||
executed := false
|
||||
q.Enqueue(context.Background(), func(ctx context.Context) {
|
||||
executed = true
|
||||
})
|
||||
|
||||
q.Wait()
|
||||
|
||||
assert.Check(t, !executed, "Task should not execute after queue is closed")
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user