The R Project SVN R

Rev

Rev 73675 | Rev 78045 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 73675 Rev 75992
Line 1... Line 1...
1
/*
1
/*
2
 *  R : A Computer Language for Statistical Data Analysis
2
 *  R : A Computer Language for Statistical Data Analysis
3
 *  file run.c: a simple 'reading' pipe (and a command executor)
3
 *  file run.c: a simple 'reading' pipe (and a command executor)
4
 *  Copyright  (C) 1999-2001  Guido Masarotto and Brian Ripley
4
 *  Copyright  (C) 1999-2001  Guido Masarotto and Brian Ripley
5
 *             (C) 2007-2017  The R Core Team
5
 *             (C) 2007-2019  The R Core Team
6
 *
6
 *
7
 *  This program is free software; you can redistribute it and/or modify
7
 *  This program is free software; you can redistribute it and/or modify
8
 *  it under the terms of the GNU General Public License as published by
8
 *  it under the terms of the GNU General Public License as published by
9
 *  the Free Software Foundation; either version 2 of the License, or
9
 *  the Free Software Foundation; either version 2 of the License, or
10
 *  (at your option) any later version.
10
 *  (at your option) any later version.
Line 141... Line 141...
141
extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
141
extern size_t Rf_utf8towcs(wchar_t *wc, const char *s, size_t n);
142
 
142
 
143
static void pcreate(const char* cmd, cetype_t enc,
143
static void pcreate(const char* cmd, cetype_t enc,
144
		      int newconsole, int visible,
144
		      int newconsole, int visible,
145
		      HANDLE hIN, HANDLE hOUT, HANDLE hERR,
145
		      HANDLE hIN, HANDLE hOUT, HANDLE hERR,
146
		      PROCESS_INFORMATION *pi)
146
		      pinfo *pi)
147
{
147
{
148
    DWORD ret;
148
    DWORD ret;
149
    STARTUPINFO si;
149
    STARTUPINFO si;
150
    STARTUPINFOW wsi;
150
    STARTUPINFOW wsi;
151
    HANDLE dupIN, dupOUT, dupERR;
151
    HANDLE dupIN, dupOUT, dupERR, job, port;
152
    WORD showWindow = SW_SHOWDEFAULT;
152
    WORD showWindow = SW_SHOWDEFAULT;
-
 
153
    DWORD flags;
-
 
154
    BOOL inJob;
-
 
155
    Rboolean breakaway;
-
 
156
    JOBOBJECT_EXTENDED_LIMIT_INFORMATION jeli;
-
 
157
    JOBOBJECT_ASSOCIATE_COMPLETION_PORT cport;
153
    int inpipe;
158
    int inpipe;
154
    char *ecmd;
159
    char *ecmd;
155
    SECURITY_ATTRIBUTES sa;
160
    SECURITY_ATTRIBUTES sa;
156
    sa.nLength = sizeof(sa);
161
    sa.nLength = sizeof(sa);
157
    sa.lpSecurityDescriptor = NULL;
162
    sa.lpSecurityDescriptor = NULL;
Line 222... Line 227...
222
	    si.hStdOutput = dupOUT;
227
	    si.hStdOutput = dupOUT;
223
	    si.hStdError  = dupERR;
228
	    si.hStdError  = dupERR;
224
	}
229
	}
225
    }
230
    }
226
 
231
 
-
 
232
    /* Originally, the external process has been waited for only using
-
 
233
       waitForSingleObject, but that has been proven unreliable: sometimes
-
 
234
       the output file would still be opened (and hence locked) by some
-
 
235
       child process after waitForSingleObject would finish. This has been
-
 
236
       observed also while running tests and particularly when building
-
 
237
       vignettes, resulting in spurious "Permission denied" errors.
-
 
238
 
-
 
239
       This has been happening almost surely due to a child process not
-
 
240
       waiting for its own children to finish, which has been reported
-
 
241
       to happen with Linux utilities ported to Windows as used for tests
-
 
242
       in Haskell/GHC. Inspired by Haskell process and a blog post about
-
 
243
       waiting for a process tree to finish, we now use job objects to
-
 
244
       wait also for process trees with this issue:
-
 
245
 
-
 
246
	https://github.com/haskell/process
-
 
247
	https://blogs.msdn.microsoft.com/oldnewthing/20130405-00/?p=4743
-
 
248
 
-
 
249
       In addition, we try to be easy on applications coded to rely on that
-
 
250
       they do not run in a job, when running in old Windows that do not
-
 
251
       support nested jobs. With nested jobs support, it might make sense
-
 
252
       to not breakaway to better support nested R processes.
-
 
253
    */
-
 
254
 
-
 
255
    /* Creating the process with CREATE_BREAKAWAY_FROM_JOB is safe when
-
 
256
       the process is not in any job or when it is in a job that allows it.
-
 
257
       The documentation does not say what would happen if we set the flag,
-
 
258
       but run in a job that does not allow it, so better don't. */
-
 
259
    breakaway = FALSE;
-
 
260
    if (IsProcessInJob(GetCurrentProcess(), NULL, &inJob) && inJob) {
-
 
261
	/* The documentation does not say that it would be ok to use
-
 
262
	   QueryInformationJobObject when the process is not in the job,
-
 
263
	   so we have better tested that upfront. */
-
 
264
	ZeroMemory(&jeli, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
-
 
265
	ret = QueryInformationJobObject(
-
 
266
		NULL,
-
 
267
	        JobObjectExtendedLimitInformation,
-
 
268
	        &jeli,
-
 
269
	        sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION),
-
 
270
		NULL);
-
 
271
	breakaway = ret &&
-
 
272
		(jeli.BasicLimitInformation.LimitFlags &
-
 
273
	         JOB_OBJECT_LIMIT_BREAKAWAY_OK);
-
 
274
    }
-
 
275
 
-
 
276
    /* create a job that allows breakaway */
-
 
277
    job = CreateJobObject(NULL, NULL);
-
 
278
    if (job) {
-
 
279
	ZeroMemory(&jeli, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
-
 
280
	jeli.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_BREAKAWAY_OK;
-
 
281
	ret = SetInformationJobObject(
-
 
282
		job,
-
 
283
		JobObjectExtendedLimitInformation,
-
 
284
		&jeli,
-
 
285
                sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION));
-
 
286
	if (!ret) {
-
 
287
	    CloseHandle(job);
-
 
288
	    job = NULL;
-
 
289
	}
-
 
290
    }
-
 
291
 
-
 
292
    /* create a completion port to learn when processes exit */
-
 
293
    if (job) {
-
 
294
	port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 1);
-
 
295
	if (!port) {
-
 
296
	    CloseHandle(job);
-
 
297
	    job = NULL;
-
 
298
	}
-
 
299
    }
-
 
300
    if (job) {
-
 
301
	ZeroMemory(&cport, sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
-
 
302
	cport.CompletionKey = job; /* use job handle as key */
-
 
303
	cport.CompletionPort = port;
-
 
304
	ret = SetInformationJobObject(
-
 
305
	    job,
-
 
306
	    JobObjectAssociateCompletionPortInformation,
-
 
307
	    &cport,
-
 
308
	    sizeof(JOBOBJECT_ASSOCIATE_COMPLETION_PORT));
-
 
309
	if (!ret) {
-
 
310
	    CloseHandle(job);
-
 
311
	    CloseHandle(port);
-
 
312
	    job = NULL;
-
 
313
	}
-
 
314
    }
-
 
315
 
-
 
316
    flags = 0;
-
 
317
    if (job)
-
 
318
	flags |= CREATE_SUSPENDED; /* assign to job before it runs */
-
 
319
    if (newconsole && (visible == 1))
-
 
320
	flags |= CREATE_NEW_CONSOLE;
-
 
321
    if (job && breakaway)
-
 
322
	flags |= CREATE_BREAKAWAY_FROM_JOB;
-
 
323
 
227
    if(enc == CE_UTF8) {
324
    if(enc == CE_UTF8) {
228
	int n = strlen(ecmd); /* max no of chars */
325
	int n = strlen(ecmd); /* max no of chars */
229
	wchar_t wcmd[n+1];
326
	wchar_t wcmd[n+1];
230
	Rf_utf8towcs(wcmd, ecmd, n+1);
327
	Rf_utf8towcs(wcmd, ecmd, n+1);
231
	ret = CreateProcessW(NULL, wcmd, &sa, &sa, TRUE,
328
	ret = CreateProcessW(NULL, wcmd, &sa, &sa, TRUE, flags,
232
			     (newconsole && (visible == 1)) ?
-
 
233
			     CREATE_NEW_CONSOLE : 0,
-
 
234
			     NULL, NULL, &wsi, pi);
329
			     NULL, NULL, &wsi, &(pi->pi));
235
    } else
330
    } else
236
	ret = CreateProcess(NULL, ecmd, &sa, &sa, TRUE,
331
	ret = CreateProcess(NULL, ecmd, &sa, &sa, TRUE, flags,
237
			    (newconsole && (visible == 1)) ?
332
			    NULL, NULL, &si, &(pi->pi));
-
 
333
 
-
 
334
    if (ret && job) {
-
 
335
	/* process was created as suspended */
-
 
336
	if (!AssignProcessToJobObject(job, pi->pi.hProcess)) {
-
 
337
	    /* will fail running on Windows without support for nested jobs,
-
 
338
	       when running in a job that does not allow breakaway */
-
 
339
	    CloseHandle(job);
-
 
340
	    CloseHandle(port);
-
 
341
	    job = NULL;
-
 
342
	}
-
 
343
	ResumeThread(pi->pi.hThread);
-
 
344
    }
-
 
345
 
-
 
346
    if (ret && job) {
-
 
347
	/* process is running in new job */
-
 
348
	pi->job = job;
-
 
349
	pi->port = port;
-
 
350
    } else {
-
 
351
	if (job) {
-
 
352
	    CloseHandle(job);
238
			    CREATE_NEW_CONSOLE : 0,
353
	    CloseHandle(port);
239
			    NULL, NULL, &si, pi);
354
	    job = NULL;
-
 
355
	}
-
 
356
	pi->job = NULL;
-
 
357
	pi->port = NULL;
-
 
358
    }
240
 
359
 
241
    if (inpipe) {
360
    if (inpipe) {
242
	CloseHandle(dupIN);
361
	CloseHandle(dupIN);
243
	CloseHandle(dupOUT);
362
	CloseHandle(dupOUT);
244
	CloseHandle(dupERR);
363
	CloseHandle(dupERR);
245
    }
364
    }
246
    if (!ret)
365
    if (!ret)
247
	snprintf(RunError, 500, _("'CreateProcess' failed to run '%s'"), ecmd);
366
	snprintf(RunError, 500, _("'CreateProcess' failed to run '%s'"), ecmd);
248
    else CloseHandle(pi->hThread);
367
    else CloseHandle(pi->pi.hThread);
249
    free(ecmd);
368
    free(ecmd);
250
    return;
369
    return;
251
}
370
}
252
 
371
 
253
/* used in rpipeOpen */
372
/* used in rpipeOpen */
Line 255... Line 374...
255
threadedwait(LPVOID param)
374
threadedwait(LPVOID param)
256
{
375
{
257
    rpipe *p = (rpipe *) param;
376
    rpipe *p = (rpipe *) param;
258
 
377
 
259
    if (p->timeoutMillis) {
378
    if (p->timeoutMillis) {
260
	DWORD wres = WaitForSingleObject(p->pi.hProcess, p->timeoutMillis);
379
	DWORD wres = WaitForSingleObject(p->pi.pi.hProcess, p->timeoutMillis);
261
	if (wres == WAIT_TIMEOUT) {
380
	if (wres == WAIT_TIMEOUT) {
262
	    TerminateProcess(p->pi.hProcess, 124);
381
	    TerminateProcess(p->pi.pi.hProcess, 124);
263
	    p->timedout = 1;
382
	    p->timedout = 1;
264
	    /* wait up to 10s for the  process to actually terminate */
383
	    /* wait up to 10s for the  process to actually terminate */
265
	    WaitForSingleObject(p->pi.hProcess, 10000);
384
	    WaitForSingleObject(p->pi.pi.hProcess, 10000);
266
	}
385
	}
267
    } else 
386
    } else 
268
	WaitForSingleObject(p->pi.hProcess, INFINITE);
387
	WaitForSingleObject(p->pi.pi.hProcess, INFINITE);
269
 
388
 
270
    DWORD ret;
389
    DWORD ret;
271
    GetExitCodeProcess(p->pi.hProcess, &ret);
390
    GetExitCodeProcess(p->pi.pi.hProcess, &ret);
272
    p->exitcode = ret;
391
    p->exitcode = ret;
273
    
392
    
274
    FlushFileBuffers(p->write);
393
    FlushFileBuffers(p->write);
275
    FlushFileBuffers(p->read);
394
    FlushFileBuffers(p->read);
276
    p->active = 0;
395
    p->active = 0;
Line 342... Line 461...
342
 
461
 
343
/* Terminate the process pwait2 is waiting for. */
462
/* Terminate the process pwait2 is waiting for. */
344
 
463
 
345
extern void GA_askok(const char *info);
464
extern void GA_askok(const char *info);
346
 
465
 
-
 
466
static void waitForJob(pinfo *pi, DWORD timeoutMillis, int* timedout)
-
 
467
{
-
 
468
    DWORD code, ret;
-
 
469
    ULONG_PTR key;
-
 
470
    DWORD beforeMillis;
-
 
471
    JOBOBJECT_BASIC_ACCOUNTING_INFORMATION jbai;
-
 
472
    LPOVERLAPPED overlapped; /* not used */
-
 
473
    DWORD queryMillis;
-
 
474
 
-
 
475
    if (timeoutMillis)
-
 
476
	beforeMillis = timeGetTime();
-
 
477
 
-
 
478
    queryMillis = 0;
-
 
479
    for(;;) {
-
 
480
	ret = GetQueuedCompletionStatus(pi->port, &code, &key,
-
 
481
					&overlapped, queryMillis);
-
 
482
	if (ret && code == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO &&
-
 
483
	    (HANDLE)key == pi->job)
-
 
484
	    break;
-
 
485
 
-
 
486
	/* start with short query timeouts because notifications often get lost,
-
 
487
	   this is essentially polling */
-
 
488
 
-
 
489
	if (queryMillis == 0)
-
 
490
	    queryMillis = 1;
-
 
491
	else if (queryMillis < 100)
-
 
492
	    queryMillis *= 2;
-
 
493
 
-
 
494
	if (timeoutMillis && (timeGetTime() - beforeMillis >= timeoutMillis)) {
-
 
495
	    if (timedout)
-
 
496
		*timedout = 1;
-
 
497
	    break;
-
 
498
	}
-
 
499
 
-
 
500
	/* Check also explicitly because notifications are documented to get
-
 
501
	   lost and they often do. */
-
 
502
	ZeroMemory(&jbai, sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION));
-
 
503
	ret = QueryInformationJobObject(
-
 
504
		pi->job,
-
 
505
		JobObjectBasicAccountingInformation,
-
 
506
		&jbai,
-
 
507
		sizeof(JOBOBJECT_BASIC_ACCOUNTING_INFORMATION),
-
 
508
		NULL);
-
 
509
	if (ret && jbai.ActiveProcesses == 0)
-
 
510
	    break;
-
 
511
    }
-
 
512
    CloseHandle(pi->port);
-
 
513
    CloseHandle(pi->job);
-
 
514
}
-
 
515
 
347
static void terminate_process(void *p)
516
static void terminate_process(void *p)
348
{
517
{
349
    PROCESS_INFORMATION *pi = (PROCESS_INFORMATION*)p;
518
    pinfo *pi = (pinfo*) p;
350
    EnumWindows((WNDENUMPROC)TerminateWindow, (LPARAM)pi->dwProcessId);
519
    EnumWindows((WNDENUMPROC)TerminateWindow, (LPARAM)pi->pi.dwProcessId);
351
 
520
 
352
    if (WaitForSingleObject(pi->hProcess, 5000) == WAIT_TIMEOUT) {
521
    if (WaitForSingleObject(pi->pi.hProcess, 5000) == WAIT_TIMEOUT) {
353
	if (R_Interactive)
522
	if (R_Interactive)
354
	    GA_askok(_("Child process not responding.  R will terminate it."));
523
	    GA_askok(_("Child process not responding.  R will terminate it."));
355
	TerminateProcess(pi->hProcess, 99);
524
	TerminateProcess(pi->pi.hProcess, 99);
356
    }
525
    }
-
 
526
 
-
 
527
    if (pi->job)
-
 
528
	waitForJob(pi, 2000, NULL);
357
}
529
}
358
 
530
 
359
static int pwait2(HANDLE p, DWORD timeoutMillis, int* timedout)
531
static int pwait2(pinfo *pi, DWORD timeoutMillis, int* timedout)
360
{
532
{
361
    DWORD ret;
533
    DWORD ret;
362
 
534
 
363
    if (!timeoutMillis) {
535
    if (!timeoutMillis) {
364
	while( WaitForSingleObject(p, 100) == WAIT_TIMEOUT )
536
	while( WaitForSingleObject(pi->pi.hProcess, 100) == WAIT_TIMEOUT )
365
	    R_CheckUserInterrupt();
537
	    R_CheckUserInterrupt();
366
    } else {
538
    } else {
367
	DWORD beforeMillis = timeGetTime();
539
	DWORD beforeMillis = timeGetTime();
368
	while( WaitForSingleObject(p, 100) == WAIT_TIMEOUT ) {
540
	while( WaitForSingleObject(pi->pi.hProcess, 100) == WAIT_TIMEOUT ) {
369
	    R_CheckUserInterrupt();
541
	    R_CheckUserInterrupt();
370
	    DWORD afterMillis = timeGetTime();
542
	    DWORD afterMillis = timeGetTime();
371
	    if (afterMillis - beforeMillis >= timeoutMillis) {
543
	    if (afterMillis - beforeMillis >= timeoutMillis) {
372
		TerminateProcess(p, 124); 
544
		TerminateProcess(pi->pi.hProcess, 124);
373
		if (timedout)
545
		if (timedout)
374
		    *timedout = 1;
546
		    *timedout = 1;
375
		/* wait up to 10s for the process to actually terminate */
547
		/* wait up to 10s for the process to actually terminate */
376
		WaitForSingleObject(p, 10000);
548
		WaitForSingleObject(pi->pi.hProcess, 10000);
377
		break;
549
		break;
378
	    }
550
	    }
379
	}
551
	}
380
    }
552
    }
381
 
553
 
382
    GetExitCodeProcess(p, &ret);
554
    GetExitCodeProcess(pi->pi.hProcess, &ret);
-
 
555
 
-
 
556
    if (pi->job)
-
 
557
	waitForJob(pi, timeoutMillis, timedout);
-
 
558
 
383
    return ret;
559
    return ret;
384
}
560
}
385
 
561
 
386
/*
562
/*
387
  Used for external commands in file.show() and edit(), and for
563
  Used for external commands in file.show() and edit(), and for
Line 406... Line 582...
406
    if (!wait && timeout)
582
    if (!wait && timeout)
407
	error("Timeout with background running processes is not supported.");
583
	error("Timeout with background running processes is not supported.");
408
    
584
    
409
    HANDLE hIN = getInputHandle(fin), hOUT, hERR;
585
    HANDLE hIN = getInputHandle(fin), hOUT, hERR;
410
    int ret = 0;
586
    int ret = 0;
411
    PROCESS_INFORMATION pi;
587
    pinfo pi;
412
    int close1 = 0, close2 = 0, close3 = 0;
588
    int close1 = 0, close2 = 0, close3 = 0;
413
    
589
    
414
    if (hIN && fin && fin[0]) close1 = 1;
590
    if (hIN && fin && fin[0]) close1 = 1;
415
 
591
 
416
    hOUT = getOutputHandle(fout, 0);
592
    hOUT = getOutputHandle(fout, 0);
Line 422... Line 598...
422
	if (!hERR) return 1;
598
	if (!hERR) return 1;
423
	if (ferr && ferr[0]) close3 = 1;
599
	if (ferr && ferr[0]) close3 = 1;
424
    }
600
    }
425
 
601
 
426
 
602
 
427
    memset(&pi, 0, sizeof(pi));
603
    memset(&(pi.pi), 0, sizeof(PROCESS_INFORMATION));
428
    pcreate(cmd, enc, !wait, visible, hIN, hOUT, hERR, &pi);
604
    pcreate(cmd, enc, !wait, visible, hIN, hOUT, hERR, &pi);
429
    if (pi.hProcess) {
605
    if (pi.pi.hProcess) {
430
	if (wait) {
606
	if (wait) {
431
	    RCNTXT cntxt;
607
	    RCNTXT cntxt;
432
	    begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
608
	    begincontext(&cntxt, CTXT_CCODE, R_NilValue, R_BaseEnv, R_BaseEnv,
433
		     R_NilValue, R_NilValue);
609
		     R_NilValue, R_NilValue);
434
	    cntxt.cend = &terminate_process;
610
	    cntxt.cend = &terminate_process;
435
	    cntxt.cenddata = &pi;
611
	    cntxt.cenddata = &pi;
436
	    DWORD timeoutMillis = (DWORD) (1000*timeout);
612
	    DWORD timeoutMillis = (DWORD) (1000*timeout);
437
	    ret = pwait2(pi.hProcess, timeoutMillis, timedout);
613
	    ret = pwait2(&pi, timeoutMillis, timedout);
438
	    endcontext(&cntxt);
614
	    endcontext(&cntxt);
439
	    snprintf(RunError, 501, _("Exit code was %d"), ret);
615
	    snprintf(RunError, 501, _("Exit code was %d"), ret);
440
	    ret &= 0xffff;
616
	    ret &= 0xffff;
441
	} else ret = 0;
617
	} else ret = 0;
442
	CloseHandle(pi.hProcess);
618
	CloseHandle(pi.pi.hProcess);
443
    } else {
619
    } else {
444
    	ret = NOLAUNCH;
620
    	ret = NOLAUNCH;
445
    }
621
    }
446
    if (close1) CloseHandle(hIN);
622
    if (close1) CloseHandle(hIN);
447
    if (close2) CloseHandle(hOUT);
623
    if (close2) CloseHandle(hOUT);
Line 471... Line 647...
471
    if (!(r = (rpipe *) malloc(sizeof(struct structRPIPE)))) {
647
    if (!(r = (rpipe *) malloc(sizeof(struct structRPIPE)))) {
472
	strcpy(RunError, _("Insufficient memory (rpipeOpen)"));
648
	strcpy(RunError, _("Insufficient memory (rpipeOpen)"));
473
	return NULL;
649
	return NULL;
474
    }
650
    }
475
    r->active = 0;
651
    r->active = 0;
476
    r->pi.hProcess = NULL;
652
    r->pi.pi.hProcess = NULL;
-
 
653
    r->pi.job = NULL;
477
    r->thread = NULL;
654
    r->thread = NULL;
478
    r->timedout = 0;
655
    r->timedout = 0;
479
    r->timeoutMillis = (DWORD) (1000*timeout);
656
    r->timeoutMillis = (DWORD) (1000*timeout);
480
    res = CreatePipe(&hReadPipe, &hWritePipe, NULL, 0);
657
    res = CreatePipe(&hReadPipe, &hWritePipe, NULL, 0);
481
    if (res == FALSE) {
658
    if (res == FALSE) {
Line 493... Line 670...
493
	/* This sends stdout and stderr to NUL: */
670
	/* This sends stdout and stderr to NUL: */
494
	pcreate(cmd, enc, 1, visible,
671
	pcreate(cmd, enc, 1, visible,
495
		r->read, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
672
		r->read, INVALID_HANDLE_VALUE, INVALID_HANDLE_VALUE,
496
		&(r->pi));
673
		&(r->pi));
497
	r->active = 1;
674
	r->active = 1;
498
	if (!r->pi.hProcess) return NULL; else return r;
675
	if (!r->pi.pi.hProcess) return NULL; else return r;
499
    }
676
    }
500
 
677
 
501
    /* pipe for R to read from */
678
    /* pipe for R to read from */
502
    hTHIS = GetCurrentProcess();
679
    hTHIS = GetCurrentProcess();
503
    r->write = hWritePipe;
680
    r->write = hWritePipe;
Line 526... Line 703...
526
    if (close1) CloseHandle(hIN);
703
    if (close1) CloseHandle(hIN);
527
    if (close2) CloseHandle(hOUT);
704
    if (close2) CloseHandle(hOUT);
528
    if (close3) CloseHandle(hERR);
705
    if (close3) CloseHandle(hERR);
529
 
706
 
530
    r->active = 1;
707
    r->active = 1;
531
    if (!r->pi.hProcess)
708
    if (!r->pi.pi.hProcess)
532
	return NULL;
709
	return NULL;
533
    if (!(r->thread = CreateThread(NULL, 0, threadedwait, r, 0, &id))) {
710
    if (!(r->thread = CreateThread(NULL, 0, threadedwait, r, 0, &id))) {
534
	rpipeClose(r, NULL);
711
	rpipeClose(r, NULL);
535
	strcpy(RunError, "CreateThread failed");
712
	strcpy(RunError, "CreateThread failed");
536
	return NULL;
713
	return NULL;
Line 623... Line 800...
623
    rpipeTerminate(r);
800
    rpipeTerminate(r);
624
    /* threadedwait may have obtained the exit code of the pipe process,
801
    /* threadedwait may have obtained the exit code of the pipe process,
625
       but also may have been terminated too early; retrieve the exit
802
       but also may have been terminated too early; retrieve the exit
626
       code again to avoid race condition */
803
       code again to avoid race condition */
627
    DWORD ret;
804
    DWORD ret;
628
    GetExitCodeProcess(r->pi.hProcess, &ret);
805
    GetExitCodeProcess(r->pi.pi.hProcess, &ret);
629
    r->exitcode = ret;
806
    r->exitcode = ret;
630
    CloseHandle(r->read);
807
    CloseHandle(r->read);
631
    CloseHandle(r->write);
808
    CloseHandle(r->write);
632
    CloseHandle(r->pi.hProcess);
809
    CloseHandle(r->pi.pi.hProcess);
633
    i = r->exitcode;
810
    i = r->exitcode;
634
    if (timedout)
811
    if (timedout)
635
	*timedout = r->timedout;
812
	*timedout = r->timedout;
636
    free(r);
813
    free(r);
637
    return i &= 0xffff;
814
    return i &= 0xffff;
Line 737... Line 914...
737
{
914
{
738
    rpipe *rp = ((RWpipeconn)con->private) ->rp;
915
    rpipe *rp = ((RWpipeconn)con->private) ->rp;
739
    DWORD towrite = nitems * size, write, ret;
916
    DWORD towrite = nitems * size, write, ret;
740
 
917
 
741
    if(!rp->active) return 0;
918
    if(!rp->active) return 0;
742
    GetExitCodeProcess(rp->pi.hProcess, &ret);
919
    GetExitCodeProcess(rp->pi.pi.hProcess, &ret);
743
    if(ret != STILL_ACTIVE) {
920
    if(ret != STILL_ACTIVE) {
744
	rp->active = 0;
921
	rp->active = 0;
745
	warning("broken Windows pipe");
922
	warning("broken Windows pipe");
746
	return 0;
923
	return 0;
747
    }
924
    }