-
Notifications
You must be signed in to change notification settings - Fork 639
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Restore fsync behavior in FSDirectory via P/Invoke
This restores the commented-out fsync behavior in FSDirectory to help mitigate a performance regression in .NET 8.
- Loading branch information
Showing
5 changed files
with
300 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
using Lucene.Net.Util; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Lucene.Net.Support.IO | ||
{ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
internal static class PosixFsyncSupport | ||
{ | ||
// https://pubs.opengroup.org/onlinepubs/009695399/functions/fsync.html | ||
[DllImport("libc", SetLastError = true)] | ||
private static extern int fsync(int fd); | ||
|
||
// https://pubs.opengroup.org/onlinepubs/007904875/functions/open.html | ||
[DllImport("libc", SetLastError = true)] | ||
private static extern int open([MarshalAs(UnmanagedType.LPStr)] string pathname, int flags); | ||
|
||
// https://pubs.opengroup.org/onlinepubs/009604499/functions/close.html | ||
[DllImport("libc", SetLastError = true)] | ||
private static extern int close(int fd); | ||
|
||
// https://pubs.opengroup.org/onlinepubs/007904975/functions/fcntl.html | ||
// and https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/fcntl.2.html | ||
[DllImport("libc", SetLastError = true)] | ||
private static extern int fcntl(int fd, int cmd, int arg); | ||
|
||
private const int O_RDONLY = 0; | ||
private const int O_WRONLY = 1; | ||
|
||
// https://opensource.apple.com/source/xnu/xnu-6153.81.5/bsd/sys/fcntl.h.auto.html | ||
private const int F_FULLFSYNC = 51; | ||
|
||
public static void Fsync(string path, bool isDir) | ||
{ | ||
using DescriptorWrapper handle = new DescriptorWrapper(path, isDir); | ||
handle.Flush(); | ||
} | ||
|
||
private readonly ref struct DescriptorWrapper | ||
{ | ||
private readonly int fd; | ||
|
||
public DescriptorWrapper(string path, bool isDir) | ||
{ | ||
fd = open(path, isDir ? O_RDONLY : O_WRONLY); | ||
|
||
if (fd == -1) | ||
{ | ||
int error = Marshal.GetLastWin32Error(); | ||
throw new IOException($"Unable to open path, error: 0x{error:x8}", error); | ||
} | ||
} | ||
|
||
public void Flush() | ||
{ | ||
// if macOS, use F_FULLFSYNC | ||
if (Constants.MAC_OS_X) | ||
{ | ||
if (fcntl(fd, F_FULLFSYNC, 0) == -1) | ||
{ | ||
throw new IOException("fcntl failed", Marshal.GetLastWin32Error()); | ||
} | ||
} | ||
else if (fsync(fd) == -1) | ||
{ | ||
throw new IOException("fsync failed", Marshal.GetLastWin32Error()); | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
if (close(fd) == -1) | ||
{ | ||
throw new IOException("close failed", Marshal.GetLastWin32Error()); | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
using System; | ||
using System.IO; | ||
using System.Runtime.InteropServices; | ||
|
||
namespace Lucene.Net.Support.IO | ||
{ | ||
/* | ||
* Licensed to the Apache Software Foundation (ASF) under one or more | ||
* contributor license agreements. See the NOTICE file distributed with | ||
* this work for additional information regarding copyright ownership. | ||
* The ASF licenses this file to You under the Apache License, Version 2.0 | ||
* (the "License"); you may not use this file except in compliance with | ||
* the License. You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
public static class WindowsFsyncSupport | ||
{ | ||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-createfilew | ||
[DllImport("kernel32.dll", CharSet = CharSet.Unicode, SetLastError = true)] | ||
private static extern IntPtr CreateFileW( | ||
string lpFileName, | ||
uint dwDesiredAccess, | ||
uint dwShareMode, | ||
IntPtr lpSecurityAttributes, | ||
uint dwCreationDisposition, | ||
uint dwFlagsAndAttributes, | ||
IntPtr hTemplateFile | ||
); | ||
|
||
// https://learn.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-flushfilebuffers | ||
[DllImport("kernel32.dll", SetLastError = true)] | ||
private static extern bool FlushFileBuffers(IntPtr hFile); | ||
|
||
// https://learn.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle | ||
[DllImport("kernel32.dll", SetLastError = true)] | ||
private static extern bool CloseHandle(IntPtr hObject); | ||
|
||
private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); | ||
|
||
private const int ERROR_ACCESS_DENIED = 5; | ||
|
||
// https://learn.microsoft.com/en-us/windows/win32/secauthz/generic-access-rights | ||
private const int GENERIC_WRITE = 0x40000000; | ||
|
||
private const int FILE_SHARE_READ = 0x00000001; | ||
private const int FILE_SHARE_WRITE = 0x00000002; | ||
private const int FILE_SHARE_DELETE = 0x00000004; | ||
private const int FILE_FLAG_BACKUP_SEMANTICS = 0x02000000; | ||
private const int OPEN_EXISTING = 3; | ||
|
||
public static void Fsync(string path, bool isDir) | ||
{ | ||
using HandleWrapper handle = new HandleWrapper(path, isDir); | ||
handle.Flush(); | ||
} | ||
|
||
private readonly ref struct HandleWrapper | ||
{ | ||
private readonly IntPtr handle; | ||
|
||
public HandleWrapper(string path, bool isDir) | ||
{ | ||
handle = CreateFileW(path, | ||
GENERIC_WRITE, | ||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, | ||
IntPtr.Zero, | ||
OPEN_EXISTING, | ||
(uint)(isDir ? FILE_FLAG_BACKUP_SEMANTICS : 0), // FILE_FLAG_BACKUP_SEMANTICS required to open a directory | ||
IntPtr.Zero); | ||
|
||
if (handle == INVALID_HANDLE_VALUE) | ||
{ | ||
int error = Marshal.GetLastWin32Error(); | ||
throw new IOException($"Unable to open directory, error: 0x{error:x8}", error); | ||
} | ||
} | ||
|
||
public void Flush() | ||
{ | ||
if (!FlushFileBuffers(handle)) | ||
{ | ||
int error = Marshal.GetLastWin32Error(); | ||
|
||
if (error != ERROR_ACCESS_DENIED) | ||
{ | ||
// swallow ERROR_ACCESS_DENIED like in OpenJDK | ||
throw new IOException($"FlushFileBuffers failed, error: 0x{error:x8}", error); | ||
} | ||
} | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
if (!CloseHandle(handle)) | ||
{ | ||
int error = Marshal.GetLastWin32Error(); | ||
throw new IOException($"CloseHandle failed, error: 0x{error:x8}", error); | ||
} | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.