Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cancellable codec API, and overloads for loading/saving #1296

Merged
merged 29 commits into from
Aug 4, 2020

Conversation

antonfirsov
Copy link
Member

@antonfirsov antonfirsov commented Jul 31, 2020

Prerequisites

  • I have written a descriptive pull-request title
  • I have verified that there are no overlapping pull-requests open
  • I have verified that I am following matches the existing coding patterns and practice as demonstrated in the repository. These follow strict Stylecop rules 👮.
  • I have provided test coverage for my change (where applicable)

Breaking changes

  • Image.LoadAsync(...), Image.IdentifyAsync(...), image.SaveAsync(...), image.SaveAsXyzAsync(...) overloads replaced by cancellable variants, with default argument cancellationToken = default
  • Async overloads on IImageEncoder, IImageDecoder and IImageVisitor replaced by cancellable variants

Description

  • Refactor duplicate code out from decoders and encoders into ImageDecoderUtilities and ImageEncoderUtilities
  • Make IImageEncoder and IImageDecoder cancellable.
    • To make the expensive codec logic cancellable, a CancellationToken is forwarded to the synchronous internal implementation classes (IImageDecoderInternals and IImageEncoderInternals implementors)
  • Cancellation is actually implemented and tested for Jpeg
  • Add a bunch of new (cancellable & non-cancellable) overloads for Image.LoadAsync and Image.IdentifyAsync together with tests
  • The Formats/*/ImageExtensions.cs files containing the .SaveAs* methods became unmaintainable with the exponential growth of parameters. Replacing them with generated code coming from ImageExtesions.Save.tt.

This is a relatively large change right before releasing 1.0 (😅), so hoping for an thorough review to minimize risks.

Resolves #1280.

@antonfirsov antonfirsov requested a review from a team July 31, 2020 00:38
@antonfirsov antonfirsov added this to the 1.0.0 milestone Jul 31, 2020
@antonfirsov antonfirsov changed the title Cancellable codecs and missing overloads Cancellable codec API, and missing overloads for loading/saving Jul 31, 2020
@antonfirsov antonfirsov changed the title Cancellable codec API, and missing overloads for loading/saving Cancellable codec API, and overloads for loading/saving Jul 31, 2020
Copy link
Member

@tocsoft tocsoft left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a couple of things

As a rule for any async methods I feel we should have this guidance:

  1. Don't have multiple overloads which only differ on having a CancellationToken or not.
  2. Public apis should always have the cancellation token, and it should be configured with a default value of default
  3. Internal/private apis should never have a default value for the CancellationToken (to make it harder to miss passing a public token along)

src/ImageSharp/Formats/Gif/GifDecoder.cs Show resolved Hide resolved
{
Guard.NotNull(stream, nameof(stream));

var decoder = new GifDecoderCore(configuration, this);

using var bufferedStream = new BufferedReadStream(configuration, stream);
return decoder.IdentifyAsync(bufferedStream);
return await decoder.IdentifyAsync(configuration, bufferedStream, cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as we are awaiting here this one should have a .ConfigureAwait(false)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this actually needs awaiting does it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we do because of the use of the buffered stream

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should double check elsewhere then.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will check if I can solve by adding a helper method.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no need to create a BufferedReadStream in this method, so it can be simplified and the async context removed.

src/ImageSharp/Formats/ImageExtesions.Save.cs Outdated Show resolved Hide resolved
src/ImageSharp/Formats/ImageExtesions.Save.cs Outdated Show resolved Hide resolved
Copy link
Member

@JimBobSquarePants JimBobSquarePants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A couple of minor things... Very impressed though, it's cool!

src/ImageSharp/Formats/ImageExtesions.Save.cs Outdated Show resolved Hide resolved
src/ImageSharp/Formats/ImageExtesions.Save.tt Outdated Show resolved Hide resolved
src/ImageSharp/Formats/Bmp/BmpDecoder.cs Outdated Show resolved Hide resolved
src/ImageSharp/Formats/Gif/GifDecoder.cs Show resolved Hide resolved
{
Guard.NotNull(stream, nameof(stream));

var decoder = new GifDecoderCore(configuration, this);

using var bufferedStream = new BufferedReadStream(configuration, stream);
return decoder.IdentifyAsync(bufferedStream);
return await decoder.IdentifyAsync(configuration, bufferedStream, cancellationToken);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this actually needs awaiting does it?

src/ImageSharp/Formats/ImageEncoderUtilities.cs Outdated Show resolved Hide resolved
src/ImageSharp/ImageSharp.csproj Outdated Show resolved Hide resolved
@antonfirsov
Copy link
Member Author

I need time to carry out the API refactor we agreed on, likely won't be able to come back before the beginning of the next week. Feel free to hunt for further possible issues in the code in the meanwhile.

@antonfirsov
Copy link
Member Author

@tocsoft @JimBobSquarePants ready for final review.

@codecov
Copy link

codecov bot commented Aug 4, 2020

Codecov Report

Merging #1296 into master will increase coverage by 0.07%.
The diff coverage is 88.65%.

Impacted file tree graph

@@            Coverage Diff             @@
##           master    #1296      +/-   ##
==========================================
+ Coverage   82.71%   82.79%   +0.07%     
==========================================
  Files         692      689       -3     
  Lines       30718    30731      +13     
  Branches     3474     3472       -2     
==========================================
+ Hits        25409    25444      +35     
+ Misses       4603     4581      -22     
  Partials      706      706              
Flag Coverage Δ
#unittests 82.79% <88.65%> (+0.07%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Impacted Files Coverage Δ
src/ImageSharp/Formats/Bmp/BmpDecoderCore.cs 94.45% <ø> (ø)
src/ImageSharp/Formats/Bmp/BmpEncoderCore.cs 96.66% <ø> (-0.19%) ⬇️
src/ImageSharp/Formats/Gif/GifDecoderCore.cs 83.96% <ø> (ø)
src/ImageSharp/Formats/Gif/GifEncoderCore.cs 95.05% <ø> (-0.16%) ⬇️
src/ImageSharp/Formats/Png/PngDecoderCore.cs 90.44% <ø> (ø)
src/ImageSharp/Formats/Png/PngEncoderCore.cs 97.81% <ø> (-0.05%) ⬇️
src/ImageSharp/Formats/Tga/TgaDecoderCore.cs 91.29% <ø> (ø)
src/ImageSharp/Formats/Tga/TgaEncoderCore.cs 99.31% <ø> (-0.05%) ⬇️
src/ImageSharp/Formats/Gif/GifDecoder.cs 58.82% <33.33%> (+8.82%) ⬆️
src/ImageSharp/Formats/Png/PngDecoder.cs 50.00% <33.33%> (+1.85%) ⬆️
... and 29 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 586d99e...e707a8d. Read the comment docs.

@antonfirsov antonfirsov added the breaking Signifies a binary breaking change. label Aug 4, 2020
Copy link
Member

@JimBobSquarePants JimBobSquarePants left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found and fixed a couple of issues where we needed to await IDisposable's but otherwise LGTM 👍

@JimBobSquarePants JimBobSquarePants merged commit 521fae3 into master Aug 4, 2020
@JimBobSquarePants JimBobSquarePants deleted the af/cancellation branch August 4, 2020 11:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
API breaking Signifies a binary breaking change.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add public API changes necessary for cancellation support for LoadAsync/SaveAsync
3 participants