Implemented epics read/write
This commit is contained in:
+36
-11
@@ -86,23 +86,47 @@ func (b *Broker) Source(name string) (datasource.DataSource, bool) {
|
||||
// If this is the first subscriber for the signal, the upstream DS subscription
|
||||
// is started. Subsequent calls for the same signal share the existing one.
|
||||
// The returned func must be called when the client no longer needs the signal.
|
||||
//
|
||||
// The broker mutex is NOT held while the upstream ds.Subscribe call is in
|
||||
// progress — some data sources (e.g. EPICS) block until the channel connects
|
||||
// or times out. Releasing the lock allows other goroutines to proceed.
|
||||
func (b *Broker) Subscribe(ref SignalRef, ch chan<- Update) (func(), error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
|
||||
sub, exists := b.subs[ref]
|
||||
if !exists {
|
||||
ds, found := b.sources[ref.DS]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("unknown data source %q", ref.DS)
|
||||
}
|
||||
if exists {
|
||||
// Fast path: already subscribed upstream; just add this client.
|
||||
sub.mu.Lock()
|
||||
sub.clients[ch] = struct{}{}
|
||||
sub.mu.Unlock()
|
||||
b.mu.Unlock()
|
||||
return func() { b.unsubscribe(ref, ch) }, nil
|
||||
}
|
||||
|
||||
rawCh := make(chan datasource.Value, 32)
|
||||
dsCancel, err := ds.Subscribe(b.ctx, ref.Name, rawCh)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("subscribe %s/%s: %w", ref.DS, ref.Name, err)
|
||||
}
|
||||
// Slow path: need to start an upstream subscription.
|
||||
// Release the lock before the potentially-blocking ds.Subscribe call.
|
||||
ds, found := b.sources[ref.DS]
|
||||
b.mu.Unlock()
|
||||
|
||||
if !found {
|
||||
return nil, fmt.Errorf("unknown data source %q", ref.DS)
|
||||
}
|
||||
|
||||
rawCh := make(chan datasource.Value, 32)
|
||||
dsCancel, err := ds.Subscribe(b.ctx, ref.Name, rawCh)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("subscribe %s/%s: %w", ref.DS, ref.Name, err)
|
||||
}
|
||||
|
||||
// Re-acquire the lock to register the new subscription.
|
||||
// Another goroutine might have registered the same ref while the lock
|
||||
// was released; if so, use that subscription and discard ours.
|
||||
b.mu.Lock()
|
||||
if existing, ok := b.subs[ref]; ok {
|
||||
// Race: someone else subscribed first. Discard duplicate upstream.
|
||||
dsCancel()
|
||||
sub = existing
|
||||
} else {
|
||||
sub = &signalSub{
|
||||
clients: make(map[chan<- Update]struct{}),
|
||||
done: make(chan struct{}),
|
||||
@@ -116,6 +140,7 @@ func (b *Broker) Subscribe(ref SignalRef, ch chan<- Update) (func(), error) {
|
||||
sub.mu.Lock()
|
||||
sub.clients[ch] = struct{}{}
|
||||
sub.mu.Unlock()
|
||||
b.mu.Unlock()
|
||||
|
||||
return func() { b.unsubscribe(ref, ch) }, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user