VirtualBox

Changeset 3466 in kBuild for trunk


Ignore:
Timestamp:
Sep 15, 2020 9:28:37 PM (4 years ago)
Author:
bird
Message:

kash: Introduced asynchronous closing of files we're written to on windows because it may take a while on NTFS. We only need to make sure async CloseHandle are done when executing or forking.

Location:
trunk/src/kash
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/kash/Makefile.kmk

    r3458 r3466  
    4040kash_DEFS += SH_FORKED_MODE
    4141else
    42 kash_DEFS += KASH_SEPARATE_PARSER_ALLOCATOR
     42kash_DEFS += KASH_SEPARATE_PARSER_ALLOCATOR KASH_ASYNC_CLOSE_HANDLE
    4343endif
    4444kash_DEFS.debug = DEBUG=2
  • trunk/src/kash/shfile.c

    r3465 r3466  
    143143
    144144
    145 /*******************************************************************************
    146 *   Global Variables                                                           *
    147 *******************************************************************************/
     145/*********************************************************************************************************************************
     146*   Global Variables                                                                                                             *
     147*********************************************************************************************************************************/
    148148#if K_OS == K_OS_WINDOWS
    149149static int                              g_shfile_globals_initialized = 0;
     
    151151static PFN_NtQueryDirectoryFile         g_pfnNtQueryDirectoryFile = NULL;
    152152static PFN_RtlUnicodeStringToAnsiString g_pfnRtlUnicodeStringToAnsiString = NULL;
     153# ifdef KASH_ASYNC_CLOSE_HANDLE
     154/** Data for the asynchronous CloseHandle machinery. */
     155static 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
    153184#endif /* K_OS_WINDOWS */
     185
     186
     187/*********************************************************************************************************************************
     188*   Internal Functions                                                                                                           *
     189*********************************************************************************************************************************/
     190#ifdef SHFILE_IN_USE
     191# if K_OS == K_OS_WINDOWS
     192static HANDLE shfile_set_inherit_win(shfile *pfd, int set);
     193# endif
     194#endif
    154195
    155196
     
    178219# endif /* DEBUG */
    179220
     221# if K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE)
     222/**
     223 * The closer thread function.
     224 */
     225static 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 */
     291static 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 */
     358void 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
    180388/**
    181389 * Close the specified native handle.
     
    183391 * @param   native      The native file handle.
    184392 * @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 */
     395static void shfile_native_close(intptr_t native, shfile *file, KBOOL inheritable)
    187396{
    188397# 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    {
    189430#  ifdef DEBUG
    190431    KU64 ns = shfile_nano_ts();
    191     BOOL fRc2 = FlushFileBuffers((HANDLE)native);
    192     KU64 ns2 = shfile_nano_ts();
    193432    BOOL fRc = CloseHandle((HANDLE)native);
    194433    assert(fRc); K_NOREF(fRc);
    195     ns2 -= ns;
    196434    ns = shfile_nano_ts() - ns;
    197435    if (ns > 1000000)
    198         TRACE2((NULL, "shfile_native_close: %u ns (%u) %p oflags=%#x %s\n",
    199                 ns, ns2, 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));
    200438#  else
    201439    BOOL fRc = CloseHandle((HANDLE)native);
    202440    assert(fRc); K_NOREF(fRc);
    203441#  endif
    204 # else
     442    }
     443
     444# else  /* K_OS != K_OS_WINDOWS */
    205445    int s = errno;
    206446    close(native);
    207447    errno = s;
    208 # endif
     448# endif /* K_OS != K_OS_WINDOWS */
    209449    K_NOREF(file);
    210450}
     
    281521    {
    282522        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);
    284524        errno = EMFILE;
    285525        return -1;
     
    326566    }
    327567    else
    328         shfile_native_close(native, NULL);
     568        shfile_native_close(native, NULL, K_FALSE);
    329569
    330570    shmtx_leave(&pfdtab->mtx, &tmp);
     
    659899            /* fatal error */
    660900        }
     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
    661931        g_shfile_globals_initialized = 1;
    662932    }
     
    11701440
    11711441            info->inherithandles  = 1;
     1442# ifdef KASH_ASYNC_CLOSE_HANDLE
     1443            info->startsuspended  = g_shfile_async_close.num_pending > 0;
     1444# else
    11721445            info->startsuspended  = 0;
     1446# endif
    11731447            strtinfo->cbReserved2 = (unsigned short)cbData;
    11741448            strtinfo->lpReserved2 = pbData;
     
    12421516                    && !(file->shflags & SHFILE_FLAGS_TRACE))
    12431517                {
    1244                     shfile_native_close(file->native, file);
     1518                    shfile_native_close(file->native, file, info->inherithandles);
    12451519
    12461520                    file->fd = -1;
     
    13821656        ns = shfile_nano_ts() - ns;
    13831657        if (ns > 1000000)
    1384             TRACE2((NULL, "shfile_open: %s ns 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));
    13851659#  endif
    13861660        if (hFile != INVALID_HANDLE_VALUE)
     
    15201794        {
    15211795            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);
    15231797
    15241798            /* setup the target. */
     
    16401914    if (file)
    16411915    {
    1642         shfile_native_close(file->native, file);
     1916        shfile_native_close(file->native, file, K_FALSE);
    16431917
    16441918        file->fd = -1;
     
    17181992# endif
    17191993
     1994        file->shflags |= SHFILE_FLAGS_DIRTY; /* there should be no concurrent access, so this is safe. */
    17201995        shfile_put(pfdtab, file, &tmp);
    17211996    }
  • trunk/src/kash/shfile.h

    r3465 r3466  
    124124#define SHFILE_FLAGS_DIR                0x0020
    125125#define SHFILE_FLAGS_TTY                0x0030
     126#define SHFILE_FLAGS_DIRTY              0x0100  /**< The file has been written to. */
    126127/** @} */
    127128
     
    151152int shfile_exec_win(shfdtab *pfdtab, int prepare, shfdexecwin *info);
    152153int shfile_exec_unix(shfdtab *pfdtab);
     154#if K_OS == K_OS_WINDOWS && defined(KASH_ASYNC_CLOSE_HANDLE)
     155void shfile_async_close_sync(void);
     156#endif
    153157
    154158int shfile_open(shfdtab *, const char *, unsigned, mode_t);
  • trunk/src/kash/shinstance.c

    r3464 r3466  
    20912091            {
    20922092                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;
    20952098                if (!rc)
    20962099                {
     2100#  ifdef KASH_ASYNC_CLOSE_HANDLE
     2101                    shfile_async_close_sync();
     2102#  endif
    20972103                    rc = ResumeThread(ProcInfo.hThread);
    20982104                    if (!rc)
     
    21212127                if (GetExitCodeProcess(ProcInfo.hProcess, &dwExitCode))
    21222128                {
    2123 #ifndef SH_FORKED_MODE
     2129#  ifndef SH_FORKED_MODE
    21242130                    shsubshellstatus_signal_and_release(psh, (int)dwExitCode);
    2125 #endif
     2131#  endif
    21262132                    CloseHandle(ProcInfo.hProcess);
    21272133                    ProcInfo.hProcess = INVALID_HANDLE_VALUE;
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette