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