Changeset 3187 in kBuild
- Timestamp:
- Mar 24, 2018 3:26:58 AM (7 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kmk/w32/winchildren.c
r3185 r3187 40 40 * making the main thread focus on managing child processes. 41 41 * 42 * 5. Output synchronization using reusable pipes [not yet implemented].42 * 5. Output synchronization using reusable pipes. 43 43 * 44 44 * … … 62 62 * on different groups. This code will try distribute children evenly among the 63 63 * processor groups, using a very simple algorithm (see details in code). 64 *65 *66 * @todo Output buffering using pipes, finally making 'link' output readable.67 64 * 68 65 * … … 198 195 /** Whether to close hStdErr after creating the process. */ 199 196 BOOL fCloseStdErr; 197 /** Whether to catch output from the process. */ 198 BOOL fCatchOutput; 200 199 201 200 /** Child process handle. */ … … 250 249 #define WINCHILD_MAGIC 0xbabebabeU 251 250 251 /** 252 * A childcare worker pipe. 253 */ 254 typedef struct WINCCWPIPE 255 { 256 /** My end of the pipe. */ 257 HANDLE hPipeMine; 258 /** The child end of the pipe. */ 259 HANDLE hPipeChild; 260 /** The event for asynchronous reading. */ 261 HANDLE hEvent; 262 /** Which pipe this is (1 == stdout, 2 == stderr). */ 263 unsigned char iWhich; 264 /** Set if we've got a read pending already. */ 265 BOOL fReadPending; 266 /** Number of bytes at the start of the buffer that we've already 267 * written out. We try write out whole lines. */ 268 DWORD cbWritten; 269 /** The buffer offset of the read currently pending. */ 270 DWORD offPendingRead; 271 /** Read buffer size. */ 272 DWORD cbBuffer; 273 /** The read buffer allocation. */ 274 unsigned char *pbBuffer; 275 /** Overlapped I/O structure. */ 276 OVERLAPPED Overlapped; 277 } WINCCWPIPE; 278 typedef WINCCWPIPE *PWINCCWPIPE; 279 252 280 253 281 /** … … 272 300 /** Magic / eyecatcher (WINCHILDCAREWORKER_MAGIC). */ 273 301 ULONG uMagic; 302 /** The worker index. */ 303 unsigned int idxWorker; 274 304 /** The processor group for this worker. */ 275 305 unsigned int iProcessorGroup; … … 280 310 /** The event the thread is idling on. */ 281 311 HANDLE hEvtIdle; 312 /** The pipe catching standard output from a child. */ 313 WINCCWPIPE StdOut; 314 /** The pipe catching standard error from a child. */ 315 WINCCWPIPE StdErr; 316 282 317 /** Pointer to the current child. */ 283 318 PWINCHILD volatile pCurChild; … … 592 627 } 593 628 629 630 /** 631 * Used to flush data we're read but not yet written at the termination of a 632 * process. 633 * 634 * @param pChild The child. 635 * @param pPipe The pipe. 636 */ 637 static void mkWinChildcareWorkerFlushUnwritten(PWINCHILD pChild, PWINCCWPIPE pPipe) 638 { 639 /** @todo integrate with output.c */ 640 DWORD cbUnwritten = pPipe->cbWritten - pPipe->offPendingRead; 641 if (cbUnwritten) 642 { 643 DWORD cbWritten = 0; 644 if (WriteFile(GetStdHandle(pPipe->iWhich == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE), 645 &pPipe->pbBuffer[pPipe->cbWritten], cbUnwritten, &cbWritten, NULL)) 646 pPipe->cbWritten += cbWritten <= cbUnwritten ? cbWritten : cbUnwritten; /* paranoia */ 647 } 648 } 649 650 /** 651 * Adds output to the given standard output for the child. 652 * 653 * There is no pending read when this function is called, so we're free to 654 * reshuffle the buffer if desirable. 655 * 656 * @param pChild The child. 657 * @param iWhich Which standard descriptor number. 658 * @param cbNewData How much more output was caught. 659 */ 660 static void mkWinChildcareWorkerCaughtMoreOutput(PWINCHILD pChild, PWINCCWPIPE pPipe, DWORD cbNewData) 661 { 662 DWORD offStart = pPipe->cbWritten; 663 assert(offStart <= pPipe->offPendingRead); 664 if (cbNewData > 0) 665 { 666 DWORD offRest; 667 668 /* Move offPendingRead ahead by cbRead. */ 669 pPipe->offPendingRead += cbNewData; 670 assert(pPipe->offPendingRead < pPipe->cbBuffer); 671 if (pPipe->offPendingRead > pPipe->cbBuffer) 672 pPipe->offPendingRead = pPipe->cbBuffer; 673 674 /* Locate the last newline in the buffer. */ 675 offRest = pPipe->offPendingRead; 676 while (offRest > offStart && pPipe->pbBuffer[offRest - 1] != '\n') 677 offRest--; 678 679 /* If none were found and we've less than 16 bytes left in the buffer, try 680 find a word boundrary to flush on instead. */ 681 if ( offRest > offStart 682 || pPipe->cbBuffer - pPipe->offPendingRead + offStart > 16) 683 { /* likely */ } 684 else 685 { 686 offRest = pPipe->offPendingRead; 687 while ( offRest > offStart 688 && isalnum(pPipe->pbBuffer[offRest - 1])) 689 offRest--; 690 if (offRest == offStart) 691 offRest = pPipe->offPendingRead; 692 } 693 if (offRest > offStart) 694 { 695 /** @todo integrate with output.c */ 696 /* Write out offStart..offRest. */ 697 DWORD cbToWrite = offRest - offStart; 698 DWORD cbWritten = 0; 699 if (WriteFile(GetStdHandle(pPipe->iWhich == 1 ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE), 700 &pPipe->pbBuffer[offStart], cbToWrite, &cbWritten, NULL)) 701 { 702 offStart += cbWritten <= cbToWrite ? cbWritten : cbToWrite; /* paranoia */ 703 pPipe->cbWritten = offStart; 704 } 705 } 706 } 707 708 /* Shuffle the data to the front of the buffer. */ 709 if (offStart > 0) 710 { 711 DWORD cbUnwritten = pPipe->offPendingRead - offStart; 712 if (cbUnwritten > 0) 713 memmove(pPipe->pbBuffer, &pPipe->pbBuffer[offStart], cbUnwritten); 714 pPipe->offPendingRead -= pPipe->cbWritten; 715 pPipe->cbWritten = 0; 716 } 717 } 718 719 /** 720 * Catches output from the given pipe. 721 * 722 * @param pChild The child. 723 * @param pPipe The pipe. 724 * @param fFlushing Set if we're flushing the pipe after the process 725 * terminated. 726 */ 727 static void mkWinChildcareWorkerCatchOutput(PWINCHILD pChild, PWINCCWPIPE pPipe, BOOL fFlushing) 728 { 729 /* 730 * Deal with already pending read. 731 */ 732 if (pPipe->fReadPending) 733 { 734 DWORD cbRead = 0; 735 if (GetOverlappedResult(pPipe->hPipeMine, &pPipe->Overlapped, &cbRead, !fFlushing)) 736 { 737 mkWinChildcareWorkerCaughtMoreOutput(pChild, pPipe, cbRead); 738 pPipe->fReadPending = FALSE; 739 } 740 else if (fFlushing && GetLastError() == ERROR_IO_INCOMPLETE) 741 { 742 if (pPipe->offPendingRead > pPipe->cbWritten) 743 mkWinChildcareWorkerFlushUnwritten(pChild, pPipe); 744 return; 745 } 746 else 747 { 748 fprintf(stderr, "warning: GetOverlappedResult failed: %u\n", GetLastError()); 749 pPipe->fReadPending = FALSE; 750 if (fFlushing) 751 return; 752 } 753 } 754 755 /* 756 * Read data till one becomes pending. 757 */ 758 for (;;) 759 { 760 DWORD cbRead; 761 762 memset(&pPipe->Overlapped, 0, sizeof(pPipe->Overlapped)); 763 pPipe->Overlapped.hEvent = pPipe->hEvent; 764 ResetEvent(pPipe->hEvent); 765 766 assert(pPipe->offPendingRead < pPipe->cbBuffer); 767 SetLastError(0); 768 cbRead = 0; 769 if (!ReadFile(pPipe->hPipeMine, &pPipe->pbBuffer[pPipe->offPendingRead], 770 pPipe->cbBuffer - pPipe->offPendingRead, &cbRead, &pPipe->Overlapped)) 771 { 772 DWORD dwErr = GetLastError(); 773 if (dwErr == ERROR_IO_PENDING) 774 pPipe->fReadPending = TRUE; 775 else 776 fprintf(stderr, "warning: ReadFile failed on standard %s: %u\n", 777 pPipe->iWhich == 1 ? "output" : "error", GetLastError()); 778 return; 779 } 780 781 mkWinChildcareWorkerCaughtMoreOutput(pChild, pPipe, cbRead); 782 } 783 } 784 594 785 /** 595 786 * Commmon worker for waiting on a child process and retrieving the exit code. … … 599 790 * @param hProcess The process handle. 600 791 * @param pwszJob The job name. 792 * @param fCatchOutput Set if we need to work the output pipes 793 * associated with the worker. 601 794 */ 602 795 static void mkWinChildcareWorkerWaitForProcess(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild, HANDLE hProcess, 603 WCHAR const *pwszJob )796 WCHAR const *pwszJob, BOOL fCatchOutput) 604 797 { 605 798 DWORD const msStart = GetTickCount(); … … 607 800 for (;;) 608 801 { 609 DWORD dwExitCode = -42; 610 DWORD dwStatus = WaitForSingleObject(hProcess, 15001 /*ms*/); 802 /* 803 * Do the waiting and output catching. 804 */ 805 DWORD dwStatus; 806 if (!fCatchOutput) 807 dwStatus = WaitForSingleObject(hProcess, 15001 /*ms*/); 808 else 809 { 810 HANDLE ahHandles[3] = { hProcess, pWorker->StdOut.hEvent, pWorker->StdErr.hEvent }; 811 dwStatus = WaitForMultipleObjects(3, ahHandles, FALSE /*fWaitAll*/, 1000 /*ms*/); 812 if (dwStatus == WAIT_OBJECT_0 + 1) 813 mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdOut, FALSE /*fFlushing*/); 814 else if (dwStatus == WAIT_OBJECT_0 + 2) 815 mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdErr, FALSE /*fFlushing*/); 816 } 611 817 assert(dwStatus != WAIT_FAILED); 818 819 /* 820 * Get the exit code and return if the process was signalled as done. 821 */ 612 822 if (dwStatus == WAIT_OBJECT_0) 613 823 { … … 616 826 { 617 827 pChild->iExitCode = (int)dwExitCode; 828 if (!fCatchOutput) 829 { 830 mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdOut, TRUE /*fFlushing*/); 831 mkWinChildcareWorkerCatchOutput(pChild, &pWorker->StdErr, TRUE /*fFlushing*/); 832 } 618 833 return; 619 834 } 620 835 } 621 else if ( dwStatus == WAIT_TIMEOUT /* whatever */ 836 /* 837 * Loop again if just a timeout or pending output? 838 * Put out a message every 15 or 30 seconds if the job takes a while. 839 */ 840 else if ( dwStatus == WAIT_TIMEOUT 841 || dwStatus == WAIT_OBJECT_0 + 1 842 || dwStatus == WAIT_OBJECT_0 + 2 622 843 || dwStatus == WAIT_IO_COMPLETION) 623 844 { … … 724 945 StartupInfo.lpReserved2 = 0; /* No CRT file handle + descriptor info possible, sorry. */ 725 946 StartupInfo.cbReserved2 = 0; 726 if (!fHaveHandles) 947 if ( !fHaveHandles 948 && !pChild->u.Process.fCatchOutput) 727 949 StartupInfo.dwFlags &= ~STARTF_USESTDHANDLES; 728 950 else … … 781 1003 * First do handle inhertiance as that's the most complicated. 782 1004 */ 783 if (fHaveHandles )1005 if (fHaveHandles || pChild->u.Process.fCatchOutput) 784 1006 { 785 1007 char szErrMsg[128]; 786 BOOL afReplace[3] = 787 { FALSE, pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE, pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE }; 788 HANDLE ahChild[3] = 789 { NULL, pChild->u.Process.hStdOut, pChild->u.Process.hStdErr }; 1008 BOOL afReplace[3]; 1009 HANDLE ahChild[3]; 1010 1011 afReplace[0] = FALSE; 1012 ahChild[0] = INVALID_HANDLE_VALUE; 1013 if (fHaveHandles) 1014 { 1015 afReplace[1] = pChild->u.Process.hStdOut != INVALID_HANDLE_VALUE; 1016 ahChild[1] = pChild->u.Process.hStdOut; 1017 afReplace[2] = pChild->u.Process.hStdErr != INVALID_HANDLE_VALUE; 1018 ahChild[2] = pChild->u.Process.hStdErr; 1019 } 1020 else 1021 { 1022 afReplace[1] = TRUE; 1023 ahChild[1] = pWorker->StdOut.hPipeChild; 1024 afReplace[2] = TRUE; 1025 ahChild[2] = pWorker->StdErr.hPipeChild; 1026 } 790 1027 791 1028 dwErr = nt_child_inject_standard_handles(ProcInfo.hProcess, afReplace, ahChild, szErrMsg, sizeof(szErrMsg)); … … 1670 1907 * Wait for the child to complete. 1671 1908 */ 1672 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess, pwszImageName); 1909 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Process.hProcess, pwszImageName, 1910 pChild->u.Process.fCatchOutput); 1673 1911 } 1674 1912 else … … 1787 2025 static void mkWinChildcareWorkerThreadHandleRedirect(PWINCHILDCAREWORKER pWorker, PWINCHILD pChild) 1788 2026 { 1789 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess, L"kmk_redirect" );2027 mkWinChildcareWorkerWaitForProcess(pWorker, pChild, pChild->u.Redirect.hProcess, L"kmk_redirect", FALSE /*fCatchOutput*/); 1790 2028 } 1791 2029 … … 1831 2069 1832 2070 _InterlockedIncrement((long *)&g_cIdleChildcareWorkers); 2071 _InterlockedExchange((long *)&g_idxLastChildcareWorker, pWorker->idxWorker); 1833 2072 dwStatus = WaitForSingleObject(pWorker->hEvtIdle, INFINITE); 1834 2073 _InterlockedExchange(&pWorker->fIdle, FALSE); … … 1909 2148 1910 2149 /** 2150 * Creates a pipe for catching child output. 2151 * 2152 * This is a custom CreatePipe implementation that allows for overlapped I/O on 2153 * our end of the pipe. Silly that they don't offer an API that does this. 2154 * 2155 * @returns Success indicator. 2156 * @param pPipe The structure for the pipe. 2157 * @param iWhich Which standard descriptor this is a pipe for. 2158 * @param idxWorker The worker index. 2159 */ 2160 static BOOL mkWinChildcareCreateWorkerPipe(PWINCCWPIPE pPipe, unsigned iWhich, unsigned int idxWorker) 2161 { 2162 /* 2163 * We try generate a reasonably unique name from the get go, so this retry 2164 * loop shouldn't really ever be needed. But you never know. 2165 */ 2166 static unsigned s_iSeqNo = 0; 2167 DWORD const cMaxInstances = 1; 2168 DWORD const cbPipe = 4096; 2169 DWORD const cMsTimeout = 0; 2170 unsigned cTries = 256; 2171 while (cTries-- > 0) 2172 { 2173 /* Create the pipe (our end). */ 2174 HANDLE hPipeRead; 2175 DWORD fOpenMode = PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE; 2176 DWORD fPipeMode = PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS; 2177 WCHAR wszName[MAX_PATH]; 2178 s_iSeqNo++; 2179 _snwprintf(wszName, MAX_PATH, L"\\\\.\\pipe\\kmk-winchildren-%u-%u-%u-%s-%u-%u", 2180 GetCurrentProcessId(), GetCurrentThreadId(), idxWorker, iWhich == 1 ? L"out" : L"err", s_iSeqNo, GetTickCount()); 2181 hPipeRead = CreateNamedPipeW(wszName, fOpenMode, fPipeMode, cMaxInstances, cbPipe, cbPipe, cMsTimeout, NULL /*pSecAttr*/); 2182 if (hPipeRead == INVALID_HANDLE_VALUE && GetLastError() == ERROR_INVALID_PARAMETER) 2183 { 2184 fOpenMode &= ~FILE_FLAG_FIRST_PIPE_INSTANCE; 2185 fPipeMode &= ~PIPE_REJECT_REMOTE_CLIENTS; 2186 hPipeRead = CreateNamedPipeW(wszName, fOpenMode, fPipeMode, cMaxInstances, cbPipe, cbPipe, cMsTimeout, NULL /*pSecAttr*/); 2187 } 2188 if (hPipeRead != INVALID_HANDLE_VALUE) 2189 { 2190 /* Connect the other end. */ 2191 HANDLE hPipeWrite = CreateFileW(wszName, GENERIC_WRITE | FILE_READ_ATTRIBUTES, 0 /*fShareMode*/, NULL /*pSecAttr*/, 2192 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL /*hTemplateFile*/); 2193 if (hPipeWrite != INVALID_HANDLE_VALUE) 2194 { 2195 /* 2196 * Create the event object and we're done. 2197 * 2198 * It starts in signalled stated so we don't need special code 2199 * for handing when we start waiting. 2200 */ 2201 HANDLE hEvent = CreateEventW(NULL /*pSecAttr*/, TRUE /*fManualReset*/, TRUE /*fInitialState*/, NULL /*pwszName*/); 2202 if (hEvent != NULL) 2203 { 2204 pPipe->hPipeMine = hPipeRead; 2205 pPipe->hPipeChild = hPipeWrite; 2206 pPipe->hEvent = hEvent; 2207 pPipe->iWhich = iWhich; 2208 pPipe->fReadPending = FALSE; 2209 pPipe->cbBuffer = cbPipe; 2210 pPipe->pbBuffer = xcalloc(cbPipe); 2211 return TRUE; 2212 } 2213 2214 CloseHandle(hPipeWrite); 2215 CloseHandle(hPipeRead); 2216 return FALSE; 2217 } 2218 CloseHandle(hPipeRead); 2219 } 2220 } 2221 return FALSE; 2222 } 2223 2224 /** 2225 * Destroys a childcare worker pipe. 2226 * 2227 * @param pPipe The pipe. 2228 */ 2229 static void mkWinChildcareDeleteWorkerPipe(PWINCCWPIPE pPipe) 2230 { 2231 if (pPipe->hPipeChild != NULL) 2232 { 2233 CloseHandle(pPipe->hPipeChild); 2234 pPipe->hPipeChild = NULL; 2235 } 2236 2237 if (pPipe->hPipeMine != NULL) 2238 { 2239 if (pPipe->fReadPending) 2240 if (!CancelIo(pPipe->hPipeMine)) 2241 WaitForSingleObject(pPipe->hEvent, INFINITE); 2242 CloseHandle(pPipe->hPipeMine); 2243 pPipe->hPipeMine = NULL; 2244 } 2245 2246 if (pPipe->hEvent != NULL) 2247 { 2248 CloseHandle(pPipe->hEvent); 2249 pPipe->hEvent = NULL; 2250 } 2251 2252 if (pPipe->pbBuffer) 2253 { 2254 free(pPipe->pbBuffer); 2255 pPipe->pbBuffer = NULL; 2256 } 2257 } 2258 2259 /** 1911 2260 * Creates another childcare worker. 1912 2261 * … … 1916 2265 { 1917 2266 PWINCHILDCAREWORKER pWorker = (PWINCHILDCAREWORKER)xcalloc(sizeof(*pWorker)); 1918 pWorker->uMagic = WINCHILDCAREWORKER_MAGIC; 1919 pWorker->hEvtIdle = CreateEvent(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pszName*/); 2267 pWorker->uMagic = WINCHILDCAREWORKER_MAGIC; 2268 pWorker->idxWorker = g_cChildCareworkers; 2269 pWorker->hEvtIdle = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/); 1920 2270 if (pWorker->hEvtIdle) 1921 2271 { 1922 /* Before we start the thread, assign it to a processor group. */ 1923 if (g_cProcessorGroups > 1) 1924 { 1925 unsigned int cMaxInGroup; 1926 unsigned int cInGroup; 1927 unsigned int iGroup = g_idxProcessorGroupAllocator % g_cProcessorGroups; 1928 pWorker->iProcessorGroup = iGroup; 1929 1930 /* Advance. We employ a very simple strategy that does 50% in 1931 each group for each group cycle. Odd processor counts are 1932 caught in odd group cycles. The init function selects the 1933 starting group based on make nesting level to avoid stressing 1934 out the first group. */ 1935 cInGroup = ++g_idxProcessorInGroupAllocator; 1936 cMaxInGroup = g_pacProcessorsInGroup[iGroup]; 1937 if ( !(cMaxInGroup & 1) 1938 || !((g_idxProcessorGroupAllocator / g_cProcessorGroups) & 1)) 1939 cMaxInGroup /= 2; 2272 if (mkWinChildcareCreateWorkerPipe(&pWorker->StdOut, 1, pWorker->idxWorker)) 2273 { 2274 if (mkWinChildcareCreateWorkerPipe(&pWorker->StdErr, 2, pWorker->idxWorker)) 2275 { 2276 /* Before we start the thread, assign it to a processor group. */ 2277 if (g_cProcessorGroups > 1) 2278 { 2279 unsigned int cMaxInGroup; 2280 unsigned int cInGroup; 2281 unsigned int iGroup = g_idxProcessorGroupAllocator % g_cProcessorGroups; 2282 pWorker->iProcessorGroup = iGroup; 2283 2284 /* Advance. We employ a very simple strategy that does 50% in 2285 each group for each group cycle. Odd processor counts are 2286 caught in odd group cycles. The init function selects the 2287 starting group based on make nesting level to avoid stressing 2288 out the first group. */ 2289 cInGroup = ++g_idxProcessorInGroupAllocator; 2290 cMaxInGroup = g_pacProcessorsInGroup[iGroup]; 2291 if ( !(cMaxInGroup & 1) 2292 || !((g_idxProcessorGroupAllocator / g_cProcessorGroups) & 1)) 2293 cMaxInGroup /= 2; 2294 else 2295 cMaxInGroup = cMaxInGroup / 2 + 1; 2296 if (cInGroup >= cMaxInGroup) 2297 { 2298 g_idxProcessorInGroupAllocator = 0; 2299 g_idxProcessorGroupAllocator++; 2300 } 2301 } 2302 2303 /* Try start the thread. */ 2304 pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker, 2305 0, &pWorker->tid); 2306 if (pWorker->hThread != NULL) 2307 { 2308 pWorker->idxWorker = g_cChildCareworkers++; /* paranoia */ 2309 g_papChildCareworkers[pWorker->idxWorker] = pWorker; 2310 return pWorker; 2311 } 2312 2313 /* Bail out! */ 2314 fprintf(stderr, "warning! _beginthreadex failed: %u (%s)\n", errno, strerror(errno)); 2315 mkWinChildcareDeleteWorkerPipe(&pWorker->StdErr); 2316 } 1940 2317 else 1941 cMaxInGroup = cMaxInGroup / 2 + 1; 1942 if (cInGroup >= cMaxInGroup) 1943 { 1944 g_idxProcessorInGroupAllocator = 0; 1945 g_idxProcessorGroupAllocator++; 1946 } 1947 } 1948 1949 /* Try start the thread. */ 1950 pWorker->hThread = (HANDLE)_beginthreadex(NULL, 0 /*cbStack*/, mkWinChildcareWorkerThread, pWorker, 1951 0, &pWorker->tid); 1952 if (pWorker->hThread != NULL) 1953 { 1954 g_papChildCareworkers[g_cChildCareworkers++] = pWorker; 1955 return pWorker; 1956 } 2318 fprintf(stderr, "warning! Failed to create stderr pipe: %u\n", GetLastError()); 2319 mkWinChildcareDeleteWorkerPipe(&pWorker->StdOut); 2320 } 2321 else 2322 fprintf(stderr, "warning! Failed to create stdout pipe: %u\n", GetLastError()); 1957 2323 CloseHandle(pWorker->hEvtIdle); 1958 2324 } 2325 else 2326 fprintf(stderr, "warning! CreateEvent failed: %u\n", GetLastError()); 1959 2327 pWorker->uMagic = ~WINCHILDCAREWORKER_MAGIC; 1960 2328 free(pWorker); … … 2254 2622 pChild->u.Process.hStdErr = INVALID_HANDLE_VALUE; 2255 2623 2624 /* We always catch the output in order to prevent character soups courtesy 2625 of the microsoft CRT and/or linkers writing character by character to the 2626 console. Always try write whole lines, even when --output-sync is none. */ 2627 pChild->u.Process.fCatchOutput = TRUE; 2628 2256 2629 return mkWinChildPushToCareWorker(pChild, pPid); 2257 2630 }
Note:
See TracChangeset
for help on using the changeset viewer.