From 02fbdf09b186fff01aaf83cda28b6c63c6f7b53e Mon Sep 17 00:00:00 2001 From: Matthew Flatt Date: Sat, 18 Oct 2008 16:40:08 +0000 Subject: [PATCH] avoid having MzScheme get stuck on pipes when CancelSynchronizedIo is not available svn: r12060 --- src/mzscheme/src/port.c | 197 +++++++++++++++++++++++++++++----------- 1 file changed, 146 insertions(+), 51 deletions(-) diff --git a/src/mzscheme/src/port.c b/src/mzscheme/src/port.c index f49a88ed02..635a10fce1 100644 --- a/src/mzscheme/src/port.c +++ b/src/mzscheme/src/port.c @@ -122,11 +122,15 @@ typedef struct Win_FD_Input_Thread { /* This is malloced for use in a Win32 thread */ HANDLE fd; volatile int avail, err, checking; + int *refcount; HANDLE eof; unsigned char *buffer; HANDLE checking_sema, ready_sema, you_clean_up_sema; + HANDLE thread; } Win_FD_Input_Thread; +static HANDLE refcount_sema; + typedef struct Win_FD_Output_Thread { /* This is malloced for use in a Win32 thread */ HANDLE fd; @@ -144,6 +148,7 @@ typedef struct Win_FD_Output_Thread { volatile int done, err_no; volatile unsigned int buflen, bufstart, bufend; /* used for blocking, only */ unsigned char *buffer; /* used for blocking, only */ + int *refcount; HANDLE lock_sema, work_sema, ready_sema, you_clean_up_sema; /* lock_sema protects the fields, work_sema starts the flush or flush-checking thread to work, ready_sema indicates that a flush @@ -187,6 +192,59 @@ typedef struct Scheme_Subprocess { # define MZ_FDS #endif +/******************** refcounts ********************/ + +#ifdef WINDOWS_FILE_HANDLES + +static int *malloc_refcount() +{ + if (!refcount_sema) + refcount_sema = CreateSemaphore(NULL, 1, 1, NULL); + + return (int *)malloc(sizeof(int)); +} + +#ifdef MZ_XFORM +START_XFORM_SKIP; +#endif + +static int dec_refcount(int *refcount) +{ + int rc; + + if (!refcount) + return 0; + + WaitForSingleObject(refcount_sema, INFINITE); + *refcount -= 1; + rc = *refcount; + ReleaseSemaphore(refcount_sema, 1, NULL); + + if (!rc) free(refcount); + + return rc; +} + +#ifdef MZ_XFORM +END_XFORM_SKIP; +#endif + +#else + +static int *malloc_refcount() +{ + return (int *)scheme_malloc_atomic(sizeof(int)); +} + +static int dec_refcount(int *refcount) +{ + if (!refcount) + return 0; + *refcont -= 1; + return *refcount; +} + +#endif /******************** file-descriptor I/O ********************/ @@ -4532,6 +4590,26 @@ scheme_make_file_input_port(FILE *fp) # ifdef WINDOWS_FILE_HANDLES static long WindowsFDReader(Win_FD_Input_Thread *th); static void WindowsFDICleanup(Win_FD_Input_Thread *th); +typedef BOOL (WINAPI* CSI_proc)(HANDLE); + +static CSI_proc get_csi(void) +{ + static int tried_csi = 0; + static CSI_proc csi; + + START_XFORM_SKIP; + if (!tried_csi) { + HMODULE hm; + hm = LoadLibrary("kernel32.dll"); + if (hm) + csi = (CSI_proc)GetProcAddress(hm, "CancelSynchronousIo"); + else + csi = NULL; + tried_csi = 1; + } + END_XFORM_SKIP; + return csi; +} # endif /* forward decl: */ @@ -4914,11 +4992,10 @@ fd_close_input(Scheme_Input_Port *port) fip = (Scheme_FD *)port->port_data; - if (fip->refcount) - *fip->refcount -= 1; - #ifdef WINDOWS_FILE_HANDLES if (fip->th) { + CSI_proc csi; + /* -1 for checking means "shut down" */ fip->th->checking = -1; ReleaseSemaphore(fip->th->checking_sema, 1, NULL); @@ -4928,25 +5005,40 @@ fd_close_input(Scheme_Input_Port *port) fip->th->eof = NULL; } + csi = get_csi(); + if (csi) { + /* Helps thread wake up. Otherwise, it's possible for the + thread to stay stuck trying to read, in which case the + file handle (probably a pipe) doesn't get closed. */ + csi(fip->th->thread); + } + /* Try to get out of cleaning up the records (since they can't be cleaned until the thread is also done: */ if (WaitForSingleObject(fip->th->you_clean_up_sema, 0) != WAIT_OBJECT_0) { /* The other thread exited and left us with clean-up: */ WindowsFDICleanup(fip->th); } /* otherwise, thread is responsible for clean-up */ - } - if (!fip->refcount || !*fip->refcount) { - CloseHandle((HANDLE)fip->fd); - --scheme_file_open_count; + } else { + int rc; + rc = dec_refcount(fip->refcount); + if (!rc) { + CloseHandle((HANDLE)fip->fd); + --scheme_file_open_count; + } } #else - if (!fip->refcount || !*fip->refcount) { - int cr; - do { - cr = close(fip->fd); - } while ((cr == -1) && (errno == EINTR)); - --scheme_file_open_count; - } + { + int rc; + rc = dec_refcount(fip->refcount); + if (!rc) { + int cr; + do { + cr = close(fip->fd); + } while ((cr == -1) && (errno == EINTR)); + --scheme_file_open_count; + } + } #endif } @@ -5088,9 +5180,12 @@ make_fd_input_port(int fd, Scheme_Object *name, int regfile, int win_textmode, i th->ready_sema = sm; sm = CreateSemaphore(NULL, 1, 1, NULL); th->you_clean_up_sema = sm; + th->refcount = refcount; h = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)WindowsFDReader, th, 0, &id); + th->thread = h; + scheme_remember_thread(h, 1); } #endif @@ -5161,9 +5256,15 @@ static long WindowsFDReader(Win_FD_Input_Thread *th) static void WindowsFDICleanup(Win_FD_Input_Thread *th) { + int rc; + CloseHandle(th->checking_sema); CloseHandle(th->ready_sema); CloseHandle(th->you_clean_up_sema); + + rc = dec_refcount(th->refcount); + if (!rc) CloseHandle(th->fd); + free(th->buffer); free(th); } @@ -5906,7 +6007,8 @@ static long flush_fd(Scheme_Output_Port *op, oth->ready_sema = sm; sm = CreateSemaphore(NULL, 1, 1, NULL); oth->you_clean_up_sema = sm; - + oth->refcount = fop->refcount; + h = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)WindowsFDWriter, oth, 0, &id); scheme_remember_thread(h, 1); @@ -6134,10 +6236,6 @@ fd_write_string(Scheme_Output_Port *port, return len; } -#ifdef WINDOWS_FILE_HANDLES -typedef BOOL (WINAPI* CSI_proc)(HANDLE); -#endif - static void fd_close_output(Scheme_Output_Port *port) { @@ -6163,32 +6261,15 @@ fd_close_output(Scheme_Output_Port *port) if (port->closed) return; - if (fop->refcount) - *fop->refcount -= 1; - #ifdef WINDOWS_FILE_HANDLES if (fop->oth) { - static int tried_csi = 0; - static CSI_proc csi; + CSI_proc csi; - START_XFORM_SKIP; - if (!tried_csi) { - HMODULE hm; - hm = LoadLibrary("kernel32.dll"); - if (hm) - csi = (BOOL (WINAPI*)(HANDLE))GetProcAddress(hm, "CancelSynchronousIo"); - else - csi = NULL; - tried_csi = 1; - } - END_XFORM_SKIP; + csi = get_csi(); if (csi) { + /* See also call to csi in fd_close_input */ csi(fop->oth->thread); - /* We're hoping that if CancelSyncrhonousIo isn't available, that - CloseHandle() will work, or that WriteFile() didn't block after - all (which seems to be the case with pre-Vista FILE_TYPE_CHAR - handles). */ } CloseHandle(fop->oth->thread); fop->oth->done = 1; @@ -6200,19 +6281,27 @@ fd_close_output(Scheme_Output_Port *port) WindowsFDOCleanup(fop->oth); } /* otherwise, thread is responsible for clean-up */ fop->oth = NULL; - } - if (!fop->refcount || !*fop->refcount) { - CloseHandle((HANDLE)fop->fd); - --scheme_file_open_count; + } else { + int rc; + rc = dec_refcount(fop->refcount); + if (!rc) { + CloseHandle((HANDLE)fop->fd); + --scheme_file_open_count; + } } #else - if (!fop->refcount || !*fop->refcount) { - int cr; - do { - cr = close(fop->fd); - } while ((cr == -1) && (errno == EINTR)); - --scheme_file_open_count; - } + { + int rc; + rc = dec_refcount(fop->refcount); + + if (!rc) { + int cr; + do { + cr = close(fop->fd); + } while ((cr == -1) && (errno == EINTR)); + --scheme_file_open_count; + } + } #endif } @@ -6290,7 +6379,7 @@ make_fd_output_port(int fd, Scheme_Object *name, int regfile, int win_textmode, if (and_read) { int *rc; Scheme_Object *a[2]; - rc = (int *)scheme_malloc_atomic(sizeof(int)); + rc = malloc_refcount(); *rc = 2; fop->refcount = rc; a[1] = the_port; @@ -6382,9 +6471,15 @@ static long WindowsFDWriter(Win_FD_Output_Thread *oth) static void WindowsFDOCleanup(Win_FD_Output_Thread *oth) { + int rc; + CloseHandle(oth->lock_sema); CloseHandle(oth->work_sema); CloseHandle(oth->you_clean_up_sema); + + rc = dec_refcount(oth->refcount); + if (!rc) CloseHandle(oth->fd); + if (oth->buffer) free(oth->buffer); free(oth);