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