@@ -27,6 +27,8 @@ struct fsentry {
2727	union  {
2828		/* Reference count of the directory listing. */ 
2929		volatile  long  refcnt ;
30+ 		/* Handle to wait on the loading thread. */ 
31+ 		HANDLE  hwait ;
3032		struct  {
3133			/* More stat members (only used for file entries). */ 
3234			off64_t  st_size ;
@@ -261,24 +263,51 @@ static inline int fscache_enabled(const char *path)
261263	return  enabled  >  0  &&  !is_absolute_path (path );
262264}
263265
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+ 
264293/* 
265294 * Looks up or creates a cache entry for the specified key. 
266295 */ 
267296static  struct  fsentry  * fscache_get (struct  fsentry  * key )
268297{
269- 	struct  fsentry  * fse ;
298+ 	struct  fsentry  * fse ,  * future ,  * waiter ;
270299
271300	EnterCriticalSection (& mutex );
272301	/* check if entry is in cache */ 
273- 	fse  =  hashmap_get_entry ( & map ,  key ,  ent ,  NULL );
302+ 	fse  =  fscache_get_wait ( key );
274303	if  (fse ) {
275304		fsentry_addref (fse );
276305		LeaveCriticalSection (& mutex );
277306		return  fse ;
278307	}
279308	/* if looking for a file, check if directory listing is in cache */ 
280309	if  (!fse  &&  key -> list ) {
281- 		fse  =  hashmap_get_entry ( & map ,  key -> list ,  ent ,  NULL );
310+ 		fse  =  fscache_get_wait ( key -> list );
282311		if  (fse ) {
283312			LeaveCriticalSection (& mutex );
284313			/* dir entry without file entry -> file doesn't exist */ 
@@ -287,16 +316,34 @@ static struct fsentry *fscache_get(struct fsentry *key)
287316		}
288317	}
289318
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+ 
290325	/* create the directory listing (outside mutex!) */ 
291326	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 );
294342		return  NULL ;
343+ 	}
295344
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 );
300347
301348	/* lookup file entry if requested (fse already points to directory) */ 
302349	if  (key -> list )
0 commit comments