code.vegaprotocol.io/vega@v0.79.0/core/subscribers/base.go (about) 1 // Copyright (C) 2023 Gobalsky Labs Limited 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <http://www.gnu.org/licenses/>. 15 16 package subscribers 17 18 import ( 19 "context" 20 "time" 21 22 "code.vegaprotocol.io/vega/core/events" 23 ) 24 25 type Base struct { 26 ctx context.Context 27 cfunc context.CancelFunc 28 sCh chan struct{} 29 ch chan []events.Event 30 ack bool 31 running bool 32 id int 33 } 34 35 func NewBase(ctx context.Context, buf int, ack bool) *Base { 36 ctx, cfunc := context.WithCancel(ctx) 37 b := &Base{ 38 ctx: ctx, 39 cfunc: cfunc, 40 sCh: make(chan struct{}), 41 ch: make(chan []events.Event, buf), 42 ack: ack, 43 running: !ack, // assume the implementation will start a routine asap 44 } 45 if b.ack { 46 go b.cleanup() 47 } 48 return b 49 } 50 51 func (b *Base) cleanup() { 52 <-b.ctx.Done() 53 b.Halt() 54 } 55 56 // Ack returns whether or not this is a synchronous/async subscriber. 57 func (b *Base) Ack() bool { 58 return b.ack 59 } 60 61 // Pause the current subscriber will not receive events from the channel. 62 func (b *Base) Pause() { 63 if b.running { 64 b.running = false 65 close(b.sCh) 66 } 67 } 68 69 // Resume unpauzes the subscriber. 70 func (b *Base) Resume() { 71 if !b.running { 72 b.sCh = make(chan struct{}) 73 b.running = true 74 } 75 } 76 77 func (b Base) isRunning() bool { 78 return b.running 79 } 80 81 // C returns the event channel for optional subscribers. 82 func (b *Base) C() chan<- []events.Event { 83 return b.ch 84 } 85 86 // Closed indicates to the broker that the subscriber is closed for business. 87 func (b *Base) Closed() <-chan struct{} { 88 return b.ctx.Done() 89 } 90 91 // Skip lets the broker know that the subscriber is not receiving events. 92 func (b *Base) Skip() <-chan struct{} { 93 return b.sCh 94 } 95 96 // Halt is called by the broker on shutdown, this closes the open channels. 97 func (b *Base) Halt() { 98 // This is a hacky fix, but the fact remains: closing this channel occasionally causes a data race 99 // we're using select, hoist the channel assignment, but the fact is: select is not atomic 100 // allow attempted writes during shutdown, unless this is an acking sub, with a potential blocking channel 101 defer func() { 102 if !b.ack { 103 time.Sleep(20 * time.Millisecond) // add sleep to avoid race (send on closed channel), 20ms should be plenty 104 } 105 close(b.ch) // close the event channel after pause (skip) and cfunc (closed) are toggled 106 }() 107 b.cfunc() // cancels the subscriber context, which breaks the loop 108 b.Pause() // close the skip channel 109 } 110 111 // SetID set the ID (exposed only to broker). 112 func (b *Base) SetID(id int) { 113 b.id = id 114 } 115 116 // ID returns the subscriber ID. 117 func (b *Base) ID() int { 118 return b.id 119 }