2424
2525namespace SixLabors . ImageSharp . Web . Middleware
2626{
27+ public struct ImageResponse
28+ {
29+ internal ImageContext Context { get ; set ; }
30+ public ImageCacheMetadata Metadata { get ; set ; }
31+ public Stream Stream { get ; set ; }
32+
33+ }
34+
35+ public struct CacheImageResponse
36+ {
37+ public bool NewOrUppdated { get ; set ; }
38+ public ImageMetadata Metadata { get ; set ; }
39+ public ImageCacheMetadata CacheMetadata { get ; set ; }
40+
41+ public IImageCacheResolver Resolver { get ; set ; }
42+
43+ }
44+
45+
2746 /// <summary>
2847 /// Middleware for handling the processing of images via image requests.
2948 /// </summary>
@@ -32,14 +51,14 @@ public class ImageSharpMiddleware
3251 /// <summary>
3352 /// The write worker used for limiting identical requests.
3453 /// </summary>
35- private static readonly ConcurrentDictionary < string , Lazy < Task > > WriteWorkers
36- = new ConcurrentDictionary < string , Lazy < Task > > ( StringComparer . OrdinalIgnoreCase ) ;
54+ private static readonly ConcurrentDictionary < string , Lazy < Task < ImageResponse > > > WriteWorkers
55+ = new ConcurrentDictionary < string , Lazy < Task < ImageResponse > > > ( StringComparer . OrdinalIgnoreCase ) ;
3756
3857 /// <summary>
3958 /// The read worker used for limiting identical requests.
4059 /// </summary>
41- private static readonly ConcurrentDictionary < string , Lazy < Task < ValueTuple < bool , ImageMetadata > > > > ReadWorkers
42- = new ConcurrentDictionary < string , Lazy < Task < ValueTuple < bool , ImageMetadata > > > > ( StringComparer . OrdinalIgnoreCase ) ;
60+ private static readonly ConcurrentDictionary < string , Lazy < Task < CacheImageResponse > > > ReadWorkers
61+ = new ConcurrentDictionary < string , Lazy < Task < CacheImageResponse > > > ( StringComparer . OrdinalIgnoreCase ) ;
4362
4463 /// <summary>
4564 /// Used to temporarily store source metadata reads to reduce the overhead of cache lookups.
@@ -251,28 +270,31 @@ private async Task ProcessRequestAsync(
251270
252271 // Check the cache, if present, not out of date and not requiring and update
253272 // we'll simply serve the file from there.
254- ( bool newOrUpdated , ImageMetadata sourceImageMetadata ) =
273+ var cacheImageResponse =
255274 await this . IsNewOrUpdatedAsync ( sourceImageResolver , imageContext , key ) ;
256275
257- if ( ! newOrUpdated )
276+ if ( ! cacheImageResponse . NewOrUppdated )
258277 {
278+ await this . SendResponseAsync ( imageContext , key , cacheImageResponse . CacheMetadata , null , cacheImageResponse . Resolver ) ;
259279 return ;
260280 }
261281
262- // Not cached? Let's get it from the image resolver.
282+ var sourceImageMetadata = cacheImageResponse . Metadata ;
283+
284+ // Not cached, or is updated? Let's get it from the image resolver.
263285 RecyclableMemoryStream outStream = null ;
264286
265287 // Enter a write lock which locks writing and any reads for the same request.
266288 // This reduces the overheads of unnecessary processing plus avoids file locks.
267- await WriteWorkers . GetOrAdd (
289+ var writeResult = await WriteWorkers . GetOrAdd (
268290 key ,
269- _ => new Lazy < Task > (
291+ _ => new Lazy < Task < ImageResponse > > (
270292 async ( ) =>
271293 {
272294 try
273295 {
274296 // Prevent a second request from starting a read during write execution.
275- if ( ReadWorkers . TryGetValue ( key , out Lazy < Task < ( bool , ImageMetadata ) > > readWork ) )
297+ if ( ReadWorkers . TryGetValue ( key , out Lazy < Task < CacheImageResponse > > readWork ) )
276298 {
277299 await readWork . Value ;
278300 }
@@ -338,7 +360,27 @@ await WriteWorkers.GetOrAdd(
338360 // for the same key.
339361 CacheResolverLru . TryRemove ( key ) ;
340362
341- await this . SendResponseAsync ( imageContext , key , cachedImageMetadata , outStream , null ) ;
363+ // This worker queue should be all about writing.
364+ // Not sending. sending would only happen once
365+ // when it is part of this queue.
366+
367+ var taskCompletionSource = new TaskCompletionSource < ImageResponse > ( ) ;
368+
369+
370+ // Return the response to the pipeline so it can return the response to multiple callers.
371+ var result = new ImageResponse
372+ {
373+ Context = imageContext ,
374+ Metadata = cachedImageMetadata ,
375+ Stream = outStream
376+ } ;
377+
378+ return result ;
379+
380+ // taskCompletionSource.SetResult(result);
381+
382+ // return taskCompletionSource.Task;
383+
342384 }
343385 catch ( Exception ex )
344386 {
@@ -349,10 +391,13 @@ await WriteWorkers.GetOrAdd(
349391 }
350392 finally
351393 {
352- await this . StreamDisposeAsync ( outStream ) ;
353- WriteWorkers . TryRemove ( key , out Lazy < Task > _ ) ;
394+ // await this.StreamDisposeAsync(outStream);
395+ WriteWorkers . TryRemove ( key , out Lazy < Task < ImageResponse > > _ ) ;
354396 }
355397 } , LazyThreadSafetyMode . ExecutionAndPublication ) ) . Value ;
398+
399+
400+ await this . SendResponseAsync ( imageContext , key , writeResult . Metadata , writeResult . Stream , null ) ;
356401 }
357402
358403 private ValueTask StreamDisposeAsync ( Stream stream )
@@ -377,24 +422,24 @@ private ValueTask StreamDisposeAsync(Stream stream)
377422#endif
378423 }
379424
380- private async Task < ValueTuple < bool , ImageMetadata > > IsNewOrUpdatedAsync (
425+ private async Task < CacheImageResponse > IsNewOrUpdatedAsync (
381426 IImageResolver sourceImageResolver ,
382427 ImageContext imageContext ,
383428 string key )
384429 {
385- if ( WriteWorkers . TryGetValue ( key , out Lazy < Task > writeWork ) )
430+ if ( WriteWorkers . TryGetValue ( key , out Lazy < Task < ImageResponse > > writeWork ) )
386431 {
387432 await writeWork . Value ;
388433 }
389434
390- if ( ReadWorkers . TryGetValue ( key , out Lazy < Task < ( bool , ImageMetadata ) > > readWork ) )
435+ if ( ReadWorkers . TryGetValue ( key , out Lazy < Task < CacheImageResponse > > readWork ) )
391436 {
392437 return await readWork . Value ;
393438 }
394439
395440 return await ReadWorkers . GetOrAdd (
396441 key ,
397- _ => new Lazy < Task < ValueTuple < bool , ImageMetadata > > > (
442+ _ => new Lazy < Task < CacheImageResponse > > (
398443 async ( ) =>
399444 {
400445 try
@@ -426,7 +471,8 @@ private async Task<ValueTuple<bool, ImageMetadata>> IsNewOrUpdatedAsync(
426471 {
427472 // Remove the null resolver from the store.
428473 CacheResolverLru . TryRemove ( key ) ;
429- return ( true , sourceImageMetadata ) ;
474+
475+ return new CacheImageResponse { NewOrUppdated = true , Metadata = sourceImageMetadata } ;
430476 }
431477
432478 // Has the cached image expired?
@@ -437,16 +483,25 @@ private async Task<ValueTuple<bool, ImageMetadata>> IsNewOrUpdatedAsync(
437483 {
438484 // We want to remove the resolver from the store so that the next check gets the updated file.
439485 CacheResolverLru . TryRemove ( key ) ;
440- return ( true , sourceImageMetadata ) ;
486+ return new CacheImageResponse { NewOrUppdated = true , Metadata = sourceImageMetadata } ;
441487 }
442488
489+ // Likewise this queue should be about getting the image metadata
490+ // not
491+
443492 // We're pulling the image from the cache.
444- await this . SendResponseAsync ( imageContext , key , cachedImage . ImageCacheMetadata , null , cachedImage . ImageCacheResolver ) ;
445- return ( false , sourceImageMetadata ) ;
493+ // await this.SendResponseAsync(imageContext, key, cachedImage.ImageCacheMetadata, null, cachedImage.ImageCacheResolver);
494+
495+ // The image is cached. Return the cached image so multiple callers can write a response.
496+ return new CacheImageResponse {
497+ NewOrUppdated = false ,
498+ Metadata = sourceImageMetadata ,
499+ CacheMetadata = cachedImage . ImageCacheMetadata ,
500+ Resolver = cachedImage . ImageCacheResolver } ;
446501 }
447502 finally
448503 {
449- ReadWorkers . TryRemove ( key , out Lazy < Task < ( bool , ImageMetadata ) > > _ ) ;
504+ ReadWorkers . TryRemove ( key , out Lazy < Task < CacheImageResponse > > _ ) ;
450505 }
451506 } , LazyThreadSafetyMode . ExecutionAndPublication ) ) . Value ;
452507 }
0 commit comments