@@ -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 ;
@@ -261,24 +263,51 @@ static inline int fscache_enabled(const char *path)
261
263
return enabled > 0 && !is_absolute_path (path );
262
264
}
263
265
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
+
264
293
/*
265
294
* Looks up or creates a cache entry for the specified key.
266
295
*/
267
296
static struct fsentry * fscache_get (struct fsentry * key )
268
297
{
269
- struct fsentry * fse ;
298
+ struct fsentry * fse , * future , * waiter ;
270
299
271
300
EnterCriticalSection (& mutex );
272
301
/* check if entry is in cache */
273
- fse = hashmap_get_entry ( & map , key , ent , NULL );
302
+ fse = fscache_get_wait ( key );
274
303
if (fse ) {
275
304
fsentry_addref (fse );
276
305
LeaveCriticalSection (& mutex );
277
306
return fse ;
278
307
}
279
308
/* if looking for a file, check if directory listing is in cache */
280
309
if (!fse && key -> list ) {
281
- fse = hashmap_get_entry ( & map , key -> list , ent , NULL );
310
+ fse = fscache_get_wait ( key -> list );
282
311
if (fse ) {
283
312
LeaveCriticalSection (& mutex );
284
313
/* dir entry without file entry -> file doesn't exist */
@@ -287,16 +316,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
287
316
}
288
317
}
289
318
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
+
290
325
/* create the directory listing (outside mutex!) */
291
326
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 );
294
342
return NULL ;
343
+ }
295
344
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 );
300
347
301
348
/* lookup file entry if requested (fse already points to directory) */
302
349
if (key -> list )
0 commit comments