@@ -33,6 +33,8 @@ struct fsentry {
33
33
union {
34
34
/* Reference count of the directory listing. */
35
35
volatile long refcnt ;
36
+ /* Handle to wait on the loading thread. */
37
+ HANDLE hwait ;
36
38
struct {
37
39
/* More stat members (only used for file entries). */
38
40
off64_t st_size ;
@@ -259,24 +261,51 @@ static inline int fscache_enabled(const char *path)
259
261
return enabled > 0 && !is_absolute_path (path );
260
262
}
261
263
264
+ /*
265
+ * Looks up a cache entry, waits if its being loaded by another thread.
266
+ * The mutex must be owned by the calling thread.
267
+ */
268
+ static struct fsentry * fscache_get_wait (struct fsentry * key )
269
+ {
270
+ struct fsentry * fse = hashmap_get (& map , key , NULL );
271
+
272
+ /* return if its a 'real' entry (future entries have refcnt == 0) */
273
+ if (!fse || fse -> list || fse -> refcnt )
274
+ return fse ;
275
+
276
+ /* create an event and link our key to the future entry */
277
+ key -> hwait = CreateEvent (NULL , TRUE, FALSE, NULL );
278
+ key -> next = fse -> next ;
279
+ fse -> next = key ;
280
+
281
+ /* wait for the loading thread to signal us */
282
+ LeaveCriticalSection (& mutex );
283
+ WaitForSingleObject (key -> hwait , INFINITE );
284
+ CloseHandle (key -> hwait );
285
+ EnterCriticalSection (& mutex );
286
+
287
+ /* repeat cache lookup */
288
+ return hashmap_get (& map , key , NULL );
289
+ }
290
+
262
291
/*
263
292
* Looks up or creates a cache entry for the specified key.
264
293
*/
265
294
static struct fsentry * fscache_get (struct fsentry * key )
266
295
{
267
- struct fsentry * fse ;
296
+ struct fsentry * fse , * future , * waiter ;
268
297
269
298
EnterCriticalSection (& mutex );
270
299
/* check if entry is in cache */
271
- fse = hashmap_get ( & map , key , NULL );
300
+ fse = fscache_get_wait ( key );
272
301
if (fse ) {
273
302
fsentry_addref (fse );
274
303
LeaveCriticalSection (& mutex );
275
304
return fse ;
276
305
}
277
306
/* if looking for a file, check if directory listing is in cache */
278
307
if (!fse && key -> list ) {
279
- fse = hashmap_get ( & map , key -> list , NULL );
308
+ fse = fscache_get_wait ( key -> list );
280
309
if (fse ) {
281
310
LeaveCriticalSection (& mutex );
282
311
/* dir entry without file entry -> file doesn't exist */
@@ -285,16 +314,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
285
314
}
286
315
}
287
316
317
+ /* add future entry to indicate that we're loading it */
318
+ future = key -> list ? key -> list : key ;
319
+ future -> next = NULL ;
320
+ future -> refcnt = 0 ;
321
+ hashmap_add (& map , future );
322
+
288
323
/* create the directory listing (outside mutex!) */
289
324
LeaveCriticalSection (& mutex );
290
- fse = fsentry_create_list (key -> list ? key -> list : key );
291
- if (!fse )
325
+ fse = fsentry_create_list (future );
326
+ EnterCriticalSection (& mutex );
327
+
328
+ /* remove future entry and signal waiting threads */
329
+ hashmap_remove (& map , future , NULL );
330
+ waiter = future -> next ;
331
+ while (waiter ) {
332
+ HANDLE h = waiter -> hwait ;
333
+ waiter = waiter -> next ;
334
+ SetEvent (h );
335
+ }
336
+
337
+ /* leave on error (errno set by fsentry_create_list) */
338
+ if (!fse ) {
339
+ LeaveCriticalSection (& mutex );
292
340
return NULL ;
341
+ }
293
342
294
- EnterCriticalSection (& mutex );
295
- /* add directory listing if it hasn't been added by some other thread */
296
- if (!hashmap_get (& map , key , NULL ))
297
- fscache_add (fse );
343
+ /* add directory listing to the cache */
344
+ fscache_add (fse );
298
345
299
346
/* lookup file entry if requested (fse already points to directory) */
300
347
if (key -> list )
0 commit comments