Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Set of offset-based APIs for thread-safe file IO - #53669

Merged
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess
Jun 15, 2021
Merged

Set of offset-based APIs for thread-safe file IO#53669
adamsitnik merged 74 commits into
dotnet:mainfrom
adamsitnik:randomAcess

Conversation

@adamsitnik

Copy link
Copy Markdown
Member

I've moved all the logic responsible for opening and initializing SafeFileHandle to SafeFileHandle. A lot of duplicated code (mostly for initializing ThreadPoolBinding) got removed. On Unix, it's also now much more clear how many syscalls we perform to just open a file.

Other changes:

  • Instead of using NtCreateFile for cases when preallocationSize was specified and CreateFileW when not, I've followed @JeremyKuhne suggestion and switched to using NtCreateFile only. This has simplified the code and provided some minor perf gain. I've discovered a bug in the previous implementation (related to DesiredAccess.DELETE) fixed it, and added a test
  • On Unix, I've realized that once we call fstat() to ensure that a given file is not a directory we can also determine whether a given file is seekable or not. This has removed one syscall for the initialization of CanSeek.

Fixes#24847

@carlossanlop@jozkee@stephentoub PTAL. Once this PR is merged, I would like to finally publish the .NET Blog post.

cc @alexbudmsft

adamsitnikand others added 30 commits May 27, 2021 13:00
Co-authored-by: Stephen Toub <stoub@microsoft.com>
* with NtCreateFile there is no need for Validation (NtCreateFile returns error and we never create a safe file handle)
* unify status codes
* complete error mapping
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/File.netcoreapp.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 11, 2021 20:39
Co-authored-by: Stephen Toub <stoub@microsoft.com>
{
public byte* Base;
public UIntPtr Count;
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: nuint


int64_t count = 0;
int fileDescriptor = ToFileDescriptor(fd);
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's open an issue in runtime for now and include the link here.

} ProcessStatus;

// NOTE: the layout of this type is intended to exactly match the layout of a `struct iovec`. There are
// assertions in pal_networking.c that validate this.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we move them to here?

// We require that IOVector have the same layout as iovec.
c_static_assert(sizeof(IOVector) ==sizeof(iovec));
c_static_assert(sizeof_member(IOVector, Base) ==sizeof_member(iovec, iov_base));
c_static_assert(offsetof(IOVector, Base) == offsetof(iovec, iov_base));
c_static_assert(sizeof_member(IOVector, Count) ==sizeof_member(iovec, iov_len));
c_static_assert(offsetof(IOVector, Count) == offsetof(iovec, iov_len));

Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Unix.cs Outdated
Comment threadsrc/libraries/System.Private.CoreLib/src/System/IO/RandomAccess.Windows.cs Outdated
adamsitnikand others added 2 commits June 15, 2021 16:55
Co-authored-by: Stephen Toub <stoub@microsoft.com>
@adamsitnik
adamsitnik merged commit 9d771a2 into dotnet:mainJun 15, 2021
@janvorli

Copy link
Copy Markdown
Member

@adamsitnik this change has broken my local build on Apple Silicon. It fails with:

/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1491:21: error: implicit declaration of function 'preadv' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
/Users/janvorli/git/runtime2/src/libraries/Native/Unix/System.Native/pal_io.c:1531:21: error: implicit declaration of function 'pwritev' is invalid in C99 [-Werror,-Wimplicit-function-declaration]
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR);
^
2 errors generated.

However the checks for HAVE_PREADV and HAVE_PWRITEV in the configuration have succeeded. I suspect that the functions have moved to a different header file in recent SDK and the pal_io.c doesn't include it. But I am not sure yet.

@janvorli

Copy link
Copy Markdown
Member

Btw, this was a clean build after git clean -xdf.

@janvorli

Copy link
Copy Markdown
Member

I can see that the preadv / pwritew in my SDK are available only if:
#if (!defined(_POSIX_C_SOURCE) && !defined(_XOPEN_SOURCE)) || defined(_DARWIN_C_SOURCE)
Looking at the compiler options for the pal_io.c, we set the _XOPEN_SOURCE, so that blocks the preadv / pwritev.

@filipnavara

filipnavara commented Jun 15, 2021

Copy link
Copy Markdown
Member

@janvorli I suspect it may be the macOS version passed to the compiler as minimum version. The APIs were added in macOS 11. If the cake checks don't pass the target minimum macOS version it will likely use the latest and find the APIs. The actual compilation likely targets older macOS version and either compiles against older SDK or the SDK hides the symbols.

Nevermind, I see you already checked the headers meanwhile. Also I realized that the Apple Silicon version may target macOS 11 as minimum (unlike the x64 one).

@janvorli

Copy link
Copy Markdown
Member

Adding _DARWIN_C_SOURCE definition for the System.Native (for OSX target) fixed the build for me.

Sign up for freeto subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Async File IO APIs mimicking Win32 OVERLAPPED

10 participants

@adamsitnik@akoeplinger@vargaz@janvorli@filipnavara@alexrp@GrabYourPitchforks@stephentoub@jkotas@JeremyKuhne