@@ -27,6 +27,8 @@ struct fsentry {
2727 union {
2828 /* Reference count of the directory listing. */
2929 volatile long refcnt ;
30+ /* Handle to wait on the loading thread. */
31+ HANDLE hwait ;
3032 struct {
3133 /* More stat members (only used for file entries). */
3234 off64_t st_size ;
@@ -266,24 +268,51 @@ static inline int fscache_enabled(const char *path)
266268 return enabled > 0 && !is_absolute_path (path );
267269}
268270
271+ /*
272+ * Looks up a cache entry, waits if its being loaded by another thread.
273+ * The mutex must be owned by the calling thread.
274+ */
275+ static struct fsentry * fscache_get_wait (struct fsentry * key )
276+ {
277+ struct fsentry * fse = hashmap_get_entry (& map , key , ent , NULL );
278+
279+ /* return if its a 'real' entry (future entries have refcnt == 0) */
280+ if (!fse || fse -> list || fse -> u .refcnt )
281+ return fse ;
282+
283+ /* create an event and link our key to the future entry */
284+ key -> u .hwait = CreateEvent (NULL , TRUE, FALSE, NULL );
285+ key -> next = fse -> next ;
286+ fse -> next = key ;
287+
288+ /* wait for the loading thread to signal us */
289+ LeaveCriticalSection (& mutex );
290+ WaitForSingleObject (key -> u .hwait , INFINITE );
291+ CloseHandle (key -> u .hwait );
292+ EnterCriticalSection (& mutex );
293+
294+ /* repeat cache lookup */
295+ return hashmap_get_entry (& map , key , ent , NULL );
296+ }
297+
269298/*
270299 * Looks up or creates a cache entry for the specified key.
271300 */
272301static struct fsentry * fscache_get (struct fsentry * key )
273302{
274- struct fsentry * fse ;
303+ struct fsentry * fse , * future , * waiter ;
275304
276305 EnterCriticalSection (& mutex );
277306 /* check if entry is in cache */
278- fse = hashmap_get_entry ( & map , key , ent , NULL );
307+ fse = fscache_get_wait ( key );
279308 if (fse ) {
280309 fsentry_addref (fse );
281310 LeaveCriticalSection (& mutex );
282311 return fse ;
283312 }
284313 /* if looking for a file, check if directory listing is in cache */
285314 if (!fse && key -> list ) {
286- fse = hashmap_get_entry ( & map , key -> list , ent , NULL );
315+ fse = fscache_get_wait ( key -> list );
287316 if (fse ) {
288317 LeaveCriticalSection (& mutex );
289318 /* dir entry without file entry -> file doesn't exist */
@@ -292,16 +321,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
292321 }
293322 }
294323
324+ /* add future entry to indicate that we're loading it */
325+ future = key -> list ? key -> list : key ;
326+ future -> next = NULL ;
327+ future -> u .refcnt = 0 ;
328+ hashmap_add (& map , & future -> ent );
329+
295330 /* create the directory listing (outside mutex!) */
296331 LeaveCriticalSection (& mutex );
297- fse = fsentry_create_list (key -> list ? key -> list : key );
298- if (!fse )
332+ fse = fsentry_create_list (future );
333+ EnterCriticalSection (& mutex );
334+
335+ /* remove future entry and signal waiting threads */
336+ hashmap_remove (& map , & future -> ent , NULL );
337+ waiter = future -> next ;
338+ while (waiter ) {
339+ HANDLE h = waiter -> u .hwait ;
340+ waiter = waiter -> next ;
341+ SetEvent (h );
342+ }
343+
344+ /* leave on error (errno set by fsentry_create_list) */
345+ if (!fse ) {
346+ LeaveCriticalSection (& mutex );
299347 return NULL ;
348+ }
300349
301- EnterCriticalSection (& mutex );
302- /* add directory listing if it hasn't been added by some other thread */
303- if (!hashmap_get_entry (& map , key , ent , NULL ))
304- fscache_add (fse );
350+ /* add directory listing to the cache */
351+ fscache_add (fse );
305352
306353 /* lookup file entry if requested (fse already points to directory) */
307354 if (key -> list )
0 commit comments