@@ -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 ;
@@ -268,24 +270,51 @@ static inline int fscache_enabled(const char *path)
268270 return enabled > 0 && !is_absolute_path (path );
269271}
270272
273+ /*
274+ * Looks up a cache entry, waits if its being loaded by another thread.
275+ * The mutex must be owned by the calling thread.
276+ */
277+ static struct fsentry * fscache_get_wait (struct fsentry * key )
278+ {
279+ struct fsentry * fse = hashmap_get_entry (& map , key , ent , NULL );
280+
281+ /* return if its a 'real' entry (future entries have refcnt == 0) */
282+ if (!fse || fse -> list || fse -> u .refcnt )
283+ return fse ;
284+
285+ /* create an event and link our key to the future entry */
286+ key -> u .hwait = CreateEvent (NULL , TRUE, FALSE, NULL );
287+ key -> next = fse -> next ;
288+ fse -> next = key ;
289+
290+ /* wait for the loading thread to signal us */
291+ LeaveCriticalSection (& mutex );
292+ WaitForSingleObject (key -> u .hwait , INFINITE );
293+ CloseHandle (key -> u .hwait );
294+ EnterCriticalSection (& mutex );
295+
296+ /* repeat cache lookup */
297+ return hashmap_get_entry (& map , key , ent , NULL );
298+ }
299+
271300/*
272301 * Looks up or creates a cache entry for the specified key.
273302 */
274303static struct fsentry * fscache_get (struct fsentry * key )
275304{
276- struct fsentry * fse ;
305+ struct fsentry * fse , * future , * waiter ;
277306
278307 EnterCriticalSection (& mutex );
279308 /* check if entry is in cache */
280- fse = hashmap_get_entry ( & map , key , ent , NULL );
309+ fse = fscache_get_wait ( key );
281310 if (fse ) {
282311 fsentry_addref (fse );
283312 LeaveCriticalSection (& mutex );
284313 return fse ;
285314 }
286315 /* if looking for a file, check if directory listing is in cache */
287316 if (!fse && key -> list ) {
288- fse = hashmap_get_entry ( & map , key -> list , ent , NULL );
317+ fse = fscache_get_wait ( key -> list );
289318 if (fse ) {
290319 LeaveCriticalSection (& mutex );
291320 /* dir entry without file entry -> file doesn't exist */
@@ -294,16 +323,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
294323 }
295324 }
296325
326+ /* add future entry to indicate that we're loading it */
327+ future = key -> list ? key -> list : key ;
328+ future -> next = NULL ;
329+ future -> u .refcnt = 0 ;
330+ hashmap_add (& map , & future -> ent );
331+
297332 /* create the directory listing (outside mutex!) */
298333 LeaveCriticalSection (& mutex );
299- fse = fsentry_create_list (key -> list ? key -> list : key );
300- if (!fse )
334+ fse = fsentry_create_list (future );
335+ EnterCriticalSection (& mutex );
336+
337+ /* remove future entry and signal waiting threads */
338+ hashmap_remove (& map , & future -> ent , NULL );
339+ waiter = future -> next ;
340+ while (waiter ) {
341+ HANDLE h = waiter -> u .hwait ;
342+ waiter = waiter -> next ;
343+ SetEvent (h );
344+ }
345+
346+ /* leave on error (errno set by fsentry_create_list) */
347+ if (!fse ) {
348+ LeaveCriticalSection (& mutex );
301349 return NULL ;
350+ }
302351
303- EnterCriticalSection (& mutex );
304- /* add directory listing if it hasn't been added by some other thread */
305- if (!hashmap_get_entry (& map , key , ent , NULL ))
306- fscache_add (fse );
352+ /* add directory listing to the cache */
353+ fscache_add (fse );
307354
308355 /* lookup file entry if requested (fse already points to directory) */
309356 if (key -> list )
0 commit comments