Use file descriptor I/O for process pipes
Use low level non-blocking I/O for process pipe streams. This may fix issues with data not getting through the pipe occasionally. Related: https://github.com/libsdl-org/SDL/issues/11006
This commit is contained in:
parent
f592a35439
commit
00385951a1
3 changed files with 171 additions and 26 deletions
|
@ -351,6 +351,164 @@ SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose)
|
||||||
}
|
}
|
||||||
#endif // defined(SDL_PLATFORM_WINDOWS)
|
#endif // defined(SDL_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
|
#if !defined(SDL_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
|
// Functions to read/write file descriptors. Not used for windows.
|
||||||
|
|
||||||
|
typedef struct IOStreamFDData
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
bool autoclose;
|
||||||
|
bool regular_file;
|
||||||
|
} IOStreamFDData;
|
||||||
|
|
||||||
|
static int SDL_fdatasync(int fd)
|
||||||
|
{
|
||||||
|
int result = 0;
|
||||||
|
|
||||||
|
#if defined(SDL_PLATFORM_APPLE) // Apple doesn't have fdatasync (rather, the symbol exists as an incompatible system call).
|
||||||
|
result = fcntl(fd, F_FULLFSYNC);
|
||||||
|
#elif defined(SDL_PLATFORM_HAIKU)
|
||||||
|
result = fsync(fd);
|
||||||
|
#elif defined(_POSIX_SYNCHRONIZED_IO) // POSIX defines this if fdatasync() exists, so we don't need a CMake test.
|
||||||
|
#ifndef SDL_PLATFORM_RISCOS // !!! FIXME: however, RISCOS doesn't have the symbol...maybe we need to link to an extra library or something?
|
||||||
|
result = fdatasync(fd);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static Sint64 SDLCALL fd_seek(void *userdata, Sint64 offset, SDL_IOWhence whence)
|
||||||
|
{
|
||||||
|
IOStreamFDData *iodata = (IOStreamFDData *) userdata;
|
||||||
|
int fdwhence;
|
||||||
|
|
||||||
|
switch (whence) {
|
||||||
|
case SDL_IO_SEEK_SET:
|
||||||
|
fdwhence = SEEK_SET;
|
||||||
|
break;
|
||||||
|
case SDL_IO_SEEK_CUR:
|
||||||
|
fdwhence = SEEK_CUR;
|
||||||
|
break;
|
||||||
|
case SDL_IO_SEEK_END:
|
||||||
|
fdwhence = SEEK_END;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
SDL_SetError("Unknown value for 'whence'");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
off_t result = lseek(iodata->fd, (off_t)offset, fdwhence);
|
||||||
|
if (result < 0) {
|
||||||
|
SDL_SetError("Couldn't get stream offset: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t SDLCALL fd_read(void *userdata, void *ptr, size_t size, SDL_IOStatus *status)
|
||||||
|
{
|
||||||
|
IOStreamFDData *iodata = (IOStreamFDData *) userdata;
|
||||||
|
ssize_t bytes;
|
||||||
|
do {
|
||||||
|
bytes = read(iodata->fd, ptr, size);
|
||||||
|
} while (bytes < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (bytes < 0) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
|
} else {
|
||||||
|
SDL_SetError("Error reading from datastream: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
return (size_t)bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static size_t SDLCALL fd_write(void *userdata, const void *ptr, size_t size, SDL_IOStatus *status)
|
||||||
|
{
|
||||||
|
IOStreamFDData *iodata = (IOStreamFDData *) userdata;
|
||||||
|
ssize_t bytes;
|
||||||
|
do {
|
||||||
|
bytes = write(iodata->fd, ptr, size);
|
||||||
|
} while (bytes < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (bytes < 0) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
*status = SDL_IO_STATUS_NOT_READY;
|
||||||
|
} else {
|
||||||
|
SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
bytes = 0;
|
||||||
|
}
|
||||||
|
return (size_t)bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SDLCALL fd_flush(void *userdata, SDL_IOStatus *status)
|
||||||
|
{
|
||||||
|
IOStreamFDData *iodata = (IOStreamFDData *) userdata;
|
||||||
|
int result;
|
||||||
|
do {
|
||||||
|
result = SDL_fdatasync(iodata->fd);
|
||||||
|
} while (result < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool SDLCALL fd_close(void *userdata)
|
||||||
|
{
|
||||||
|
IOStreamFDData *iodata = (IOStreamFDData *) userdata;
|
||||||
|
bool status = true;
|
||||||
|
if (iodata->autoclose) {
|
||||||
|
if (close(iodata->fd) < 0) {
|
||||||
|
status = SDL_SetError("Error closing datastream: %s", strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
SDL_free(iodata);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_IOStream *SDL_IOFromFD(int fd, bool autoclose)
|
||||||
|
{
|
||||||
|
IOStreamFDData *iodata = (IOStreamFDData *) SDL_calloc(1, sizeof (*iodata));
|
||||||
|
if (!iodata) {
|
||||||
|
if (autoclose) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
SDL_IOStreamInterface iface;
|
||||||
|
SDL_INIT_INTERFACE(&iface);
|
||||||
|
// There's no fd_size because SDL_GetIOSize emulates it the same way we'd do it for fd anyhow.
|
||||||
|
iface.seek = fd_seek;
|
||||||
|
iface.read = fd_read;
|
||||||
|
iface.write = fd_write;
|
||||||
|
iface.flush = fd_flush;
|
||||||
|
iface.close = fd_close;
|
||||||
|
|
||||||
|
iodata->fd = fd;
|
||||||
|
iodata->autoclose = autoclose;
|
||||||
|
|
||||||
|
struct stat st;
|
||||||
|
iodata->regular_file = ((fstat(fd, &st) == 0) && S_ISREG(st.st_mode));
|
||||||
|
|
||||||
|
SDL_IOStream *iostr = SDL_OpenIO(&iface, iodata);
|
||||||
|
if (!iostr) {
|
||||||
|
iface.close(iodata);
|
||||||
|
} else {
|
||||||
|
const SDL_PropertiesID props = SDL_GetIOProperties(iostr);
|
||||||
|
if (props) {
|
||||||
|
SDL_SetNumberProperty(props, SDL_PROP_IOSTREAM_FILE_DESCRIPTOR_NUMBER, fd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return iostr;
|
||||||
|
}
|
||||||
|
#endif // !defined(SDL_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
#if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
|
#if defined(HAVE_STDIO_H) && !defined(SDL_PLATFORM_WINDOWS)
|
||||||
|
|
||||||
// Functions to read/write stdio file pointers. Not used for windows.
|
// Functions to read/write stdio file pointers. Not used for windows.
|
||||||
|
@ -482,26 +640,15 @@ static bool SDLCALL stdio_flush(void *userdata, SDL_IOStatus *status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(SDL_PLATFORM_APPLE) // Apple doesn't have fdatasync (rather, the symbol exists as an incompatible system call).
|
int result;
|
||||||
if (fcntl(fileno(iodata->fp), F_FULLFSYNC) == -1) {
|
int fd = fileno(iodata->fp);
|
||||||
|
do {
|
||||||
|
result = SDL_fdatasync(fd);
|
||||||
|
} while (result < 0 && errno == EINTR);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
|
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
#elif defined(_POSIX_SYNCHRONIZED_IO) // POSIX defines this if fdatasync() exists, so we don't need a CMake test.
|
|
||||||
#ifndef SDL_PLATFORM_RISCOS // !!! FIXME: however, RISCOS doesn't have the symbol...maybe we need to link to an extra library or something?
|
|
||||||
if (iodata->regular_file) {
|
|
||||||
int sync_result;
|
|
||||||
#ifdef SDL_PLATFORM_HAIKU
|
|
||||||
sync_result = fsync(fileno(iodata->fp));
|
|
||||||
#else
|
|
||||||
sync_result = fdatasync(fileno(iodata->fp));
|
|
||||||
#endif
|
|
||||||
if (sync_result == -1) {
|
|
||||||
return SDL_SetError("Error flushing datastream: %s", strerror(errno));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -511,7 +658,7 @@ static bool SDLCALL stdio_close(void *userdata)
|
||||||
bool status = true;
|
bool status = true;
|
||||||
if (iodata->autoclose) {
|
if (iodata->autoclose) {
|
||||||
if (fclose(iodata->fp) != 0) {
|
if (fclose(iodata->fp) != 0) {
|
||||||
status = SDL_SetError("Error writing to datastream: %s", strerror(errno));
|
status = SDL_SetError("Error closing datastream: %s", strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SDL_free(iodata);
|
SDL_free(iodata);
|
||||||
|
|
|
@ -25,8 +25,11 @@
|
||||||
|
|
||||||
#if defined(SDL_PLATFORM_WINDOWS)
|
#if defined(SDL_PLATFORM_WINDOWS)
|
||||||
SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose);
|
SDL_IOStream *SDL_IOFromHandle(HANDLE handle, const char *mode, bool autoclose);
|
||||||
#elif defined(HAVE_STDIO_H)
|
#else
|
||||||
|
#if defined(HAVE_STDIO_H)
|
||||||
extern SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose);
|
extern SDL_IOStream *SDL_IOFromFP(FILE *fp, bool autoclose);
|
||||||
#endif
|
#endif
|
||||||
|
extern SDL_IOStream *SDL_IOFromFD(int fd, bool autoclose);
|
||||||
|
#endif
|
||||||
|
|
||||||
#endif // SDL_iostream_c_h_
|
#endif // SDL_iostream_c_h_
|
||||||
|
|
|
@ -57,12 +57,7 @@ static bool SetupStream(SDL_Process *process, int fd, const char *mode, const ch
|
||||||
// Set the file descriptor to non-blocking mode
|
// Set the file descriptor to non-blocking mode
|
||||||
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
|
fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);
|
||||||
|
|
||||||
FILE *fp = fdopen(fd, mode);
|
SDL_IOStream *io = SDL_IOFromFD(fd, true);
|
||||||
if (!fp) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
SDL_IOStream *io = SDL_IOFromFP(fp, true);
|
|
||||||
if (!io) {
|
if (!io) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue