@@ -27,6 +27,8 @@ struct fsentry {
27
27
union {
28
28
/* Reference count of the directory listing. */
29
29
volatile long refcnt ;
30
+ /* Handle to wait on the loading thread. */
31
+ HANDLE hwait ;
30
32
struct {
31
33
/* More stat members (only used for file entries). */
32
34
off64_t st_size ;
@@ -266,24 +268,51 @@ static inline int fscache_enabled(const char *path)
266
268
return enabled > 0 && !is_absolute_path (path );
267
269
}
268
270
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
+
269
298
/*
270
299
* Looks up or creates a cache entry for the specified key.
271
300
*/
272
301
static struct fsentry * fscache_get (struct fsentry * key )
273
302
{
274
- struct fsentry * fse ;
303
+ struct fsentry * fse , * future , * waiter ;
275
304
276
305
EnterCriticalSection (& mutex );
277
306
/* check if entry is in cache */
278
- fse = hashmap_get_entry ( & map , key , ent , NULL );
307
+ fse = fscache_get_wait ( key );
279
308
if (fse ) {
280
309
fsentry_addref (fse );
281
310
LeaveCriticalSection (& mutex );
282
311
return fse ;
283
312
}
284
313
/* if looking for a file, check if directory listing is in cache */
285
314
if (!fse && key -> list ) {
286
- fse = hashmap_get_entry ( & map , key -> list , ent , NULL );
315
+ fse = fscache_get_wait ( key -> list );
287
316
if (fse ) {
288
317
LeaveCriticalSection (& mutex );
289
318
/* dir entry without file entry -> file doesn't exist */
@@ -292,16 +321,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
292
321
}
293
322
}
294
323
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
+
295
330
/* create the directory listing (outside mutex!) */
296
331
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 );
299
347
return NULL ;
348
+ }
300
349
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 );
305
352
306
353
/* lookup file entry if requested (fse already points to directory) */
307
354
if (key -> list )
0 commit comments