- Timestamp:
- Sep 15, 2020 9:28:37 PM (4 years ago)
- Location:
- trunk/src/kash
- Files:
-
- 4 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/kash/Makefile.kmk
r3458 r3466 40 40 kash_DEFS += SH_FORKED_MODE 41 41 else 42 kash_DEFS += KASH_SEPARATE_PARSER_ALLOCATOR 42 kash_DEFS += KASH_SEPARATE_PARSER_ALLOCATOR KASH_ASYNC_CLOSE_HANDLE 43 43 endif 44 44 kash_DEFS.debug = DEBUG=2 -
trunk/src/kash/shfile.c
r3465 r3466 143 143 144 144 145 /******************************************************************************* 146 * Global Variables *147 ******************************************************************************* /145 /********************************************************************************************************************************* 146 * Global Variables * 147 *********************************************************************************************************************************/ 148 148 #if K_OS == K_OS_WINDOWS 149 149 static int g_shfile_globals_initialized = 0; … … 151 151 static PFN_NtQueryDirectoryFile g_pfnNtQueryDirectoryFile = NULL; 152 152 static PFN_RtlUnicodeStringToAnsiString g_pfnRtlUnicodeStringToAnsiString = NULL; 153 # ifdef KASH_ASYNC_CLOSE_HANDLE 154 /** Data for the asynchronous CloseHandle machinery. */ 155 static struct shfileasyncclose 156 { 157 /** Mutex protecting the asynchronous CloseHandle stuff. */ 158 shmtx mtx; 159 /** Handle to event that the closer-threads are waiting on. */ 160 HANDLE evt_workers; 161 /** The ring buffer read index (for closer-threads). */ 162 unsigned volatile idx_read; 163 /** The ring buffer write index (for shfile_native_close). 164 * When idx_read and idx_write are the same, the ring buffer is empty. */ 165 unsigned volatile idx_write; 166 /** Number of handles currently being pending closure (handles + current 167 * CloseHandle calls). */ 168 unsigned volatile num_pending; 169 /** Set if the threads should terminate. */ 170 KBOOL volatile terminate_threads; 171 /** Set if one or more shell threads have requested evt_sync to be signalled 172 * when there are no more pending requests. */ 173 KBOOL volatile signal_sync; 174 /** Number of threads that have been spawned. */ 175 KU8 num_threads; 176 /** Handle to event that the shell threads are waiting on to sync. */ 177 HANDLE evt_sync; 178 /** Ring buffer containing handles to be closed. */ 179 HANDLE handles[32]; 180 /** Worker threads doing the asynchronous closing. */ 181 HANDLE threads[8]; 182 } g_shfile_async_close; 183 # endif 153 184 #endif /* K_OS_WINDOWS */ 185 186 187 /********************************************************************************************************************************* 188 * Internal Functions * 189 *********************************************************************************************************************************/ 190 #ifdef SHFILE_IN_USE 191 # if K_OS == K_OS_WINDOWS 192 static HANDLE shfile_set_inherit_win(shfile *pfd, int set); 193 # endif 194 #endif 154 195 155 196 … … 178 219 # endif /* DEBUG */ 179 220 221 # if K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE) 222 /** 223 * The closer thread function. 224 */ 225 static unsigned __stdcall shfile_async_close_handle_thread(void *ignored) 226 { 227 KBOOL decrement_pending = K_FALSE; 228 while (!g_shfile_async_close.terminate_threads) 229 { 230 HANDLE toclose; 231 unsigned idx; 232 shmtxtmp tmp; 233 234 /* 235 * Grab a handle if there is one: 236 */ 237 shmtx_enter(&g_shfile_async_close.mtx, &tmp); 238 239 if (decrement_pending) 240 { 241 assert(g_shfile_async_close.num_pending > 0); 242 g_shfile_async_close.num_pending -= 1; 243 } 244 245 idx = g_shfile_async_close.idx_read % K_ELEMENTS(g_shfile_async_close.handles); 246 if (idx != g_shfile_async_close.idx_write % K_ELEMENTS(g_shfile_async_close.handles)) 247 { 248 assert(g_shfile_async_close.num_pending > 0); 249 toclose = g_shfile_async_close.handles[idx]; 250 assert(toclose); 251 g_shfile_async_close.handles[idx] = NULL; 252 g_shfile_async_close.idx_read = (idx + 1) % K_ELEMENTS(g_shfile_async_close.handles); 253 } 254 else 255 { 256 /* Signal waiters if requested and we've reached zero pending requests. */ 257 if (g_shfile_async_close.signal_sync && g_shfile_async_close.num_pending == 0) 258 { 259 BOOL rc = SetEvent(g_shfile_async_close.evt_sync); 260 assert(rc); 261 g_shfile_async_close.signal_sync = FALSE; 262 } 263 toclose = NULL; 264 } 265 shmtx_leave(&g_shfile_async_close.mtx, &tmp); 266 267 /* Do the closing if we have something to close, otherwise wait for work to arrive. */ 268 if (toclose != NULL) 269 { 270 BOOL rc = CloseHandle(toclose); 271 assert(rc); 272 decrement_pending = K_TRUE; 273 } 274 else 275 { 276 DWORD dwRet = WaitForSingleObject(g_shfile_async_close.evt_workers, 10000 /*ms*/); 277 assert(dwRet == WAIT_OBJECT_0 || dwRet == WAIT_TIMEOUT); 278 decrement_pending = K_FALSE; 279 } 280 } 281 282 K_NOREF(ignored); 283 return 0; 284 } 285 286 /** 287 * Try submit a handle for automatic closing. 288 * 289 * @returns K_TRUE if submitted successfully, K_FALSE if not. 290 */ 291 static KBOOL shfile_async_close_submit(HANDLE toclose) 292 { 293 KBOOL ret; 294 unsigned idx; 295 unsigned idx_next; 296 unsigned idx_read; 297 shmtxtmp tmp; 298 shmtx_enter(&g_shfile_async_close.mtx, &tmp); 299 300 /* Get the write index and check that there is a free slot in the buffer: */ 301 idx = g_shfile_async_close.idx_write % K_ELEMENTS(g_shfile_async_close.handles); 302 idx_next = (idx + 1) % K_ELEMENTS(g_shfile_async_close.handles); 303 idx_read = g_shfile_async_close.idx_read % K_ELEMENTS(g_shfile_async_close.handles); 304 if (idx_next != idx_read) 305 { 306 unsigned num_pending, num_threads; 307 308 /* Write the handle to the ring buffer: */ 309 assert(g_shfile_async_close.handles[idx] == NULL); 310 g_shfile_async_close.handles[idx] = toclose; 311 g_shfile_async_close.idx_write = idx_next; 312 313 num_pending = g_shfile_async_close.num_pending + 1; 314 g_shfile_async_close.num_pending = num_pending; 315 316 ret = SetEvent(g_shfile_async_close.evt_workers); 317 assert(ret); 318 if (ret) 319 { 320 /* If we have more pending requests than threads, create a new thread. */ 321 num_threads = g_shfile_async_close.num_threads; 322 if (num_pending > num_threads && num_threads < K_ELEMENTS(g_shfile_async_close.threads)) 323 { 324 unsigned tid = 0; 325 intptr_t hThread = _beginthreadex(NULL /*security*/, 0 /*stack_size*/, shfile_async_close_handle_thread, 326 NULL /*arg*/, 0 /*initflags*/, &tid); 327 assert(hThread != -1); 328 if (hThread != -1) 329 { 330 g_shfile_async_close.threads[num_threads] = (HANDLE)hThread; 331 g_shfile_async_close.num_threads = num_threads + 1; 332 } 333 else if (num_threads == 0) 334 ret = K_FALSE; 335 } 336 } 337 338 /* cleanup on failure. */ 339 if (ret) 340 { /* likely */ } 341 else 342 { 343 g_shfile_async_close.handles[idx] = NULL; 344 g_shfile_async_close.idx_write = idx; 345 g_shfile_async_close.num_pending = num_pending - 1; 346 } 347 } 348 else 349 ret = K_FALSE; 350 351 shmtx_leave(&g_shfile_async_close.mtx, &tmp); 352 return ret; 353 } 354 355 /** 356 * Wait for all pending CloseHandle calls to complete. 357 */ 358 void shfile_async_close_sync(void) 359 { 360 shmtxtmp tmp; 361 shmtx_enter(&g_shfile_async_close.mtx, &tmp); 362 363 if (g_shfile_async_close.num_pending > 0) 364 { 365 DWORD dwRet; 366 367 /** @todo help out? */ 368 if (!g_shfile_async_close.signal_sync) 369 { 370 BOOL rc = ResetEvent(g_shfile_async_close.evt_sync); 371 assert(rc); K_NOREF(rc); 372 373 g_shfile_async_close.signal_sync = K_TRUE; 374 } 375 376 shmtx_leave(&g_shfile_async_close.mtx, &tmp); 377 378 dwRet = WaitForSingleObject(g_shfile_async_close.evt_sync, 10000 /*ms*/); 379 assert(dwRet == WAIT_OBJECT_0); 380 assert(g_shfile_async_close.num_pending == 0); 381 } 382 else 383 shmtx_leave(&g_shfile_async_close.mtx, &tmp); 384 } 385 386 # endif /* K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE) */ 387 180 388 /** 181 389 * Close the specified native handle. … … 183 391 * @param native The native file handle. 184 392 * @param file The file table entry if available. 185 */ 186 static void shfile_native_close(intptr_t native, shfile *file) 393 * @param inheritable The inheritability of the handle on windows, K_FALSE elsewhere. 394 */ 395 static void shfile_native_close(intptr_t native, shfile *file, KBOOL inheritable) 187 396 { 188 397 # if K_OS == K_OS_WINDOWS 398 # ifdef KASH_ASYNC_CLOSE_HANDLE 399 /* 400 * CloseHandle may take several milliseconds on NTFS after we've appended 401 * a few bytes to a file. When a script uses lots of 'echo line-text >> file' 402 * we end up executing very slowly, even if 'echo' is builtin and the statement 403 * requires no subshells to be spawned. 404 * 405 * So, detect problematic handles and do CloseHandle asynchronously. When 406 * executing a child process, we probably will have to make sure the CloseHandle 407 * operation has completed before we let do ResumeThread on the child to make 408 * 100% sure we can't have any sharing conflicts. Sharing conflicts are not a 409 * problem for builtin commands. 410 * 411 * If child processes are spawned using handle inheritance and the handle in 412 * question is inheritable, we will have to fix the inheriability before pushing 413 * on the async-close queue. This shouldn't have the CloseHandle issues. 414 */ 415 if ( file 416 && (file->shflags & (SHFILE_FLAGS_DIRTY | SHFILE_FLAGS_TYPE_MASK)) 417 == (SHFILE_FLAGS_DIRTY | SHFILE_FLAGS_FILE)) 418 { 419 if (inheritable) 420 native = (intptr_t)shfile_set_inherit_win(file, 0); 421 if (shfile_async_close_submit((HANDLE)native)) 422 return; 423 } 424 425 /* 426 * Otherwise close it right here: 427 */ 428 # endif 429 { 189 430 # ifdef DEBUG 190 431 KU64 ns = shfile_nano_ts(); 191 BOOL fRc2 = FlushFileBuffers((HANDLE)native);192 KU64 ns2 = shfile_nano_ts();193 432 BOOL fRc = CloseHandle((HANDLE)native); 194 433 assert(fRc); K_NOREF(fRc); 195 ns2 -= ns;196 434 ns = shfile_nano_ts() - ns; 197 435 if (ns > 1000000) 198 TRACE2((NULL, "shfile_native_close: %u ns (%u)%p oflags=%#x %s\n",199 ns, n s2, native, file ? file->oflags : 0, file ? file->dbgname : NULL));436 TRACE2((NULL, "shfile_native_close: %u ns %p oflags=%#x %s\n", 437 ns, native, file ? file->oflags : 0, file ? file->dbgname : NULL)); 200 438 # else 201 439 BOOL fRc = CloseHandle((HANDLE)native); 202 440 assert(fRc); K_NOREF(fRc); 203 441 # endif 204 # else 442 } 443 444 # else /* K_OS != K_OS_WINDOWS */ 205 445 int s = errno; 206 446 close(native); 207 447 errno = s; 208 # endif 448 # endif /* K_OS != K_OS_WINDOWS */ 209 449 K_NOREF(file); 210 450 } … … 281 521 { 282 522 TRACE2((NULL, "shfile_insert: fdMin=%d is out of bounds; native=%p %s\n", fdMin, native, dbgname)); 283 shfile_native_close(native, NULL );523 shfile_native_close(native, NULL, K_FALSE); 284 524 errno = EMFILE; 285 525 return -1; … … 326 566 } 327 567 else 328 shfile_native_close(native, NULL );568 shfile_native_close(native, NULL, K_FALSE); 329 569 330 570 shmtx_leave(&pfdtab->mtx, &tmp); … … 659 899 /* fatal error */ 660 900 } 901 902 # ifdef KASH_ASYNC_CLOSE_HANDLE 903 /* 904 * Init the async CloseHandle state. 905 */ 906 shmtx_init(&g_shfile_async_close.mtx); 907 g_shfile_async_close.evt_workers = CreateEventW(NULL, FALSE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/); 908 g_shfile_async_close.evt_sync = CreateEventW(NULL, TRUE /*fManualReset*/, FALSE /*fInitialState*/, NULL /*pwszName*/); 909 if ( !g_shfile_async_close.evt_workers 910 || !g_shfile_async_close.evt_sync) 911 { 912 fprintf(stderr, "fatal error: CreateEventW failed: %u\n", GetLastError()); 913 _exit(19); 914 } 915 g_shfile_async_close.idx_read = 0; 916 g_shfile_async_close.idx_write = 0; 917 g_shfile_async_close.num_pending = 0; 918 g_shfile_async_close.terminate_threads = K_FALSE; 919 g_shfile_async_close.signal_sync = K_FALSE; 920 g_shfile_async_close.num_threads = 0; 921 { 922 unsigned i = K_ELEMENTS(g_shfile_async_close.handles); 923 while (i-- > 0) 924 g_shfile_async_close.handles[i] = NULL; 925 i = K_ELEMENTS(g_shfile_async_close.threads); 926 while (i-- > 0) 927 g_shfile_async_close.threads[i] = NULL; 928 } 929 # endif 930 661 931 g_shfile_globals_initialized = 1; 662 932 } … … 1170 1440 1171 1441 info->inherithandles = 1; 1442 # ifdef KASH_ASYNC_CLOSE_HANDLE 1443 info->startsuspended = g_shfile_async_close.num_pending > 0; 1444 # else 1172 1445 info->startsuspended = 0; 1446 # endif 1173 1447 strtinfo->cbReserved2 = (unsigned short)cbData; 1174 1448 strtinfo->lpReserved2 = pbData; … … 1242 1516 && !(file->shflags & SHFILE_FLAGS_TRACE)) 1243 1517 { 1244 shfile_native_close(file->native, file );1518 shfile_native_close(file->native, file, info->inherithandles); 1245 1519 1246 1520 file->fd = -1; … … 1382 1656 ns = shfile_nano_ts() - ns; 1383 1657 if (ns > 1000000) 1384 TRACE2((NULL, "shfile_open: % sns hFile=%p (%d) %s\n", ns, hFile, GetLastError(), absname));1658 TRACE2((NULL, "shfile_open: %u ns hFile=%p (%d) %s\n", ns, hFile, GetLastError(), absname)); 1385 1659 # endif 1386 1660 if (hFile != INVALID_HANDLE_VALUE) … … 1520 1794 { 1521 1795 if (pfdtab->tab[fdto].fd != -1) 1522 shfile_native_close(pfdtab->tab[fdto].native, &pfdtab->tab[fdto] );1796 shfile_native_close(pfdtab->tab[fdto].native, &pfdtab->tab[fdto], K_FALSE); 1523 1797 1524 1798 /* setup the target. */ … … 1640 1914 if (file) 1641 1915 { 1642 shfile_native_close(file->native, file );1916 shfile_native_close(file->native, file, K_FALSE); 1643 1917 1644 1918 file->fd = -1; … … 1718 1992 # endif 1719 1993 1994 file->shflags |= SHFILE_FLAGS_DIRTY; /* there should be no concurrent access, so this is safe. */ 1720 1995 shfile_put(pfdtab, file, &tmp); 1721 1996 } -
trunk/src/kash/shfile.h
r3465 r3466 124 124 #define SHFILE_FLAGS_DIR 0x0020 125 125 #define SHFILE_FLAGS_TTY 0x0030 126 #define SHFILE_FLAGS_DIRTY 0x0100 /**< The file has been written to. */ 126 127 /** @} */ 127 128 … … 151 152 int shfile_exec_win(shfdtab *pfdtab, int prepare, shfdexecwin *info); 152 153 int shfile_exec_unix(shfdtab *pfdtab); 154 #if K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE) 155 void shfile_async_close_sync(void); 156 #endif 153 157 154 158 int shfile_open(shfdtab *, const char *, unsigned, mode_t); -
trunk/src/kash/shinstance.c
r3464 r3466 2091 2091 { 2092 2092 char errmsg[512]; 2093 rc = nt_child_inject_standard_handles(ProcInfo.hProcess, fdinfo.replacehandles, (HANDLE*)&fdinfo.handles[0], 2094 errmsg, sizeof(errmsg)); 2093 if (!fdinfo.inherithandles) 2094 rc = nt_child_inject_standard_handles(ProcInfo.hProcess, fdinfo.replacehandles, 2095 (HANDLE *)&fdinfo.handles[0], errmsg, sizeof(errmsg)); 2096 else 2097 rc = 0; 2095 2098 if (!rc) 2096 2099 { 2100 # ifdef KASH_ASYNC_CLOSE_HANDLE 2101 shfile_async_close_sync(); 2102 # endif 2097 2103 rc = ResumeThread(ProcInfo.hThread); 2098 2104 if (!rc) … … 2121 2127 if (GetExitCodeProcess(ProcInfo.hProcess, &dwExitCode)) 2122 2128 { 2123 # ifndef SH_FORKED_MODE2129 # ifndef SH_FORKED_MODE 2124 2130 shsubshellstatus_signal_and_release(psh, (int)dwExitCode); 2125 # endif2131 # endif 2126 2132 CloseHandle(ProcInfo.hProcess); 2127 2133 ProcInfo.hProcess = INVALID_HANDLE_VALUE;
Note:
See TracChangeset
for help on using the changeset viewer.