@@ -33,6 +33,8 @@ struct fsentry {
3333 union {
3434 /* Reference count of the directory listing. */
3535 volatile long refcnt ;
36+ /* Handle to wait on the loading thread. */
37+ HANDLE hwait ;
3638 struct {
3739 /* More stat members (only used for file entries). */
3840 off64_t st_size ;
@@ -245,24 +247,51 @@ static inline int fscache_enabled(const char *path)
245247 return enabled > 0 && !is_absolute_path (path );
246248}
247249
250+ /*
251+ * Looks up a cache entry, waits if its being loaded by another thread.
252+ * The mutex must be owned by the calling thread.
253+ */
254+ static struct fsentry * fscache_get_wait (struct fsentry * key )
255+ {
256+ struct fsentry * fse = hashmap_get (& map , key , NULL );
257+
258+ /* return if its a 'real' entry (future entries have refcnt == 0) */
259+ if (!fse || fse -> list || fse -> refcnt )
260+ return fse ;
261+
262+ /* create an event and link our key to the future entry */
263+ key -> hwait = CreateEvent (NULL , TRUE, FALSE, NULL );
264+ key -> next = fse -> next ;
265+ fse -> next = key ;
266+
267+ /* wait for the loading thread to signal us */
268+ LeaveCriticalSection (& mutex );
269+ WaitForSingleObject (key -> hwait , INFINITE );
270+ CloseHandle (key -> hwait );
271+ EnterCriticalSection (& mutex );
272+
273+ /* repeat cache lookup */
274+ return hashmap_get (& map , key , NULL );
275+ }
276+
248277/*
249278 * Looks up or creates a cache entry for the specified key.
250279 */
251280static struct fsentry * fscache_get (struct fsentry * key )
252281{
253- struct fsentry * fse ;
282+ struct fsentry * fse , * future , * waiter ;
254283
255284 EnterCriticalSection (& mutex );
256285 /* check if entry is in cache */
257- fse = hashmap_get ( & map , key , NULL );
286+ fse = fscache_get_wait ( key );
258287 if (fse ) {
259288 fsentry_addref (fse );
260289 LeaveCriticalSection (& mutex );
261290 return fse ;
262291 }
263292 /* if looking for a file, check if directory listing is in cache */
264293 if (!fse && key -> list ) {
265- fse = hashmap_get ( & map , key -> list , NULL );
294+ fse = fscache_get_wait ( key -> list );
266295 if (fse ) {
267296 LeaveCriticalSection (& mutex );
268297 /* dir entry without file entry -> file doesn't exist */
@@ -271,16 +300,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
271300 }
272301 }
273302
303+ /* add future entry to indicate that we're loading it */
304+ future = key -> list ? key -> list : key ;
305+ future -> next = NULL ;
306+ future -> refcnt = 0 ;
307+ hashmap_add (& map , future );
308+
274309 /* create the directory listing (outside mutex!) */
275310 LeaveCriticalSection (& mutex );
276- fse = fsentry_create_list (key -> list ? key -> list : key );
277- if (!fse )
311+ fse = fsentry_create_list (future );
312+ EnterCriticalSection (& mutex );
313+
314+ /* remove future entry and signal waiting threads */
315+ hashmap_remove (& map , future , NULL );
316+ waiter = future -> next ;
317+ while (waiter ) {
318+ HANDLE h = waiter -> hwait ;
319+ waiter = waiter -> next ;
320+ SetEvent (h );
321+ }
322+
323+ /* leave on error (errno set by fsentry_create_list) */
324+ if (!fse ) {
325+ LeaveCriticalSection (& mutex );
278326 return NULL ;
327+ }
279328
280- EnterCriticalSection (& mutex );
281- /* add directory listing if it hasn't been added by some other thread */
282- if (!hashmap_get (& map , key , NULL ))
283- fscache_add (fse );
329+ /* add directory listing to the cache */
330+ fscache_add (fse );
284331
285332 /* lookup file entry if requested (fse already points to directory) */
286333 if (key -> list )
0 commit comments