From 3c8dca55c43b78c0564ad0ff93d67587a7a64bf8 Mon Sep 17 00:00:00 2001 From: usa Date: Thu, 28 Aug 2008 12:46:58 +0000 Subject: * win32/win32.c, include/ruby/win32.h (rb_w32_open): overlapped file I/O support. * win32/win32.c, include/ruby/win32.h (rb_w32_pipe): overlapped pipe I/O support. * wn32/win32.c (rb_w32_read, rb_w32_write): overlapped I/O support to enable canceling I/O. * thread_win32.c (ubf_handle): remove workaround. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18897 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 13 ++ include/ruby/win32.h | 5 +- thread_win32.c | 9 - win32/win32.c | 457 ++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 465 insertions(+), 19 deletions(-) diff --git a/ChangeLog b/ChangeLog index 7c8a8c8cdd..97e0297be0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +Thu Aug 28 21:43:05 2008 NAKAMURA Usaku + + * win32/win32.c, include/ruby/win32.h (rb_w32_open): overlapped file + I/O support. + + * win32/win32.c, include/ruby/win32.h (rb_w32_pipe): overlapped pipe + I/O support. + + * wn32/win32.c (rb_w32_read, rb_w32_write): overlapped I/O support to + enable canceling I/O. + + * thread_win32.c (ubf_handle): remove workaround. + Thu Aug 28 20:22:49 2008 Yukihiro Matsumoto * vm_insnhelper.c (vm_yield_setup_args): object with to_ary should diff --git a/include/ruby/win32.h b/include/ruby/win32.h index 41fe48a197..19e5030d92 100644 --- a/include/ruby/win32.h +++ b/include/ruby/win32.h @@ -134,7 +134,8 @@ extern DWORD rb_w32_osid(void); #define utime(_p, _t) rb_w32_utime(_p, _t) #define lseek(_f, _o, _w) _lseeki64(_f, _o, _w) -#define pipe(p) _pipe(p, 65536L, _O_NOINHERIT) +#define pipe(p) rb_w32_pipe(p) +#define open rb_w32_open #define close(h) rb_w32_close(h) #define fclose(f) rb_w32_fclose(f) #define read(f, b, s) rb_w32_read(f, b, s) @@ -543,8 +544,10 @@ HANDLE GetCurrentThreadHandle(void); int rb_w32_sleep(unsigned long msec); int rb_w32_putc(int, FILE*); int rb_w32_getc(FILE*); +int rb_w32_open(const char *, int, ...); int rb_w32_close(int); int rb_w32_fclose(FILE*); +int rb_w32_pipe(int[2]); size_t rb_w32_read(int, void *, size_t); size_t rb_w32_write(int, const void *, size_t); int rb_w32_utime(const char *, const struct utimbuf *); diff --git a/thread_win32.c b/thread_win32.c index eaff139be9..46780d8ff4 100644 --- a/thread_win32.c +++ b/thread_win32.c @@ -524,18 +524,9 @@ static void ubf_handle(void *ptr) { typedef BOOL (WINAPI *cancel_io_func_t)(HANDLE); - static cancel_io_func_t cancel_func = NULL; rb_thread_t *th = (rb_thread_t *)ptr; thread_debug("ubf_handle: %p\n", th); - if (!cancel_func) { - cancel_func = (cancel_io_func_t)GetProcAddress(GetModuleHandle("kernel32"), "CancelSynchronousIo"); - if (!cancel_func) - cancel_func = (cancel_io_func_t)-1; - } - if (cancel_func != (cancel_io_func_t)-1) - cancel_func((HANDLE)th->thread_id); - w32_set_event(th->native_thread_data.interrupt_event); } diff --git a/win32/win32.c b/win32/win32.c index 29088d2f29..8827f3b593 100644 --- a/win32/win32.c +++ b/win32/win32.c @@ -1736,6 +1736,8 @@ set_pioinfo_extra(void) #define _set_osflags(fh, flags) (_osfile(fh) = (flags)) #define FOPEN 0x01 /* file handle open */ +#define FEOFLAG 0x02 /* end of file has been encountered */ +#define FPIPE 0x08 /* file handle refers to a pipe */ #define FNOINHERIT 0x10 /* file handle opened O_NOINHERIT */ #define FAPPEND 0x20 /* file handle opened O_APPEND */ #define FDEV 0x40 /* file handle refers to device */ @@ -3994,6 +3996,167 @@ rb_w32_getppid(void) return ppid; } +int +rb_w32_open(const char *file, int oflag, ...) +{ + char flags = 0; + int fd; + DWORD access; + DWORD create; + DWORD attr = FILE_ATTRIBUTE_NORMAL; + SECURITY_ATTRIBUTES sec; + HANDLE h; + + sec.nLength = sizeof(sec); + sec.lpSecurityDescriptor = NULL; + if (oflag & O_NOINHERIT) { + sec.bInheritHandle = FALSE; + flags |= FNOINHERIT; + } + else { + sec.bInheritHandle = TRUE; + } + oflag &= ~O_NOINHERIT; + + /* always open with binary mode */ + oflag &= ~(O_BINARY | O_TEXT); + + switch (oflag & (O_RDWR | O_RDONLY | O_WRONLY)) { + case O_RDWR: + access = GENERIC_READ | GENERIC_WRITE; + break; + case O_RDONLY: + access = GENERIC_READ; + break; + case O_WRONLY: + access = GENERIC_WRITE; + break; + default: + errno = EINVAL; + return -1; + } + oflag &= ~(O_RDWR | O_RDONLY | O_WRONLY); + + switch (oflag & (O_CREAT | O_EXCL | O_TRUNC)) { + case O_CREAT: + create = OPEN_ALWAYS; + break; + case 0: + case O_EXCL: + create = OPEN_EXISTING; + break; + case O_CREAT | O_EXCL: + case O_CREAT | O_EXCL | O_TRUNC: + create = CREATE_NEW; + break; + case O_TRUNC: + case O_TRUNC | O_EXCL: + create = TRUNCATE_EXISTING; + break; + case O_CREAT | O_TRUNC: + create = CREATE_ALWAYS; + break; + default: + errno = EINVAL; + return -1; + } + if (oflag & O_CREAT) { + va_list arg; + int pmode; + va_start(arg, oflag); + pmode = va_arg(arg, int); + va_end(arg); + /* TODO: we need to check umask here, but it's not exported... */ + if (!(pmode & S_IWRITE)) + attr = FILE_ATTRIBUTE_READONLY; + } + oflag &= ~(O_CREAT | O_EXCL | O_TRUNC); + + if (oflag & O_TEMPORARY) { + attr |= FILE_FLAG_DELETE_ON_CLOSE; + access |= DELETE; + } + oflag &= ~O_TEMPORARY; + + if (oflag & _O_SHORT_LIVED) + attr |= FILE_ATTRIBUTE_TEMPORARY; + oflag &= ~_O_SHORT_LIVED; + + switch (oflag & (O_SEQUENTIAL | O_RANDOM)) { + case 0: + break; + case O_SEQUENTIAL: + attr |= FILE_FLAG_SEQUENTIAL_SCAN; + break; + case O_RANDOM: + attr |= FILE_FLAG_RANDOM_ACCESS; + break; + default: + errno = EINVAL; + return -1; + } + oflag &= ~(O_SEQUENTIAL | O_RANDOM); + + if (oflag & ~O_APPEND) { + errno = EINVAL; + return -1; + } + + /* allocate a C Runtime file handle */ + RUBY_CRITICAL({ + h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL); + fd = _open_osfhandle((long)h, 0); + CloseHandle(h); + }); + if (fd == -1) { + errno = EMFILE; + return -1; + } + RUBY_CRITICAL({ + MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock))); + _set_osfhnd(fd, (long)INVALID_HANDLE_VALUE); + _set_osflags(fd, 0); + + /* open with FILE_FLAG_OVERLAPPED if have CancelIo */ + if (cancel_io) + attr |= FILE_FLAG_OVERLAPPED; + h = CreateFile(file, access, FILE_SHARE_READ | FILE_SHARE_WRITE, &sec, + create, attr, NULL); + if (h == INVALID_HANDLE_VALUE) { + errno = map_errno(GetLastError()); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + fd = -1; + goto quit; + } + + switch (GetFileType(h)) { + case FILE_TYPE_CHAR: + flags |= FDEV; + break; + case FILE_TYPE_PIPE: + flags |= FPIPE; + break; + case FILE_TYPE_UNKNOWN: + errno = map_errno(GetLastError()); + CloseHandle(h); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + fd = -1; + goto quit; + } + if (!(flags & (FDEV | FPIPE)) && (oflag & O_APPEND)) + flags |= FAPPEND; + + _set_osfhnd(fd, (long)h); + _osfile(fd) = flags | FOPEN; + + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + quit: + ; + }); + + return fd; +} + int rb_w32_fclose(FILE *fp) { @@ -4016,6 +4179,98 @@ rb_w32_fclose(FILE *fp) return 0; } +int +rb_w32_pipe(int fds[2]) +{ + static DWORD serial = 0; + char name[] = "\\\\.\\pipe\\ruby0000000000000000-0000000000000000"; + char *p; + SECURITY_ATTRIBUTES sec; + HANDLE hRead, hWrite, h; + int fdRead, fdWrite; + int ret; + + /* if doesn't have CancelIo, use default pipe function */ + if (!cancel_io) + return _pipe(fds, 65536L, _O_NOINHERIT); + + p = strchr(name, '0'); + snprintf(p, strlen(p) + 1, "%x-%x", rb_w32_getpid(), serial++); + + sec.nLength = sizeof(sec); + sec.lpSecurityDescriptor = NULL; + sec.bInheritHandle = FALSE; + + RUBY_CRITICAL({ + hRead = CreateNamedPipe(name, PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED, + 0, 2, 65536, 65536, 0, &sec); + }); + if (hRead == INVALID_HANDLE_VALUE) { + DWORD err = GetLastError(); + if (err == ERROR_PIPE_BUSY) + errno = EMFILE; + else + errno = map_errno(GetLastError()); + return -1; + } + + RUBY_CRITICAL({ + hWrite = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, &sec, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + }); + if (hWrite == INVALID_HANDLE_VALUE) { + errno = map_errno(GetLastError()); + CloseHandle(hRead); + return -1; + } + + RUBY_CRITICAL(do { + ret = 0; + h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL); + fdRead = _open_osfhandle((long)h, 0); + CloseHandle(h); + if (fdRead == -1) { + errno = EMFILE; + CloseHandle(hWrite); + CloseHandle(hRead); + ret = -1; + break; + } + + MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdRead)->lock))); + _set_osfhnd(fdRead, (long)hRead); + _set_osflags(fdRead, FOPEN | FPIPE | FNOINHERIT); + MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdRead)->lock))); + } while (0)); + if (ret) + return ret; + + RUBY_CRITICAL(do { + h = CreateFile("NUL", 0, 0, NULL, OPEN_ALWAYS, 0, NULL); + fdWrite = _open_osfhandle((long)h, 0); + CloseHandle(h); + if (fdWrite == -1) { + errno = EMFILE; + CloseHandle(hWrite); + ret = -1; + break; + } + MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fdWrite)->lock))); + _set_osfhnd(fdWrite, (long)hWrite); + _set_osflags(fdWrite, FOPEN | FPIPE | FNOINHERIT); + MTHREAD_ONLY(LeaveCriticalSection(&(_pioinfo(fdWrite)->lock))); + } while (0)); + if (ret) { + rb_w32_close(fdRead); + return ret; + } + + fds[0] = fdRead; + fds[1] = fdWrite; + + return 0; +} + int rb_w32_close(int fd) { @@ -4045,11 +4300,107 @@ size_t rb_w32_read(int fd, void *buf, size_t size) { SOCKET sock = TO_SOCKET(fd); + DWORD read; + DWORD wait; + DWORD err; + OVERLAPPED ol, *pol = NULL; - if (!is_socket(sock)) - return read(fd, buf, size); - else + if (is_socket(sock)) return rb_w32_recv(fd, buf, size, 0); + + if (!(_osfile(fd) & FOPEN)) { + errno = EBADF; + return -1; + } + + MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock))); + + if (!size || _osfile(fd) & FEOFLAG) { + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return 0; + } + + /* if have cancel_io, use Overlapped I/O */ + if (cancel_io) { + memset(&ol, 0, sizeof(ol)); + if (!(_osfile(fd) & (FDEV | FPIPE))) { + LONG high = 0; + DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, + FILE_CURRENT); +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + if (low == INVALID_SET_FILE_POINTER) { + errno = map_errno(GetLastError()); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + ol.Offset = low; + ol.OffsetHigh = high; + } + ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (!ol.hEvent) { + errno = map_errno(GetLastError()); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + + pol = &ol; + } + + if (!ReadFile((HANDLE)_osfhnd(fd), buf, size, &read, pol)) { + err = GetLastError(); + if (err != ERROR_IO_PENDING) { + if (err == ERROR_ACCESS_DENIED) + errno = EBADF; + else if (err == ERROR_BROKEN_PIPE) { + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return 0; + } + else + errno = map_errno(err); + + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + + if (pol) { + wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE); + if (wait != WAIT_OBJECT_0) { + if (errno != EINTR) + errno = map_errno(GetLastError()); + CloseHandle(ol.hEvent); + cancel_io((HANDLE)_osfhnd(fd)); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + + if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &read, TRUE) && + (err = GetLastError()) != ERROR_HANDLE_EOF) { + errno = map_errno(err); + CloseHandle(ol.hEvent); + cancel_io((HANDLE)_osfhnd(fd)); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + } + } + + if (pol) { + CloseHandle(ol.hEvent); + + if (!(_osfile(fd) & (FDEV | FPIPE))) { + LONG high = ol.OffsetHigh; + LONG low = ol.Offset + read; + if (low < ol.Offset) + ++high; + SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN); + } + } + + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + + return read; } #undef write @@ -4057,15 +4408,103 @@ size_t rb_w32_write(int fd, const void *buf, size_t size) { SOCKET sock = TO_SOCKET(fd); + DWORD written; + DWORD wait; + DWORD err; + OVERLAPPED ol, *pol = NULL; - if (!is_socket(sock)) { - size_t ret = write(fd, buf, size); - if ((int)ret < 0 && errno == EINVAL) + if (is_socket(sock)) + return rb_w32_send(fd, buf, size, 0); + + if (!(_osfile(fd) & FOPEN)) { + errno = EBADF; + return -1; + } + + MTHREAD_ONLY(EnterCriticalSection(&(_pioinfo(fd)->lock))); + + if (!size || _osfile(fd) & FEOFLAG) { + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return 0; + } + + /* if have cancel_io, use Overlapped I/O */ + if (cancel_io) { + memset(&ol, 0, sizeof(ol)); + if (!(_osfile(fd) & (FDEV | FPIPE))) { + LONG high = 0; + DWORD low = SetFilePointer((HANDLE)_osfhnd(fd), 0, &high, + FILE_CURRENT); +#ifndef INVALID_SET_FILE_POINTER +#define INVALID_SET_FILE_POINTER ((DWORD)-1) +#endif + if (low == INVALID_SET_FILE_POINTER) { + errno = map_errno(GetLastError()); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + ol.Offset = low; + ol.OffsetHigh = high; + } + ol.hEvent = CreateEvent(NULL, TRUE, TRUE, NULL); + if (!ol.hEvent) { errno = map_errno(GetLastError()); - return ret; + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + + pol = &ol; } - else - return rb_w32_send(fd, buf, size, 0); + + if (!WriteFile((HANDLE)_osfhnd(fd), buf, size, &written, pol)) { + err = GetLastError(); + if (err != ERROR_IO_PENDING) { + if (err == ERROR_ACCESS_DENIED) + errno = EBADF; + else + errno = map_errno(err); + + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + + if (pol) { + wait = rb_w32_wait_events_blocking(&ol.hEvent, 1, INFINITE); + if (wait != WAIT_OBJECT_0) { + if (errno != EINTR) + errno = map_errno(GetLastError()); + CloseHandle(ol.hEvent); + cancel_io((HANDLE)_osfhnd(fd)); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + + if (!GetOverlappedResult((HANDLE)_osfhnd(fd), &ol, &written, + TRUE)) { + errno = map_errno(err); + CloseHandle(ol.hEvent); + cancel_io((HANDLE)_osfhnd(fd)); + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + return -1; + } + } + } + + if (pol) { + CloseHandle(ol.hEvent); + + if (!(_osfile(fd) & (FDEV | FPIPE))) { + LONG high = ol.OffsetHigh; + LONG low = ol.Offset + written; + if (low < ol.Offset) + ++high; + SetFilePointer((HANDLE)_osfhnd(fd), low, &high, FILE_BEGIN); + } + } + + MTHREAD_ONLY(LeaveCriticalSection(&_pioinfo(fd)->lock)); + + return written; } static int -- cgit v1.2.3