Skip to content

Commit 00e7550

Browse files
authored
Fix for deadlock in cluster: isMaster() globalsign#120 (globalsign#121)
Proposed fix for deadlock in globalsign#120
2 parents 7f9dd4f + 56f05b4 commit 00e7550

File tree

3 files changed

+52
-1
lines changed

3 files changed

+52
-1
lines changed

cluster.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ func (cluster *mongoCluster) isMaster(socket *mongoSocket, result *isMasterResul
181181
})
182182
})
183183

184-
err := session.Run(cmd, result)
184+
err := session.runOnSocket(socket, cmd, result)
185185
session.Close()
186186
return err
187187
}

cluster_test.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1964,6 +1964,41 @@ func (s *S) TestConnectCloseConcurrency(c *C) {
19641964
wg.Wait()
19651965
}
19661966

1967+
func (s *S) TestNoDeadlockOnClose(c *C) {
1968+
if *fast {
1969+
// Unfortunately I seem to need quite a high dial timeout to get this to work
1970+
// on my machine.
1971+
c.Skip("-fast")
1972+
}
1973+
1974+
var shouldStop int32
1975+
atomic.StoreInt32(&shouldStop, 0)
1976+
1977+
listener, err := net.Listen("tcp4", "127.0.0.1:")
1978+
c.Check(err, Equals, nil)
1979+
1980+
go func() {
1981+
for atomic.LoadInt32(&shouldStop) == 0 {
1982+
sock, err := listener.Accept()
1983+
if err != nil {
1984+
// Probs just closed
1985+
continue
1986+
}
1987+
sock.Close()
1988+
}
1989+
}()
1990+
defer func() {
1991+
atomic.StoreInt32(&shouldStop, 1)
1992+
listener.Close()
1993+
}()
1994+
1995+
session, err := mgo.DialWithTimeout(listener.Addr().String(), 10*time.Second)
1996+
// If execution reaches here, the deadlock did not happen and all is OK
1997+
if session != nil {
1998+
session.Close()
1999+
}
2000+
}
2001+
19672002
func (s *S) TestSelectServers(c *C) {
19682003
if !s.versionAtLeast(2, 2) {
19692004
c.Skip("read preferences introduced in 2.2")

session.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -848,6 +848,15 @@ func (db *Database) Run(cmd interface{}, result interface{}) error {
848848
return db.run(socket, cmd, result)
849849
}
850850

851+
// runOnSocket does the same as Run, but guarantees that your command will be run
852+
// on the provided socket instance; if it's unhealthy, you will receive the error
853+
// from it.
854+
func (db *Database) runOnSocket(socket *mongoSocket, cmd interface{}, result interface{}) error {
855+
socket.Acquire()
856+
defer socket.Release()
857+
return db.run(socket, cmd, result)
858+
}
859+
851860
// Credential holds details to authenticate with a MongoDB server.
852861
type Credential struct {
853862
// Username and Password hold the basic details for authentication.
@@ -2312,6 +2321,13 @@ func (s *Session) Run(cmd interface{}, result interface{}) error {
23122321
return s.DB("admin").Run(cmd, result)
23132322
}
23142323

2324+
// runOnSocket does the same as Run, but guarantees that your command will be run
2325+
// on the provided socket instance; if it's unhealthy, you will receive the error
2326+
// from it.
2327+
func (s *Session) runOnSocket(socket *mongoSocket, cmd interface{}, result interface{}) error {
2328+
return s.DB("admin").runOnSocket(socket, cmd, result)
2329+
}
2330+
23152331
// SelectServers restricts communication to servers configured with the
23162332
// given tags. For example, the following statement restricts servers
23172333
// used for reading operations to those with both tag "disk" set to

0 commit comments

Comments
 (0)