Make lopen() atomic on linux (if the kernel supports O_CLOEXEC)

This commit is contained in:
Kovid Goyal 2013-11-29 16:07:55 +05:30
parent a5e071d091
commit bee4b2b9c7

View File

@ -16,7 +16,7 @@ __builtin__.__dict__['_'] = lambda s: s
# immediately translated to the environment language # immediately translated to the environment language
__builtin__.__dict__['__'] = lambda s: s __builtin__.__dict__['__'] = lambda s: s
from calibre.constants import iswindows, preferred_encoding, plugins, isosx from calibre.constants import iswindows, preferred_encoding, plugins, isosx, islinux
_run_once = False _run_once = False
winutil = winutilerror = None winutil = winutilerror = None
@ -24,7 +24,7 @@ winutil = winutilerror = None
if not _run_once: if not _run_once:
_run_once = True _run_once = True
################################################################################ #
# Platform specific modules # Platform specific modules
if iswindows: if iswindows:
winutil, winutilerror = plugins['winutil'] winutil, winutilerror = plugins['winutil']
@ -33,12 +33,12 @@ if not _run_once:
if len(sys.argv) > 1 and not isinstance(sys.argv[1], unicode): if len(sys.argv) > 1 and not isinstance(sys.argv[1], unicode):
sys.argv[1:] = winutil.argv()[1-len(sys.argv):] sys.argv[1:] = winutil.argv()[1-len(sys.argv):]
################################################################################ #
# Ensure that all temp files/dirs are created under a calibre tmp dir # Ensure that all temp files/dirs are created under a calibre tmp dir
from calibre.ptempfile import base_dir from calibre.ptempfile import base_dir
base_dir() base_dir()
################################################################################ #
# Convert command line arguments to unicode # Convert command line arguments to unicode
enc = preferred_encoding enc = preferred_encoding
if isosx: if isosx:
@ -52,19 +52,18 @@ if not _run_once:
if not isinstance(sys.argv[i], unicode): if not isinstance(sys.argv[i], unicode):
sys.argv[i] = sys.argv[i].decode(enc, 'replace') sys.argv[i] = sys.argv[i].decode(enc, 'replace')
################################################################################ #
# Setup resources # Setup resources
import calibre.utils.resources as resources import calibre.utils.resources as resources
resources resources
#
################################################################################
# Setup translations # Setup translations
from calibre.utils.localization import set_translators from calibre.utils.localization import set_translators
set_translators() set_translators()
################################################################################ #
# Initialize locale # Initialize locale
# Import string as we do not want locale specific # Import string as we do not want locale specific
# string.whitespace/printable, on windows especially, this causes problems. # string.whitespace/printable, on windows especially, this causes problems.
@ -82,7 +81,7 @@ if not _run_once:
except: except:
pass pass
################################################################################ #
def local_open(name, mode='r', bufsize=-1): def local_open(name, mode='r', bufsize=-1):
''' '''
@ -93,6 +92,7 @@ if not _run_once:
''' '''
if iswindows: if iswindows:
class fwrapper(object): class fwrapper(object):
def __init__(self, name, fobject): def __init__(self, name, fobject):
object.__setattr__(self, 'fobject', fobject) object.__setattr__(self, 'fobject', fobject)
object.__setattr__(self, 'name', name) object.__setattr__(self, 'name', name)
@ -129,7 +129,6 @@ if not _run_once:
fobject = object.__getattribute__(self, 'fobject') fobject = object.__getattribute__(self, 'fobject')
return fobject.__exit__(*args) return fobject.__exit__(*args)
m = mode[0] m = mode[0]
random = len(mode) > 1 and mode[1] == '+' random = len(mode) > 1 and mode[1] == '+'
binary = mode[-1] == 'b' binary = mode[-1] == 'b'
@ -162,8 +161,15 @@ if not _run_once:
cloexec_flag = fcntl.FD_CLOEXEC cloexec_flag = fcntl.FD_CLOEXEC
except AttributeError: except AttributeError:
cloexec_flag = 1 cloexec_flag = 1
# Python 2.x uses fopen which on recent glibc/linux kernel at least
# respects the 'e' mode flag. On OS X the e is ignored. So to try
# to get atomicity where possible we pass 'e' and then only use
# fcntl only if CLOEXEC was not set.
if islinux:
mode += 'e'
ans = open(name, mode, bufsize) ans = open(name, mode, bufsize)
old = fcntl.fcntl(ans, fcntl.F_GETFD) old = fcntl.fcntl(ans, fcntl.F_GETFD)
if not (old & cloexec_flag):
fcntl.fcntl(ans, fcntl.F_SETFD, old | cloexec_flag) fcntl.fcntl(ans, fcntl.F_SETFD, old | cloexec_flag)
return ans return ans