Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
128
backend/internal/pluginhost/client_guard.go
Normal file
128
backend/internal/pluginhost/client_guard.go
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
package pluginhost
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
)
|
||||
|
||||
type guardedPluginClient struct {
|
||||
mu sync.Mutex
|
||||
cond *sync.Cond
|
||||
inner pluginClient
|
||||
calls int
|
||||
closed bool
|
||||
shutdownDone chan struct{}
|
||||
}
|
||||
|
||||
func newGuardedPluginClient(inner pluginClient) *guardedPluginClient {
|
||||
client := &guardedPluginClient{inner: inner, shutdownDone: make(chan struct{})}
|
||||
client.cond = sync.NewCond(&client.mu)
|
||||
return client
|
||||
}
|
||||
|
||||
func (c *guardedPluginClient) Call(ctx context.Context, method string, request []byte) ([]byte, error) {
|
||||
inner, errAcquire := c.acquire()
|
||||
if errAcquire != nil {
|
||||
return nil, errAcquire
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
result := make(chan guardedPluginCallResult, 1)
|
||||
go func() {
|
||||
defer c.release()
|
||||
defer func() {
|
||||
if recovered := recover(); recovered != nil {
|
||||
result <- guardedPluginCallResult{recovered: recovered}
|
||||
}
|
||||
}()
|
||||
response, errCall := inner.Call(ctx, method, request)
|
||||
result <- guardedPluginCallResult{response: response, err: errCall}
|
||||
}()
|
||||
select {
|
||||
case callResult := <-result:
|
||||
if callResult.recovered != nil {
|
||||
panic(callResult.recovered)
|
||||
}
|
||||
return callResult.response, callResult.err
|
||||
case <-ctx.Done():
|
||||
return nil, ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
type guardedPluginCallResult struct {
|
||||
response []byte
|
||||
err error
|
||||
recovered any
|
||||
}
|
||||
|
||||
func (c *guardedPluginClient) acquire() (pluginClient, error) {
|
||||
if c == nil {
|
||||
return nil, fmt.Errorf("plugin client is closed")
|
||||
}
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
if c.closed || c.inner == nil {
|
||||
return nil, fmt.Errorf("plugin client is closed")
|
||||
}
|
||||
c.calls++
|
||||
return c.inner, nil
|
||||
}
|
||||
|
||||
func (c *guardedPluginClient) release() {
|
||||
c.mu.Lock()
|
||||
c.calls--
|
||||
if c.calls == 0 {
|
||||
c.cond.Broadcast()
|
||||
}
|
||||
c.mu.Unlock()
|
||||
}
|
||||
|
||||
func (c *guardedPluginClient) Shutdown() {
|
||||
c.ShutdownContext(context.Background())
|
||||
}
|
||||
|
||||
// ShutdownContext detaches the client immediately and waits for active calls only
|
||||
// until ctx is canceled. Detached cleanup continues asynchronously when needed.
|
||||
func (c *guardedPluginClient) ShutdownContext(ctx context.Context) {
|
||||
if c == nil {
|
||||
return
|
||||
}
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
|
||||
c.mu.Lock()
|
||||
if c.closed {
|
||||
done := c.shutdownDone
|
||||
c.mu.Unlock()
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
return
|
||||
}
|
||||
c.closed = true
|
||||
inner := c.inner
|
||||
c.inner = nil
|
||||
done := c.shutdownDone
|
||||
c.mu.Unlock()
|
||||
|
||||
go func() {
|
||||
c.mu.Lock()
|
||||
for c.calls > 0 {
|
||||
c.cond.Wait()
|
||||
}
|
||||
c.mu.Unlock()
|
||||
if inner != nil {
|
||||
inner.Shutdown()
|
||||
}
|
||||
close(done)
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-done:
|
||||
case <-ctx.Done():
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue