Skip to content

Commit 2295134

Browse files
authored
Merge pull request #785 from fluxcd/mutexless
libgit2: remove deadlock
2 parents 812f6e4 + a530c5d commit 2295134

File tree

1 file changed

+32
-39
lines changed

1 file changed

+32
-39
lines changed

pkg/git/libgit2/managed/ssh.go

Lines changed: 32 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import (
5454
"runtime"
5555
"strings"
5656
"sync"
57+
"sync/atomic"
5758
"time"
5859

5960
"golang.org/x/crypto/ssh"
@@ -80,10 +81,12 @@ func registerManagedSSH() error {
8081
}
8182

8283
func sshSmartSubtransportFactory(remote *git2go.Remote, transport *git2go.Transport) (git2go.SmartSubtransport, error) {
84+
var closed int32 = 0
8385
return &sshSmartSubtransport{
84-
transport: transport,
85-
ctx: context.Background(),
86-
logger: logr.Discard(),
86+
transport: transport,
87+
ctx: context.Background(),
88+
logger: logr.Discard(),
89+
closedSessions: &closed,
8790
}, nil
8891
}
8992

@@ -109,15 +112,12 @@ type sshSmartSubtransport struct {
109112
stdin io.WriteCloser
110113
stdout io.Reader
111114

112-
con connection
113-
}
115+
closedSessions *int32
114116

115-
type connection struct {
116117
client *ssh.Client
117118
session *ssh.Session
118119
currentStream *sshSmartSubtransportStream
119120
connected bool
120-
m sync.RWMutex
121121
}
122122

123123
func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.SmartServiceAction) (git2go.SmartSubtransportStream, error) {
@@ -151,17 +151,17 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
151151
var cmd string
152152
switch action {
153153
case git2go.SmartServiceActionUploadpackLs, git2go.SmartServiceActionUploadpack:
154-
if t.con.currentStream != nil {
154+
if t.currentStream != nil {
155155
if t.lastAction == git2go.SmartServiceActionUploadpackLs {
156-
return t.con.currentStream, nil
156+
return t.currentStream, nil
157157
}
158158
}
159159
cmd = fmt.Sprintf("git-upload-pack '%s'", uPath)
160160

161161
case git2go.SmartServiceActionReceivepackLs, git2go.SmartServiceActionReceivepack:
162-
if t.con.currentStream != nil {
162+
if t.currentStream != nil {
163163
if t.lastAction == git2go.SmartServiceActionReceivepackLs {
164-
return t.con.currentStream, nil
164+
return t.currentStream, nil
165165
}
166166
}
167167
cmd = fmt.Sprintf("git-receive-pack '%s'", uPath)
@@ -208,32 +208,30 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
208208
return nil
209209
}
210210

211-
t.con.m.RLock()
212-
if t.con.connected == true {
211+
if t.connected {
213212
// The connection is no longer shared across actions, so ensures
214213
// all has been released before starting a new connection.
215214
_ = t.Close()
216215
}
217-
t.con.m.RUnlock()
218216

219217
err = t.createConn(addr, sshConfig)
220218
if err != nil {
221219
return nil, err
222220
}
223221

224222
t.logger.V(logger.TraceLevel).Info("creating new ssh session")
225-
if t.con.session, err = t.con.client.NewSession(); err != nil {
223+
if t.session, err = t.client.NewSession(); err != nil {
226224
return nil, err
227225
}
228226

229-
if t.stdin, err = t.con.session.StdinPipe(); err != nil {
227+
if t.stdin, err = t.session.StdinPipe(); err != nil {
230228
return nil, err
231229
}
232230

233231
var w *io.PipeWriter
234232
var reader io.Reader
235233
t.stdout, w = io.Pipe()
236-
if reader, err = t.con.session.StdoutPipe(); err != nil {
234+
if reader, err = t.session.StdoutPipe(); err != nil {
237235
return nil, err
238236
}
239237

@@ -251,7 +249,6 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
251249
"recovered from libgit2 ssh smart subtransport panic")
252250
}
253251
}()
254-
255252
var cancel context.CancelFunc
256253
ctx := t.ctx
257254

@@ -261,19 +258,17 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
261258
defer cancel()
262259
}
263260

261+
closedAlready := atomic.LoadInt32(t.closedSessions)
264262
for {
265263
select {
266264
case <-ctx.Done():
267265
t.Close()
268266
return nil
269267

270268
default:
271-
t.con.m.RLock()
272-
if !t.con.connected {
273-
t.con.m.RUnlock()
269+
if atomic.LoadInt32(t.closedSessions) > closedAlready {
274270
return nil
275271
}
276-
t.con.m.RUnlock()
277272

278273
_, err := io.Copy(w, reader)
279274
if err != nil {
@@ -285,16 +280,16 @@ func (t *sshSmartSubtransport) Action(transportOptionsURL string, action git2go.
285280
}()
286281

287282
t.logger.V(logger.TraceLevel).Info("run on remote", "cmd", cmd)
288-
if err := t.con.session.Start(cmd); err != nil {
283+
if err := t.session.Start(cmd); err != nil {
289284
return nil, err
290285
}
291286

292287
t.lastAction = action
293-
t.con.currentStream = &sshSmartSubtransportStream{
288+
t.currentStream = &sshSmartSubtransportStream{
294289
owner: t,
295290
}
296291

297-
return t.con.currentStream, nil
292+
return t.currentStream, nil
298293
}
299294

300295
func (t *sshSmartSubtransport) createConn(addr string, sshConfig *ssh.ClientConfig) error {
@@ -311,10 +306,8 @@ func (t *sshSmartSubtransport) createConn(addr string, sshConfig *ssh.ClientConf
311306
return err
312307
}
313308

314-
t.con.m.Lock()
315-
t.con.connected = true
316-
t.con.client = ssh.NewClient(c, chans, reqs)
317-
t.con.m.Unlock()
309+
t.connected = true
310+
t.client = ssh.NewClient(c, chans, reqs)
318311

319312
return nil
320313
}
@@ -330,27 +323,27 @@ func (t *sshSmartSubtransport) createConn(addr string, sshConfig *ssh.ClientConf
330323
// SmartSubTransport (i.e. unreleased resources, staled connections).
331324
func (t *sshSmartSubtransport) Close() error {
332325
t.logger.V(logger.TraceLevel).Info("sshSmartSubtransport.Close()")
333-
t.con.m.Lock()
334-
defer t.con.m.Unlock()
335326

336-
t.con.currentStream = nil
337-
if t.con.client != nil && t.stdin != nil {
327+
t.currentStream = nil
328+
if t.client != nil && t.stdin != nil {
338329
_ = t.stdin.Close()
339330
}
340331
t.stdin = nil
341332

342-
if t.con.session != nil {
333+
if t.session != nil {
343334
t.logger.V(logger.TraceLevel).Info("session.Close()")
344-
_ = t.con.session.Close()
335+
_ = t.session.Close()
345336
}
346-
t.con.session = nil
337+
t.session = nil
347338

348-
if t.con.client != nil {
349-
_ = t.con.client.Close()
339+
if t.client != nil {
340+
_ = t.client.Close()
350341
t.logger.V(logger.TraceLevel).Info("close client")
351342
}
343+
t.client = nil
352344

353-
t.con.connected = false
345+
t.connected = false
346+
atomic.AddInt32(t.closedSessions, 1)
354347

355348
return nil
356349
}

0 commit comments

Comments
 (0)