@@ -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 ;
@@ -261,24 +263,51 @@ static inline int fscache_enabled(const char *path)
261263 return enabled > 0 && !is_absolute_path (path );
262264}
263265
266+ /*
267+ * Looks up a cache entry, waits if its being loaded by another thread.
268+ * The mutex must be owned by the calling thread.
269+ */
270+ static struct fsentry * fscache_get_wait (struct fsentry * key )
271+ {
272+ struct fsentry * fse = hashmap_get_entry (& map , key , ent , NULL );
273+
274+ /* return if its a 'real' entry (future entries have refcnt == 0) */
275+ if (!fse || fse -> list || fse -> u .refcnt )
276+ return fse ;
277+
278+ /* create an event and link our key to the future entry */
279+ key -> u .hwait = CreateEvent (NULL , TRUE, FALSE, NULL );
280+ key -> next = fse -> next ;
281+ fse -> next = key ;
282+
283+ /* wait for the loading thread to signal us */
284+ LeaveCriticalSection (& mutex );
285+ WaitForSingleObject (key -> u .hwait , INFINITE );
286+ CloseHandle (key -> u .hwait );
287+ EnterCriticalSection (& mutex );
288+
289+ /* repeat cache lookup */
290+ return hashmap_get_entry (& map , key , ent , NULL );
291+ }
292+
264293/*
265294 * Looks up or creates a cache entry for the specified key.
266295 */
267296static struct fsentry * fscache_get (struct fsentry * key )
268297{
269- struct fsentry * fse ;
298+ struct fsentry * fse , * future , * waiter ;
270299
271300 EnterCriticalSection (& mutex );
272301 /* check if entry is in cache */
273- fse = hashmap_get_entry ( & map , key , ent , NULL );
302+ fse = fscache_get_wait ( key );
274303 if (fse ) {
275304 fsentry_addref (fse );
276305 LeaveCriticalSection (& mutex );
277306 return fse ;
278307 }
279308 /* if looking for a file, check if directory listing is in cache */
280309 if (!fse && key -> list ) {
281- fse = hashmap_get_entry ( & map , key -> list , ent , NULL );
310+ fse = fscache_get_wait ( key -> list );
282311 if (fse ) {
283312 LeaveCriticalSection (& mutex );
284313 /* dir entry without file entry -> file doesn't exist */
@@ -287,16 +316,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
287316 }
288317 }
289318
319+ /* add future entry to indicate that we're loading it */
320+ future = key -> list ? key -> list : key ;
321+ future -> next = NULL ;
322+ future -> u .refcnt = 0 ;
323+ hashmap_add (& map , & future -> ent );
324+
290325 /* create the directory listing (outside mutex!) */
291326 LeaveCriticalSection (& mutex );
292- fse = fsentry_create_list (key -> list ? key -> list : key );
293- if (!fse )
327+ fse = fsentry_create_list (future );
328+ EnterCriticalSection (& mutex );
329+
330+ /* remove future entry and signal waiting threads */
331+ hashmap_remove (& map , & future -> ent , NULL );
332+ waiter = future -> next ;
333+ while (waiter ) {
334+ HANDLE h = waiter -> u .hwait ;
335+ waiter = waiter -> next ;
336+ SetEvent (h );
337+ }
338+
339+ /* leave on error (errno set by fsentry_create_list) */
340+ if (!fse ) {
341+ LeaveCriticalSection (& mutex );
294342 return NULL ;
343+ }
295344
296- EnterCriticalSection (& mutex );
297- /* add directory listing if it hasn't been added by some other thread */
298- if (!hashmap_get_entry (& map , key , ent , NULL ))
299- fscache_add (fse );
345+ /* add directory listing to the cache */
346+ fscache_add (fse );
300347
301348 /* lookup file entry if requested (fse already points to directory) */
302349 if (key -> list )
0 commit comments