Skip to content

Commit

Permalink
Merge pull request benoitc#1220 from benoitc/fix/host-restart-uds
Browse files Browse the repository at this point in the history
Fix/host restart uds
  • Loading branch information
tilgovi committed Mar 13, 2016
2 parents d4b3883 + 45d6330 commit 8e2415a
Show file tree
Hide file tree
Showing 2 changed files with 69 additions and 2 deletions.
15 changes: 13 additions & 2 deletions gunicorn/sock.py
Expand Up @@ -4,6 +4,7 @@
# See the NOTICE for more information.

import errno
import fcntl
import os
import socket
import stat
Expand Down Expand Up @@ -105,6 +106,8 @@ def __init__(self, addr, conf, log, fd=None):
raise ValueError("%r is not a socket" % addr)
self.parent = os.getpid()
super(UnixSocket, self).__init__(addr, conf, log, fd=fd)
# each arbiter grabs a shared lock on the unix socket.
fcntl.lockf(self.sock, fcntl.LOCK_SH | fcntl.LOCK_NB)

def __str__(self):
return "unix:%s" % self.cfg_addr
Expand All @@ -117,9 +120,17 @@ def bind(self, sock):


def close(self):
super(UnixSocket, self).close()
if self.parent == os.getpid():
os.unlink(self.cfg_addr)
# attempt to acquire an exclusive lock on the unix socket.
# if we're the only arbiter running, the lock will succeed, and
# we can safely rm the socket.
try:
fcntl.lockf(self.sock, fcntl.LOCK_EX | fcntl.LOCK_NB)
except:
pass
else:
os.unlink(self.cfg_addr)
super(UnixSocket, self).close()


def _sock_type(addr):
Expand Down
56 changes: 56 additions & 0 deletions tests/test_sock.py
@@ -0,0 +1,56 @@
import fcntl

try:
import unittest.mock as mock
except ImportError:
import mock

from gunicorn import sock


@mock.patch('fcntl.lockf')
@mock.patch('socket.fromfd')
def test_unix_socket_init_lock(fromfd, lockf):
s = fromfd.return_value
sock.UnixSocket('test.sock', mock.Mock(), mock.Mock(), mock.Mock())
lockf.assert_called_with(s, fcntl.LOCK_SH | fcntl.LOCK_NB)


@mock.patch('fcntl.lockf')
@mock.patch('os.getpid')
@mock.patch('os.unlink')
@mock.patch('socket.fromfd')
def test_unix_socket_close_delete_if_exlock(fromfd, unlink, getpid, lockf):
s = fromfd.return_value
gsock = sock.UnixSocket('test.sock', mock.Mock(), mock.Mock(), mock.Mock())
lockf.reset_mock()
gsock.close()
lockf.assert_called_with(s, fcntl.LOCK_EX | fcntl.LOCK_NB)
unlink.assert_called_with('test.sock')


@mock.patch('fcntl.lockf')
@mock.patch('os.getpid')
@mock.patch('os.unlink')
@mock.patch('socket.fromfd')
def test_unix_socket_close_keep_if_no_exlock(fromfd, unlink, getpid, lockf):
s = fromfd.return_value
gsock = sock.UnixSocket('test.sock', mock.Mock(), mock.Mock(), mock.Mock())
lockf.reset_mock()
lockf.side_effect = IOError('locked')
gsock.close()
lockf.assert_called_with(s, fcntl.LOCK_EX | fcntl.LOCK_NB)
unlink.assert_not_called()


@mock.patch('fcntl.lockf')
@mock.patch('os.getpid')
@mock.patch('socket.fromfd')
def test_unix_socket_not_deleted_by_worker(fromfd, getpid, lockf):
fd = mock.Mock()
gsock = sock.UnixSocket('name', mock.Mock(), mock.Mock(), fd)
lockf.reset_mock()
getpid.reset_mock()
getpid.return_value = mock.Mock() # fake a pid change
gsock.close()
lockf.assert_not_called()

0 comments on commit 8e2415a

Please sign in to comment.