-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Set of offset-based APIs for thread-safe file IO #53669
Changes from 72 commits
d37d1ee
539393d
b60e876
c61d639
ad252de
1540dba
d8a3113
02cd564
1430dd6
ebaa92a
fbbcd4d
7f94f85
33ecb8d
f1af8f6
a46da91
9ef23eb
485eb93
c8818aa
dc13de0
6c58252
fb24f5c
8baf0bd
50aba1f
143d31c
1d41d65
2d8ac90
bbbd267
46d9b6f
a946c5d
4f807ef
638e67a
fe8fbd1
381776b
1febba1
8db3f7a
f33d4ca
f8c8423
2126f15
e613e72
73f0751
940609c
891c2b3
e462d49
253faa1
fd86735
6241b08
648ab9b
e075441
b1d3006
98eaf24
76a4326
dcd9e12
2b6a555
4a6bb62
5ec9e1f
4d002b2
d5c9229
994b112
9405abe
752ff9d
e609c06
5ae1dfb
d4e5b09
abc4b14
043a926
fc76167
93db089
1014688
fde8c34
05327b1
05e65eb
c87354c
f80bbb7
07051fa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
internal unsafe struct IOVector | ||
{ | ||
public byte* Base; | ||
public UIntPtr Count; | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PRead", SetLastError = true)] | ||
internal static extern unsafe int PRead(SafeHandle fd, byte* buffer, int count, long fileOffset); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PReadV", SetLastError = true)] | ||
internal static extern unsafe long PReadV(SafeHandle fd, IOVector* vectors, int vectorCount, long fileOffset); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PWrite", SetLastError = true)] | ||
internal static extern unsafe int PWrite(SafeHandle fd, byte* buffer, int bufferSize, long fileOffset); | ||
adamsitnik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System.Runtime.InteropServices; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Sys | ||
{ | ||
[DllImport(Libraries.SystemNative, EntryPoint = "SystemNative_PWriteV", SetLastError = true)] | ||
internal static extern unsafe long PWriteV(SafeHandle fd, IOVector* vectors, int vectorCount, long fileOffset); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Runtime.InteropServices; | ||
using System.Threading; | ||
|
||
internal static partial class Interop | ||
{ | ||
internal static partial class Kernel32 | ||
{ | ||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
internal static extern unsafe int ReadFileScatter( | ||
SafeHandle handle, | ||
long* segments, | ||
int numBytesToRead, | ||
IntPtr reserved_mustBeZero, | ||
NativeOverlapped* overlapped); | ||
|
||
[DllImport(Libraries.Kernel32, SetLastError = true)] | ||
internal static extern unsafe int WriteFileGather( | ||
SafeHandle handle, | ||
long* segments, | ||
int numBytesToWrite, | ||
IntPtr reserved_mustBeZero, | ||
NativeOverlapped* overlapped); | ||
adamsitnik marked this conversation as resolved.
Show resolved
Hide resolved
|
||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
#include <sys/file.h> | ||
#include <sys/ioctl.h> | ||
#include <sys/socket.h> | ||
#include <sys/uio.h> | ||
#include <syslog.h> | ||
#include <termios.h> | ||
#include <unistd.h> | ||
|
@@ -1454,3 +1455,107 @@ int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStat | |
return -1; | ||
#endif // __sun | ||
} | ||
|
||
int32_t SystemNative_PRead(intptr_t fd, void* buffer, int32_t bufferSize, int64_t fileOffset) | ||
{ | ||
assert(buffer != NULL); | ||
assert(bufferSize >= 0); | ||
|
||
ssize_t count; | ||
while ((count = pread(ToFileDescriptor(fd), buffer, (uint32_t)bufferSize, (off_t)fileOffset)) < 0 && errno == EINTR); | ||
|
||
assert(count >= -1 && count <= bufferSize); | ||
return (int32_t)count; | ||
} | ||
|
||
int32_t SystemNative_PWrite(intptr_t fd, void* buffer, int32_t bufferSize, int64_t fileOffset) | ||
{ | ||
assert(buffer != NULL); | ||
assert(bufferSize >= 0); | ||
|
||
ssize_t count; | ||
while ((count = pwrite(ToFileDescriptor(fd), buffer, (uint32_t)bufferSize, (off_t)fileOffset)) < 0 && errno == EINTR); | ||
|
||
assert(count >= -1 && count <= bufferSize); | ||
return (int32_t)count; | ||
} | ||
|
||
int64_t SystemNative_PReadV(intptr_t fd, IOVector* vectors, int32_t vectorCount, int64_t fileOffset) | ||
{ | ||
assert(vectors != NULL); | ||
assert(vectorCount >= 0); | ||
|
||
int64_t count = 0; | ||
int fileDescriptor = ToFileDescriptor(fd); | ||
#if HAVE_PREADV && !defined(TARGET_WASM) // preadv is buggy on WASM | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there an issue to go along with that bug? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @stephentoub I've not reported it yet. @vargaz when I was testing The most suprising thing is that it happens in the native layer (source code) @vargaz where should I report the bug? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. |
||
while ((count = preadv(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR); | ||
#else | ||
int64_t current; | ||
for (int i = 0; i < vectorCount; i++) | ||
{ | ||
IOVector vector = vectors[i]; | ||
while ((current = pread(fileDescriptor, vector.Base, vector.Count, (off_t)(fileOffset + count))) < 0 && errno == EINTR); | ||
|
||
if (current < 0) | ||
{ | ||
// if previous calls were succesfull, we return what we got so far | ||
// otherwise, we return the error code | ||
return count > 0 ? count : current; | ||
} | ||
|
||
count += current; | ||
|
||
// Incomplete pread operation may happen for two reasons: | ||
// a) We have reached EOF. | ||
// b) The operation was interrupted by a signal handler. | ||
// To mimic preadv, we stop on the first incomplete operation. | ||
if (current != (int64_t)vector.Count) | ||
{ | ||
return count; | ||
} | ||
} | ||
#endif | ||
|
||
assert(count >= -1); | ||
return count; | ||
} | ||
|
||
int64_t SystemNative_PWriteV(intptr_t fd, IOVector* vectors, int32_t vectorCount, int64_t fileOffset) | ||
{ | ||
assert(vectors != NULL); | ||
assert(vectorCount >= 0); | ||
|
||
int64_t count = 0; | ||
int fileDescriptor = ToFileDescriptor(fd); | ||
#if HAVE_PWRITEV && !defined(TARGET_WASM) // pwritev is buggy on WASM | ||
while ((count = pwritev(fileDescriptor, (struct iovec*)vectors, (int)vectorCount, (off_t)fileOffset)) < 0 && errno == EINTR); | ||
#else | ||
int64_t current; | ||
for (int i = 0; i < vectorCount; i++) | ||
{ | ||
IOVector vector = vectors[i]; | ||
while ((current = pwrite(fileDescriptor, vector.Base, vector.Count, (off_t)(fileOffset + count))) < 0 && errno == EINTR); | ||
|
||
if (current < 0) | ||
{ | ||
// if previous calls were succesfull, we return what we got so far | ||
// otherwise, we return the error code | ||
return count > 0 ? count : current; | ||
} | ||
|
||
count += current; | ||
|
||
// Incomplete pwrite operation may happen for few reasons: | ||
// a) There was not enough space available or the file is too large for given file system. | ||
// b) The operation was interrupted by a signal handler. | ||
// To mimic pwritev, we stop on the first incomplete operation. | ||
if (current != (int64_t)vector.Count) | ||
{ | ||
return count; | ||
} | ||
} | ||
#endif | ||
|
||
assert(count >= -1); | ||
return count; | ||
} |
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -41,6 +41,14 @@ typedef struct | |||||||||||||
// add more fields when needed. | ||||||||||||||
} 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. | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we move them to here? runtime/src/libraries/Native/Unix/System.Native/pal_networking.c Lines 163 to 168 in c87354c
|
||||||||||||||
typedef struct | ||||||||||||||
{ | ||||||||||||||
uint8_t* Base; | ||||||||||||||
uintptr_t Count; | ||||||||||||||
} IOVector; | ||||||||||||||
|
||||||||||||||
/* Provide consistent access to nanosecond fields, if they exist. */ | ||||||||||||||
/* Seconds are always available through st_atime, st_mtime, st_ctime. */ | ||||||||||||||
|
||||||||||||||
|
@@ -730,3 +738,31 @@ PALEXPORT int32_t SystemNative_LChflagsCanSetHiddenFlag(void); | |||||||||||||
* Returns 1 if the process status was read; otherwise, 0. | ||||||||||||||
*/ | ||||||||||||||
PALEXPORT int32_t SystemNative_ReadProcessStatusInfo(pid_t pid, ProcessStatus* processStatus); | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Reads the number of bytes specified into the provided buffer from the specified, opened file descriptor at specified offset. | ||||||||||||||
* | ||||||||||||||
* Returns the number of bytes read on success; otherwise, -1 is returned an errno is set. | ||||||||||||||
*/ | ||||||||||||||
PALEXPORT int32_t SystemNative_PRead(intptr_t fd, void* buffer, int32_t bufferSize, int64_t fileOffset); | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Writes the number of bytes specified in the buffer into the specified, opened file descriptor at specified offset. | ||||||||||||||
* | ||||||||||||||
* Returns the number of bytes written on success; otherwise, -1 is returned an errno is set. | ||||||||||||||
*/ | ||||||||||||||
PALEXPORT int32_t SystemNative_PWrite(intptr_t fd, void* buffer, int32_t bufferSize, int64_t fileOffset); | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Reads the number of bytes specified into the provided buffers from the specified, opened file descriptor at specified offset. | ||||||||||||||
* | ||||||||||||||
* Returns the number of bytes read on success; otherwise, -1 is returned an errno is set. | ||||||||||||||
*/ | ||||||||||||||
PALEXPORT int64_t SystemNative_PReadV(intptr_t fd, IOVector* vectors, int32_t vectorCount, int64_t fileOffset); | ||||||||||||||
|
||||||||||||||
/** | ||||||||||||||
* Writes the number of bytes specified in the buffers into the specified, opened file descriptor at specified offset. | ||||||||||||||
* | ||||||||||||||
* Returns the number of bytes written on success; otherwise, -1 is returned an errno is set. | ||||||||||||||
*/ | ||||||||||||||
PALEXPORT int64_t SystemNative_PWriteV(intptr_t fd, IOVector* vectors, int32_t vectorCount, int64_t fileOffset); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: nuint