Skip to content

Commit 24e3e3c

Browse files
committed
staticaddr: serialize address creation with a channel
1 parent ae22323 commit 24e3e3c

File tree

1 file changed

+109
-3
lines changed

1 file changed

+109
-3
lines changed

staticaddr/address/manager.go

Lines changed: 109 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,19 +54,91 @@ type Manager struct {
5454
cfg *ManagerConfig
5555

5656
currentHeight atomic.Int32
57+
58+
// addrRequest is a channel used to request new static addresses from
59+
// the manager. The manager employs a go worker routine that handles the
60+
// requests.
61+
addrRequest chan request
62+
}
63+
64+
type request struct {
65+
ctx context.Context
66+
respChan chan response
67+
}
68+
69+
type response struct {
70+
addr *btcutil.AddressTaproot
71+
expiry int64
72+
err error
5773
}
5874

5975
// NewManager creates a new address manager.
6076
func NewManager(cfg *ManagerConfig, currentHeight int32) *Manager {
6177
m := &Manager{
62-
cfg: cfg,
78+
cfg: cfg,
79+
addrRequest: make(chan request),
6380
}
6481
m.currentHeight.Store(currentHeight)
6582

6683
return m
6784
}
6885

69-
// Run runs the address manager.
86+
// addrWorker is a worker that handles address creation requests. It calls
87+
// m.newAddress which blocks on server I/O and returns the address and expiry.
88+
func (m *Manager) addrWorker(ctx context.Context) {
89+
for {
90+
select {
91+
case req := <-m.addrRequest:
92+
m.handleAddrRequest(ctx, req)
93+
94+
case <-ctx.Done():
95+
return
96+
}
97+
}
98+
}
99+
100+
// handleAddrRequest is responsible for processing a single address request.
101+
func (m *Manager) handleAddrRequest(managerCtx context.Context, req request) {
102+
// If processing this request panics, we want to recover to process
103+
// successive requests after we returned an error to the caller.
104+
defer func() {
105+
if r := recover(); r != nil {
106+
var err error
107+
if e, ok := r.(error); ok {
108+
err = e
109+
} else {
110+
err = fmt.Errorf("addrWorker panic: %v", r)
111+
}
112+
113+
select {
114+
case req.respChan <- response{err: err}:
115+
116+
case <-req.ctx.Done():
117+
118+
case <-managerCtx.Done():
119+
}
120+
}
121+
}()
122+
123+
addr, expiry, e := m.newAddress(req.ctx)
124+
125+
resp := response{
126+
addr: addr,
127+
expiry: expiry,
128+
err: e,
129+
}
130+
131+
select {
132+
case req.respChan <- resp:
133+
134+
case <-req.ctx.Done():
135+
136+
case <-managerCtx.Done():
137+
}
138+
}
139+
140+
// Run runs the address manager. It keeps track of the current block height and
141+
// creates new static addresses as needed.
70142
func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
71143
newBlockChan, newBlockErrChan, err :=
72144
m.cfg.ChainNotifier.RegisterBlockEpochNtfn(ctx)
@@ -75,6 +147,10 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
75147
return err
76148
}
77149

150+
// The address worker offloads the address creation with the server to a
151+
// separate go routine.
152+
go m.addrWorker(ctx)
153+
78154
// Communicate to the caller that the address manager has completed its
79155
// initialization.
80156
close(initChan)
@@ -95,10 +171,40 @@ func (m *Manager) Run(ctx context.Context, initChan chan struct{}) error {
95171
}
96172

97173
// NewAddress creates a new static address with the server or returns an
98-
// existing one.
174+
// existing one. It now sends a request to the manager's Run loop which
175+
// executes the actual address creation logic.
99176
func (m *Manager) NewAddress(ctx context.Context) (*btcutil.AddressTaproot,
100177
int64, error) {
101178

179+
respChan := make(chan response, 1)
180+
req := request{
181+
ctx: ctx,
182+
respChan: respChan,
183+
}
184+
185+
// Send the new address request to the manager run loop.
186+
select {
187+
case m.addrRequest <- req:
188+
189+
case <-ctx.Done():
190+
return nil, 0, ctx.Err()
191+
}
192+
193+
// Wait for the response from the manager run loop.
194+
select {
195+
case resp := <-respChan:
196+
return resp.addr, resp.expiry, resp.err
197+
198+
case <-ctx.Done():
199+
return nil, 0, ctx.Err()
200+
}
201+
}
202+
203+
// newAddress contains the body of the former NewAddress method and performs the
204+
// actual address creation/lookup according to the requested type.
205+
func (m *Manager) newAddress(ctx context.Context) (*btcutil.AddressTaproot,
206+
int64, error) {
207+
102208
// If there's already a static address in the database, we can return
103209
// it.
104210
m.Lock()

0 commit comments

Comments
 (0)