-
Notifications
You must be signed in to change notification settings - Fork 546
[Xamarin.Android.Build.Tasks] Dispose DirectoryAssemblyResolver instances #233
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
Merged
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This was referenced Sep 24, 2016
Looks ok. The Required PR for Java.Interop has been merge so we just need to update this one with the new hash (and the ReaderParameter TODO bits) and it should be good. |
…nces Context: https://bugzilla.xamarin.com/show_bug.cgi?id=44529 Commit 1d65825 bumped to Cecil 0.10.0-beta1-v2 and Java.Interop/a1d3ecc8, which likewise uses Cecil 0.10.0-beta1-v2. Among the various API changes in Cecil 0.10.x vs. Cecil 0.9.x is a change to `Mono.Cecil.IAssemblyResolver`, implemented by `Java.Interop.Tools.Cecil.DirectoryAssemblyResolver`, to implement the `IDisposable` interface. Java.Interop/a1d3ecc8 added a `DirectoryAssemblyResolver.Dispose()` method, but this method didn't *do* anything. Cecil 0.10.0-beta1-v2 also has a *semantic* change vs. Cecil 0.9.x: `AssemblyDefinition` is no longer a "purely in-memory" construct. Instead, it holds a `System.IO.Stream` to the assembly it's reading (unless `Mono.Cecil.ReaderParameters.InMemory` is `true`). This semantic change means that an assembly can't be read from and written to at the same time via different mechanisms, so long as there is an `AssemblyDefinition` instance to that file. Which brings us to (private) Bug #44529, in which an `IOException` is thrown from the `<StripEmbeddedLibraries/>` task: Error executing task StripEmbeddedLibraries: System.IO.IOException: Sharing violation on path .../obj/Release/linksrc/Xamarin.Android.NUnitLite.dll at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share, System.Int32 bufferSize, System.Boolean anonymous, System.IO.FileOptions options) [0x0025f] in <253a3790b2c44512bbca8669ecfc1822>:0 at System.IO.FileStream..ctor (System.String path, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) [0x00000] in <253a3790b2c44512bbca8669ecfc1822>:0 at (wrapper remoting-invoke-with-check) System.IO.FileStream:.ctor (string,System.IO.FileMode,System.IO.FileAccess,System.IO.FileShare) at Mono.Cecil.ModuleDefinition.GetFileStream (System.String fileName, System.IO.FileMode mode, System.IO.FileAccess access, System.IO.FileShare share) [0x00007] in :0 at Mono.Cecil.ModuleDefinition.Write (System.String fileName, Mono.Cecil.WriterParameters parameters) [0x00007] in :0 at Mono.Cecil.AssemblyDefinition.Write (System.String fileName, Mono.Cecil.WriterParameters parameters) [0x00001] in :0 at Xamarin.Android.Tasks.StripEmbeddedLibraries.Execute () [0x0034a] in <3d5202a5d4874a76a99388021bf1ab1a>:0 The underlying cause of the `IOException` is twofold: 1. `AssemblyDefinition` instances now hold a `FileStream` to the file they're reading from (and optionally writing to), and this `FileStream` will be kept open until `AssemblyDefinition.Dispose()` is called. 2. Nothing in `Xamarin.Android.Build.Tasks` calls `AssemblyDefinition.Dispose()`. Nor should anything in `Xamarin.Android.Build.Tasks` call `AssemblyDefinition.Dispose()`! (Almost) All `AssemblyDefinition` instances are *cached* within `DirectoryAssemblyResolver` instances, and nothing calls `DirectoryAssemblyResolver.Dispose()` either! (Not that this matters *too* much, becuase `DirectoryAssemblyResolver.Dispose()` is currently a no-op.) There is no sane reasoning here: so long as we're using `DirectoryAssemblyResolver`, we're going to have more files open (compared to when Cecil 0.9.x was used), which can result in file sharing problems and the above `IOException`. The way foward is: 1. `Java.Interop.Tools.Cecil.DirectoryAssemblyResolver` needs to be updated so that [`DirectoryAssemblyResolver.Dispose()` is useful][0], i.e. that it will `Dispose()` of all held `AssemblyDefinition` instances. 2. Bump to Java.Interop/084e9f7f to pick up (1). 3. `Xamarin.Android.Build.Tasks.dll` needs to be updated so that `DirectoryAssemblyResolver.Dispose()` is used. 4. All code, in particular `Xamarin.Andorid.Build.Tasks.dll`, needs to be reviewed to ensure it doesn't do anything "stupid" with `AssemblyDefinition` instances and/or `DirectoryAssemblyResolver.` Examples of "stupid" things include: * `Dispose()`ing of the return value from `DirectoryAssemblyResolver.Load()`: resolver.Load (assemblyName).Dispose(); * Treating `AssemblyDefinition` instances in a manner inconsistent with how `DirectoryAssemblyResolver` loads them. `DirectoryAssemblyResolver` will be getting a new constructor overload which takes an `Mono.Cecil.ReaderParameters` parameter; the default will be the default Cecil behavior, which is for read-only behavior. Thus, trying to use the `AssemblyDefinition` in a "write" context will fail: var r = new DirectoryAssemblyResolver (...); var a = r.Load (assemblyName); a.Write (); // BOOM [0]: dotnet/java-interop#88
712be22
to
5544f92
Compare
LGTM |
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Supersedes: #231
Requires: dotnet/java-interop#88
Context: https://bugzilla.xamarin.com/show_bug.cgi?id=44529
Commit 1d65825 bumped to Cecil 0.10.0-beta1-v2 and
Java.Interop/a1d3ecc8, which likewise uses Cecil 0.10.0-beta1-v2.
Among the various API changes in Cecil 0.10.x vs. Cecil 0.9.x is a
change to
Mono.Cecil.IAssemblyResolver
, implemented byJava.Interop.Tools.Cecil.DirectoryAssemblyResolver
, to implement theIDisposable
interface.Java.Interop/a1d3ecc8 added a
DirectoryAssemblyResolver.Dispose()
method, but this method didn't do anything.
Cecil 0.10.0-beta1-v2 also has a semantic change vs. Cecil 0.9.x:
AssemblyDefinition
is no longer a "purely in-memory" construct.Instead, it holds a
System.IO.Stream
to the assembly it's reading(unless
Mono.Cecil.ReaderParameters.InMemory
istrue
).This semantic change means that an assembly can't be read from and
written to at the same time via different mechanisms, so long as there
is an
AssemblyDefinition
instance to that file.Which brings us to (private) Bug #44529, in which an
IOException
isthrown from the
<StripEmbeddedLibraries/>
task:The underlying cause of the
IOException
is twofold:AssemblyDefinition
instances now hold aFileStream
to the filethey're reading from (and optionally writing to), and this
FileStream
will be kept open untilAssemblyDefinition.Dispose()
is called.Xamarin.Android.Build.Tasks
callsAssemblyDefinition.Dispose()
.Nor should anything in
Xamarin.Android.Build.Tasks
callAssemblyDefinition.Dispose()
! (Almost) AllAssemblyDefinition
instances are cached within
DirectoryAssemblyResolver
instances,and nothing calls
DirectoryAssemblyResolver.Dispose()
either!(Not that this matters too much, becuase
DirectoryAssemblyResolver.Dispose()
is currently a no-op.)There is no sane reasoning here: so long as we're using
DirectoryAssemblyResolver
, we're going to have more files open(compared to when Cecil 0.9.x was used), which can result in file
sharing problems and the above
IOException
.The way foward is:
Java.Interop.Tools.Cecil.DirectoryAssemblyResolver
needs to beupdated so that
DirectoryAssemblyResolver.Dispose()
is useful, i.e. that itwill
Dispose()
of all heldAssemblyDefinition
instances.Bump to Java.Interop/084e9f7f to pick up (1).
Xamarin.Android.Build.Tasks.dll
needs to be updated so thatDirectoryAssemblyResolver.Dispose()
is used.All code, in particular
Xamarin.Andorid.Build.Tasks.dll
, needs tobe reviewed to ensure it doesn't do anything "stupid" with
AssemblyDefinition
instances and/orDirectoryAssemblyResolver.
Examples of "stupid" things include:
Dispose()
ing of the return value fromDirectoryAssemblyResolver.Load()
:Treating
AssemblyDefinition
instances in a manner inconsistentwith how
DirectoryAssemblyResolver
loads them.DirectoryAssemblyResolver
will be getting a new constructoroverload which takes an
Mono.Cecil.ReaderParameters
parameter;the default will be the default Cecil behavior, which is for
read-only behavior. Thus, trying to use the
AssemblyDefinition
in a "write" context will fail: