@@ -26,6 +26,8 @@ struct fsentry {
2626 union {
2727 /* Reference count of the directory listing. */
2828 volatile long refcnt ;
29+ /* Handle to wait on the loading thread. */
30+ HANDLE hwait ;
2931 struct {
3032 /* More stat members (only used for file entries). */
3133 off64_t st_size ;
@@ -260,24 +262,51 @@ static inline int fscache_enabled(const char *path)
260262 return enabled > 0 && !is_absolute_path (path );
261263}
262264
265+ /*
266+ * Looks up a cache entry, waits if its being loaded by another thread.
267+ * The mutex must be owned by the calling thread.
268+ */
269+ static struct fsentry * fscache_get_wait (struct fsentry * key )
270+ {
271+ struct fsentry * fse = hashmap_get_entry (& map , key , ent , NULL );
272+
273+ /* return if its a 'real' entry (future entries have refcnt == 0) */
274+ if (!fse || fse -> list || fse -> u .refcnt )
275+ return fse ;
276+
277+ /* create an event and link our key to the future entry */
278+ key -> u .hwait = CreateEvent (NULL , TRUE, FALSE, NULL );
279+ key -> next = fse -> next ;
280+ fse -> next = key ;
281+
282+ /* wait for the loading thread to signal us */
283+ LeaveCriticalSection (& mutex );
284+ WaitForSingleObject (key -> u .hwait , INFINITE );
285+ CloseHandle (key -> u .hwait );
286+ EnterCriticalSection (& mutex );
287+
288+ /* repeat cache lookup */
289+ return hashmap_get_entry (& map , key , ent , NULL );
290+ }
291+
263292/*
264293 * Looks up or creates a cache entry for the specified key.
265294 */
266295static struct fsentry * fscache_get (struct fsentry * key )
267296{
268- struct fsentry * fse ;
297+ struct fsentry * fse , * future , * waiter ;
269298
270299 EnterCriticalSection (& mutex );
271300 /* check if entry is in cache */
272- fse = hashmap_get_entry ( & map , key , ent , NULL );
301+ fse = fscache_get_wait ( key );
273302 if (fse ) {
274303 fsentry_addref (fse );
275304 LeaveCriticalSection (& mutex );
276305 return fse ;
277306 }
278307 /* if looking for a file, check if directory listing is in cache */
279308 if (!fse && key -> list ) {
280- fse = hashmap_get_entry ( & map , key -> list , ent , NULL );
309+ fse = fscache_get_wait ( key -> list );
281310 if (fse ) {
282311 LeaveCriticalSection (& mutex );
283312 /* dir entry without file entry -> file doesn't exist */
@@ -286,16 +315,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
286315 }
287316 }
288317
318+ /* add future entry to indicate that we're loading it */
319+ future = key -> list ? key -> list : key ;
320+ future -> next = NULL ;
321+ future -> u .refcnt = 0 ;
322+ hashmap_add (& map , & future -> ent );
323+
289324 /* create the directory listing (outside mutex!) */
290325 LeaveCriticalSection (& mutex );
291- fse = fsentry_create_list (key -> list ? key -> list : key );
292- if (!fse )
326+ fse = fsentry_create_list (future );
327+ EnterCriticalSection (& mutex );
328+
329+ /* remove future entry and signal waiting threads */
330+ hashmap_remove (& map , & future -> ent , NULL );
331+ waiter = future -> next ;
332+ while (waiter ) {
333+ HANDLE h = waiter -> u .hwait ;
334+ waiter = waiter -> next ;
335+ SetEvent (h );
336+ }
337+
338+ /* leave on error (errno set by fsentry_create_list) */
339+ if (!fse ) {
340+ LeaveCriticalSection (& mutex );
293341 return NULL ;
342+ }
294343
295- EnterCriticalSection (& mutex );
296- /* add directory listing if it hasn't been added by some other thread */
297- if (!hashmap_get_entry (& map , key , ent , NULL ))
298- fscache_add (fse );
344+ /* add directory listing to the cache */
345+ fscache_add (fse );
299346
300347 /* lookup file entry if requested (fse already points to directory) */
301348 if (key -> list )
0 commit comments