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