comparison mercurial/win32.py @ 20528:1a9ebc83a74c

win32: improve the performance of win32.unlink() over CIFS Emulating POSIX unlink() behavior with os.rename() and os.unlink() is often slow especially over CIFS from Windows clients due to its protocol overhead. This patch changes win32.unlink() to try first an exclusive open with the Win32 delete-on-close flag, and if a sharing violation is detected, to fall back to the original emulation. This patch also removes a test with os.path.isdir() since we expect opening a directory shall fail as os.unlink() would. Example measurements (repeated 3-times after 1-time calibration): (Without this patch: hg update from null to default) 127 files updated, 0 files merged, 0 files removed, 0 files unresolved time: real 19.871 secs (user 0.328+0.000 sys 1.794+0.000) time: real 19.622 secs (user 0.312+0.000 sys 2.044+0.000) time: real 19.138 secs (user 0.250+0.000 sys 1.872+0.000) (Without this patch: hg update from default to null) 0 files updated, 0 files merged, 127 files removed, 0 files unresolved time: real 35.158 secs (user 0.156+0.000 sys 2.512+0.000) time: real 35.272 secs (user 0.250+0.000 sys 2.512+0.000) time: real 36.569 secs (user 0.203+0.000 sys 2.387+0.000) (With this patch: hg update from null to default) 127 files updated, 0 files merged, 0 files removed, 0 files unresolved time: real 17.893 secs (user 0.328+0.000 sys 1.700+0.000) time: real 18.512 secs (user 0.265+0.000 sys 1.529+0.000) time: real 20.238 secs (user 0.312+0.000 sys 1.685+0.000) (With this patch: hg update from default to null) 0 files updated, 0 files merged, 127 files removed, 0 files unresolved time: real 12.312 secs (user 0.250+0.000 sys 0.811+0.000) time: real 12.471 secs (user 0.156+0.000 sys 0.889+0.000) time: real 9.727 secs (user 0.125+0.000 sys 0.858+0.000)
author Kaz Nishimura <kazssym@vx68k.org>
date Thu, 17 Oct 2013 13:27:17 +0900
parents ca6aa8362f33
children 07f9825865de
comparison
equal deleted inserted replaced
20527:bde426f18e0a 20528:1a9ebc83a74c
22 22
23 _INVALID_HANDLE_VALUE = _HANDLE(-1).value 23 _INVALID_HANDLE_VALUE = _HANDLE(-1).value
24 24
25 # GetLastError 25 # GetLastError
26 _ERROR_SUCCESS = 0 26 _ERROR_SUCCESS = 0
27 _ERROR_SHARING_VIOLATION = 32
27 _ERROR_INVALID_PARAMETER = 87 28 _ERROR_INVALID_PARAMETER = 87
28 _ERROR_INSUFFICIENT_BUFFER = 122 29 _ERROR_INSUFFICIENT_BUFFER = 122
29 30
30 # WPARAM is defined as UINT_PTR (unsigned type) 31 # WPARAM is defined as UINT_PTR (unsigned type)
31 # LPARAM is defined as LONG_PTR (signed type) 32 # LPARAM is defined as LONG_PTR (signed type)
57 _FILE_SHARE_WRITE = 0x00000002 58 _FILE_SHARE_WRITE = 0x00000002
58 _FILE_SHARE_DELETE = 0x00000004 59 _FILE_SHARE_DELETE = 0x00000004
59 60
60 _OPEN_EXISTING = 3 61 _OPEN_EXISTING = 3
61 62
63 _FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000
62 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000 64 _FILE_FLAG_BACKUP_SEMANTICS = 0x02000000
65 _FILE_FLAG_DELETE_ON_CLOSE = 0x04000000
63 66
64 # SetFileAttributes 67 # SetFileAttributes
65 _FILE_ATTRIBUTE_NORMAL = 0x80 68 _FILE_ATTRIBUTE_NORMAL = 0x80
66 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000 69 _FILE_ATTRIBUTE_NOT_CONTENT_INDEXED = 0x2000
67 70
418 return _tochildpid(pi.dwProcessId) 421 return _tochildpid(pi.dwProcessId)
419 422
420 def unlink(f): 423 def unlink(f):
421 '''try to implement POSIX' unlink semantics on Windows''' 424 '''try to implement POSIX' unlink semantics on Windows'''
422 425
423 if os.path.isdir(f): 426 # If we can open f exclusively, no other processes must have open handles
424 # use EPERM because it is POSIX prescribed value, even though 427 # for it and we can expect its name will be deleted immediately when we
425 # unlink(2) on directories returns EISDIR on Linux 428 # close the handle unless we have another in the same process. We also
426 raise IOError(errno.EPERM, 429 # expect we shall simply fail to open f if it is a directory.
427 "Unlinking directory not permitted: '%s'" % f) 430 fh = _kernel32.CreateFileA(f, 0, 0, None, _OPEN_EXISTING,
431 _FILE_FLAG_OPEN_REPARSE_POINT | _FILE_FLAG_DELETE_ON_CLOSE, None)
432 if fh != _INVALID_HANDLE_VALUE:
433 _kernel32.CloseHandle(fh)
434 return
435 error = _kernel32.GetLastError()
436 if error != _ERROR_SHARING_VIOLATION:
437 raise ctypes.WinError(error)
428 438
429 # POSIX allows to unlink and rename open files. Windows has serious 439 # POSIX allows to unlink and rename open files. Windows has serious
430 # problems with doing that: 440 # problems with doing that:
431 # - Calling os.unlink (or os.rename) on a file f fails if f or any 441 # - Calling os.unlink (or os.rename) on a file f fails if f or any
432 # hardlinked copy of f has been opened with Python's open(). There is no 442 # hardlinked copy of f has been opened with Python's open(). There is no