avoid having MzScheme get stuck on pipes when CancelSynchronizedIo is not available

svn: r12060
This commit is contained in:
Matthew Flatt 2008-10-18 16:40:08 +00:00
parent 2288db4fc9
commit 02fbdf09b1

View File

@ -122,11 +122,15 @@ typedef struct Win_FD_Input_Thread {
/* This is malloced for use in a Win32 thread */ /* This is malloced for use in a Win32 thread */
HANDLE fd; HANDLE fd;
volatile int avail, err, checking; volatile int avail, err, checking;
int *refcount;
HANDLE eof; HANDLE eof;
unsigned char *buffer; unsigned char *buffer;
HANDLE checking_sema, ready_sema, you_clean_up_sema; HANDLE checking_sema, ready_sema, you_clean_up_sema;
HANDLE thread;
} Win_FD_Input_Thread; } Win_FD_Input_Thread;
static HANDLE refcount_sema;
typedef struct Win_FD_Output_Thread { typedef struct Win_FD_Output_Thread {
/* This is malloced for use in a Win32 thread */ /* This is malloced for use in a Win32 thread */
HANDLE fd; HANDLE fd;
@ -144,6 +148,7 @@ typedef struct Win_FD_Output_Thread {
volatile int done, err_no; volatile int done, err_no;
volatile unsigned int buflen, bufstart, bufend; /* used for blocking, only */ volatile unsigned int buflen, bufstart, bufend; /* used for blocking, only */
unsigned char *buffer; /* used for blocking, only */ unsigned char *buffer; /* used for blocking, only */
int *refcount;
HANDLE lock_sema, work_sema, ready_sema, you_clean_up_sema; HANDLE lock_sema, work_sema, ready_sema, you_clean_up_sema;
/* lock_sema protects the fields, work_sema starts the flush or /* lock_sema protects the fields, work_sema starts the flush or
flush-checking thread to work, ready_sema indicates that a flush flush-checking thread to work, ready_sema indicates that a flush
@ -187,6 +192,59 @@ typedef struct Scheme_Subprocess {
# define MZ_FDS # define MZ_FDS
#endif #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 ********************/ /******************** file-descriptor I/O ********************/
@ -4532,6 +4590,26 @@ scheme_make_file_input_port(FILE *fp)
# ifdef WINDOWS_FILE_HANDLES # ifdef WINDOWS_FILE_HANDLES
static long WindowsFDReader(Win_FD_Input_Thread *th); static long WindowsFDReader(Win_FD_Input_Thread *th);
static void WindowsFDICleanup(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 # endif
/* forward decl: */ /* forward decl: */
@ -4914,11 +4992,10 @@ fd_close_input(Scheme_Input_Port *port)
fip = (Scheme_FD *)port->port_data; fip = (Scheme_FD *)port->port_data;
if (fip->refcount)
*fip->refcount -= 1;
#ifdef WINDOWS_FILE_HANDLES #ifdef WINDOWS_FILE_HANDLES
if (fip->th) { if (fip->th) {
CSI_proc csi;
/* -1 for checking means "shut down" */ /* -1 for checking means "shut down" */
fip->th->checking = -1; fip->th->checking = -1;
ReleaseSemaphore(fip->th->checking_sema, 1, NULL); ReleaseSemaphore(fip->th->checking_sema, 1, NULL);
@ -4928,25 +5005,40 @@ fd_close_input(Scheme_Input_Port *port)
fip->th->eof = NULL; 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 /* Try to get out of cleaning up the records (since they can't be
cleaned until the thread is also done: */ cleaned until the thread is also done: */
if (WaitForSingleObject(fip->th->you_clean_up_sema, 0) != WAIT_OBJECT_0) { if (WaitForSingleObject(fip->th->you_clean_up_sema, 0) != WAIT_OBJECT_0) {
/* The other thread exited and left us with clean-up: */ /* The other thread exited and left us with clean-up: */
WindowsFDICleanup(fip->th); WindowsFDICleanup(fip->th);
} /* otherwise, thread is responsible for clean-up */ } /* otherwise, thread is responsible for clean-up */
} } else {
if (!fip->refcount || !*fip->refcount) { int rc;
CloseHandle((HANDLE)fip->fd); rc = dec_refcount(fip->refcount);
--scheme_file_open_count; if (!rc) {
CloseHandle((HANDLE)fip->fd);
--scheme_file_open_count;
}
} }
#else #else
if (!fip->refcount || !*fip->refcount) { {
int cr; int rc;
do { rc = dec_refcount(fip->refcount);
cr = close(fip->fd); if (!rc) {
} while ((cr == -1) && (errno == EINTR)); int cr;
--scheme_file_open_count; do {
} cr = close(fip->fd);
} while ((cr == -1) && (errno == EINTR));
--scheme_file_open_count;
}
}
#endif #endif
} }
@ -5088,9 +5180,12 @@ make_fd_input_port(int fd, Scheme_Object *name, int regfile, int win_textmode, i
th->ready_sema = sm; th->ready_sema = sm;
sm = CreateSemaphore(NULL, 1, 1, NULL); sm = CreateSemaphore(NULL, 1, 1, NULL);
th->you_clean_up_sema = sm; th->you_clean_up_sema = sm;
th->refcount = refcount;
h = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)WindowsFDReader, th, 0, &id); h = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)WindowsFDReader, th, 0, &id);
th->thread = h;
scheme_remember_thread(h, 1); scheme_remember_thread(h, 1);
} }
#endif #endif
@ -5161,9 +5256,15 @@ static long WindowsFDReader(Win_FD_Input_Thread *th)
static void WindowsFDICleanup(Win_FD_Input_Thread *th) static void WindowsFDICleanup(Win_FD_Input_Thread *th)
{ {
int rc;
CloseHandle(th->checking_sema); CloseHandle(th->checking_sema);
CloseHandle(th->ready_sema); CloseHandle(th->ready_sema);
CloseHandle(th->you_clean_up_sema); CloseHandle(th->you_clean_up_sema);
rc = dec_refcount(th->refcount);
if (!rc) CloseHandle(th->fd);
free(th->buffer); free(th->buffer);
free(th); free(th);
} }
@ -5906,6 +6007,7 @@ static long flush_fd(Scheme_Output_Port *op,
oth->ready_sema = sm; oth->ready_sema = sm;
sm = CreateSemaphore(NULL, 1, 1, NULL); sm = CreateSemaphore(NULL, 1, 1, NULL);
oth->you_clean_up_sema = sm; oth->you_clean_up_sema = sm;
oth->refcount = fop->refcount;
h = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)WindowsFDWriter, oth, 0, &id); h = CreateThread(NULL, 4096, (LPTHREAD_START_ROUTINE)WindowsFDWriter, oth, 0, &id);
@ -6134,10 +6236,6 @@ fd_write_string(Scheme_Output_Port *port,
return len; return len;
} }
#ifdef WINDOWS_FILE_HANDLES
typedef BOOL (WINAPI* CSI_proc)(HANDLE);
#endif
static void static void
fd_close_output(Scheme_Output_Port *port) fd_close_output(Scheme_Output_Port *port)
{ {
@ -6163,32 +6261,15 @@ fd_close_output(Scheme_Output_Port *port)
if (port->closed) if (port->closed)
return; return;
if (fop->refcount)
*fop->refcount -= 1;
#ifdef WINDOWS_FILE_HANDLES #ifdef WINDOWS_FILE_HANDLES
if (fop->oth) { if (fop->oth) {
static int tried_csi = 0; CSI_proc csi;
static CSI_proc csi;
START_XFORM_SKIP; csi = get_csi();
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;
if (csi) { if (csi) {
/* See also call to csi in fd_close_input */
csi(fop->oth->thread); 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); CloseHandle(fop->oth->thread);
fop->oth->done = 1; fop->oth->done = 1;
@ -6200,19 +6281,27 @@ fd_close_output(Scheme_Output_Port *port)
WindowsFDOCleanup(fop->oth); WindowsFDOCleanup(fop->oth);
} /* otherwise, thread is responsible for clean-up */ } /* otherwise, thread is responsible for clean-up */
fop->oth = NULL; fop->oth = NULL;
} } else {
if (!fop->refcount || !*fop->refcount) { int rc;
CloseHandle((HANDLE)fop->fd); rc = dec_refcount(fop->refcount);
--scheme_file_open_count; if (!rc) {
CloseHandle((HANDLE)fop->fd);
--scheme_file_open_count;
}
} }
#else #else
if (!fop->refcount || !*fop->refcount) { {
int cr; int rc;
do { rc = dec_refcount(fop->refcount);
cr = close(fop->fd);
} while ((cr == -1) && (errno == EINTR)); if (!rc) {
--scheme_file_open_count; int cr;
} do {
cr = close(fop->fd);
} while ((cr == -1) && (errno == EINTR));
--scheme_file_open_count;
}
}
#endif #endif
} }
@ -6290,7 +6379,7 @@ make_fd_output_port(int fd, Scheme_Object *name, int regfile, int win_textmode,
if (and_read) { if (and_read) {
int *rc; int *rc;
Scheme_Object *a[2]; Scheme_Object *a[2];
rc = (int *)scheme_malloc_atomic(sizeof(int)); rc = malloc_refcount();
*rc = 2; *rc = 2;
fop->refcount = rc; fop->refcount = rc;
a[1] = the_port; a[1] = the_port;
@ -6382,9 +6471,15 @@ static long WindowsFDWriter(Win_FD_Output_Thread *oth)
static void WindowsFDOCleanup(Win_FD_Output_Thread *oth) static void WindowsFDOCleanup(Win_FD_Output_Thread *oth)
{ {
int rc;
CloseHandle(oth->lock_sema); CloseHandle(oth->lock_sema);
CloseHandle(oth->work_sema); CloseHandle(oth->work_sema);
CloseHandle(oth->you_clean_up_sema); CloseHandle(oth->you_clean_up_sema);
rc = dec_refcount(oth->refcount);
if (!rc) CloseHandle(oth->fd);
if (oth->buffer) if (oth->buffer)
free(oth->buffer); free(oth->buffer);
free(oth); free(oth);