10000 feat: add status watcher to MCP server by code-asher · Pull Request #18320 · coder/coder · GitHub
[go: up one dir, main page]

Skip to content

feat: add status watcher to MCP server #18320

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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Jun 13, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add queue util
  • Loading branch information
code-asher committed Jun 12, 2025
commit 5cee4c4d74b943b9fb256e04abb31b08038e77da
72 changes: 72 additions & 0 deletions cli/cliutil/queue.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package cliutil

import (
"sync"

"golang.org/x/xerrors"
)

// Queue is a FIFO queue with a fixed size. If the size is exceeded, the first
// item is dropped.
type Queue[T any] struct {
cond *sync.Cond
items []T
mu sync.Mutex
size int
closed bool
}

// NewQueue creates a queue with the given size.
func NewQueue[T any](size int) *Queue[T] {
q := &Queue[T]{
items: make([]T, 0, size),
size: size,
}
q.cond = sync.NewCond(&q.mu)
return q
}

// Close aborts any pending pops and makes future pushes error.
func (q *Queue[T]) Close() {
q.mu.Lock()
defer q.mu.Unlock()
q.closed = true
q.cond.Broadcast()
}

// Push adds an item to the queue. If closed, returns an error.
func (q *Queue[T]) Push(x T) error {
q.mu.Lock()
defer q.mu.Unlock()
if q.closed {
return xerrors.New("queue has been closed")
}
if len(q.items) >= q.size {
q.items = q.items[1:]
}
q.items = append(q.items, x)
q.cond.Broadcast()
return nil
}

// Pop removes and returns the first item from the queue, waiting until there is
// something to pop if necessary. If closed, returns false.
func (q *Queue[T]) Pop() (T, bool) {
var head T
q.mu.Lock()
defer q.mu.Unlock()
for len(q.items) == 0 && !q.closed {
q.cond.Wait()
}
if q.closed {
return head, false
}
head, q.items = q.items[0], q.items[1:]
return head, true
}

func (q *Queue[T]) Len() int {
q.mu.Lock()
defer q.mu.Unlock()
return len(q.items)
}
85 changes: 85 additions & 0 deletions cli/cliutil/queue_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package cliutil_test

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"

"github.com/coder/coder/v2/cli/cliutil"
)

func TestQueue(t *testing.T) {
t.Parallel()

t.Run("DropsFirst", func(t *testing.T) {
t.Parallel()

q := cliutil.NewQueue[int](10)
require.Equal(t, 0, q.Len())

for i := 0; i < 20; i++ {
err := q.Push(i)
require.NoError(t, err)
if i < 10 {
require.Equal(t, i+1, q.Len())
} else {
require.Equal(t, 10, q.Len())
}
}

val, ok := q.Pop()
require.True(t, ok)
require.Equal(t, 10, val)
require.Equal(t, 9, q.Len())
})

t.Run("Pop", func(t *testing.T) {
t.Parallel()

q := cliutil.NewQueue[int](10)
for i := 0; i < 5; i++ {
err := q.Push(i)
require.NoError(t, err)
}

// No blocking, should pop immediately.
for i := 0; i < 5; i++ {
val, ok := q.Pop()
require.True(t, ok)
require.Equal(t, i, val)
}

// Pop should block until the next push.
go func() {
err := q.Push(55)
assert.NoError(t, err)
}()

item, ok := q.Pop()
require.True(t, ok)
require.Equal(t, 55, item)
})

t.Run("Close", func(t *testing.T) {
t.Parallel()

q := cliutil.NewQueue[int](10)

done := make(chan bool)
go func() {
_, ok := q.Pop()
done <- ok
}()

q.Close()

require.False(t, <-done)

_, ok := q.Pop()
require.False(t, ok)

err := q.Push(10)
require.Error(t, err)
})
}
0