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

[Xamarin.Android.Build.Tasks] fix potential AsyncTask.Log calls #4187

Merged
merged 1 commit into from Jan 30, 2020

Conversation

jonathanpeppers
Copy link
Member

@jonathanpeppers jonathanpeppers commented Jan 28, 2020

Changes: https://github.com/xamarin/monodroid/compare/78d56588...017b3618

Bump to xamarin/monodroid/master@017b3618

We have had a continuing concern of accidentally calling
AsyncTask.Log from a background thread. If done so, this can cause a
hang inside Visual Studio on Windows.

I finally had the idea of creating a build error if we inadvertently
do this:

[Obsolete("You should not use the 'Log' property directly for AsyncTask. Use the 'Log*' methods instead.", error: true)]
public new TaskLoggingHelper Log { get; }

This uncovered 26 build errors! Some were actually on the main thread
and completely OK, but several were not OK.

In most of the places, I could simply change Log.LogDebugMessage to
LogDebugMessage.

However a few of the more troublesome places:

  • ManifestDocument required a TaskLoggingHelper for its ctor. I
    moved this to only the methods that need it.

  • NdkUtils and NdkUtilsOld I used Action<string> and
    Action<string, string> callbacks instead.

  • The <Aot/> task had an empty ValidateAotConfiguration method I
    removed.

Going forward, it should be a lot harder for us to introduce a hang by
using AsyncTask.Log.

@jonathanpeppers
Copy link
Member Author

I actually found this was a real hang when using ReSharper:

image

I'll test and see if the changes here fix the issue.

@@ -34,12 +34,15 @@ public abstract class AndroidAsyncTask : AsyncTask

public abstract string TaskPrefix { get; }

[Obsolete("You should not use the 'Log' property directly for AsyncTask. Use the 'Log*' methods instead.", error: true)]
public new TaskLoggingHelper Log { get; }
Copy link
Member

Choose a reason for hiding this comment

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

I realize it has error:true, but should this also throw an exception for Really Good Measure?

Obsolete (..., error: true)]
public new TaskLoggingHelpe Log {
    get => throw new NotSupportedException ("Should not be reached!");
}

Admittedly, if the new Log property is accessed e.g. via Reflection, it'll "just" return null (and possibly NRE?), and it really shouldn't happen at all anyway, but... defense in depth? ¯_(ツ)_/¯

Copy link
Contributor

Choose a reason for hiding this comment

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

I would be careful about throwing an exception. the base class AsyncTask uses Log allot when on the UI thread. See https://github.com/xamarin/Xamarin.Build.AsyncTask/blob/master/Xamarin.Build.AsyncTask/AsyncTask.cs#L138 note all the #pragma stuff which ignore that Obsolete warning.

Copy link
Member Author

Choose a reason for hiding this comment

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

Because I used the new keyword, the base class shouldn't be able to call the property. I think throwing might be better than the NRE, as I could put a better exception message.

Copy link
Member Author

Choose a reason for hiding this comment

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

Or maybe I should just return base.Log? Might be the absolute safest option.

@dellis1972
Copy link
Contributor

I actually found this was a real hang when using ReSharper:

image

I'll test and see if the changes here fix the issue.

@jonathanpeppers that is not the hang. That is where its manifesting. The WaitForCompletion is the method which waits on all the events while a task is running. It times out every 10 milliseconds

https://github.com/xamarin/Xamarin.Build.AsyncTask/blob/master/Xamarin.Build.AsyncTask/AsyncTask.cs#L284

That is by design and is exactly how the ToolTask works in MSbuild itself. So the problem isn't that method exactly, its something it trying to use the UI thread while that Wait is in that 10 millisecond "wait". That then causes the lockup. So the cause will be elsewhere.

@jonathanpeppers
Copy link
Member Author

@dellis1972 sorry I didn't explain what the picture was.

In a new project, in the 16.5 Preview, I used ReSharper's "new class" dialog. This triggered a design-time build and the UI of VS was completely stuck: only option is to end task.

I saved a dump file of devenv.exe and opened it in a new VS. It was on this line, which means it was probably spinning in that loop forever:

image

Since CalculateLayoutCodeBehind had direct Log calls, could it be the cause?

I was going re-test with a build from master after this is merged, but I can probably try the build from this PR.

Changes: xamarin/monodroid@78d5658...017b361

Bump to xamarin/monodroid@017b3618

We have had a continuing concern of accidentally calling
`AsyncTask.Log` from a background thread. If done so, this can cause a
hang inside Visual Studio on Windows.

I finally had the idea of creating a build error if we inadvertently
do this:

    [Obsolete("You should not use the 'Log' property directly for AsyncTask. Use the 'Log*' methods instead.", error: true)]
    public new TaskLoggingHelper Log { get; }

This uncovered 26 build errors! Some were actually on the main thread
and completely OK, but several were not OK.

In most of the places, I could simply change `Log.LogDebugMessage` to
`LogDebugMessage`.

However a few of the more troublesome places:

* `ManifestDocument` required a `TaskLoggingHelper` for its ctor. I
  moved this to only the methods that need it.

* `NdkUtils` and `NdkUtilsOld` I used `Action<string>` and
  `Action<string, string>` callbacks instead.

* The `<Aot/>` task had an empty `ValidateAotConfiguration` method I
  removed.

Going forward, it should be a lot harder for us to introduce a hang by
using `AsyncTask.Log`.
@jonathanpeppers
Copy link
Member Author

jonathanpeppers commented Jan 30, 2020

I found the hang continued to happen until I changed:

-<Target Name="_FindLayoutsForBinding" Condition=" '$(Language)' == 'C#' ">
+<Target Name="_FindLayoutsForBinding" Condition=" '$(AndroidGenerateLayoutBindings)' == 'True' And '$(Language)' == 'C#' ">

I'll make that change in a different PR, probably should be doing this anyway?

@dellis1972
Copy link
Contributor

@jonathanpeppers what you got was a deadlock, the IDE/UI thread was in System.Threading.WaitHandle.WaitAny and then something else tried to call a method in there.
Having Log.XXX calls on a background thread will definitely do that.

jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Jan 30, 2020
… unused

Context: xamarin#4187

We have been able to repro a hang when using ReSharper +
Xamarin.Android with this stack trace:

    Xamarin.Build.AsyncTask.dll!Xamarin.Build.AsyncTask.WaitForCompletion() Line 285
    Xamarin.Build.AsyncTask.dll!Xamarin.Build.AsyncTask.Execute() Line 226
    Xamarin.Android.Build.Tasks.dll!Xamarin.Android.Tasks.AndroidAsyncTask.RunTask()
    Xamarin.Android.Build.Tasks.dll!Xamarin.Android.Tasks.AndroidAsyncTask.Execute()

This is happening inside the `<CalculateLayoutCodeBehind/>` MSBuild
task.

Using ReSharper's special "new class" dialog, a design-time build
causes the UI to lock up in VS.

PR xamarin#4187 does not fully solve the problem, however the problem *does*
go away if I make `_FindLayoutsToBind` conditional.

`_FindLayoutsToBind` can be completely skipped if you aren't using the
new code-behind features in Xamarin.Android.

This is a good fix for now to cover most Xamarin.Android projects, and
we can revisit `<CalculateLayoutCodeBehind/>` at a later time.
@jonpryor jonpryor merged commit 9fca138 into xamarin:master Jan 30, 2020
jonathanpeppers added a commit to jonathanpeppers/xamarin-android that referenced this pull request Jan 30, 2020
…rin#4187)

We have had a continuing concern of accidentally calling methods on
`AsyncTask.Log` from a background thread.  If it happens, this can
cause a hang inside Visual Studio on Windows.

I finally had the idea of creating a build error if we inadvertently
do this:

	[Obsolete("You should not use the 'Log' property directly for AsyncTask. Use the 'Log*' methods instead.", error: true)]
	public new TaskLoggingHelper Log { get; }

This uncovered 26 build errors!  Some were actually on the main
thread and completely OK, but several were not OK.

In most of the places, I could simply change `Log.LogDebugMessage()`
to `LogDebugMessage()`.

However a few of the more troublesome places:

  * `ManifestDocument` required a `TaskLoggingHelper` for its ctor.
    I moved this to only the methods that need it.

  * `NdkUtils` and `NdkUtilsOld` I used `Action<string>` and
    `Action<string, string>` callbacks instead.

  * The `<Aot/>` task had an empty `ValidateAotConfiguration()`
    method I removed.

Going forward, it should be a lot harder for us to introduce a hang
by using `AsyncTask.Log`.
@jonathanpeppers jonathanpeppers deleted the asynctask.log branch January 30, 2020 18:55
jonpryor pushed a commit that referenced this pull request Jan 31, 2020
… (#4205)

We have had a continuing concern of accidentally calling methods on
`AsyncTask.Log` from a background thread.  If it happens, this can
cause a hang inside Visual Studio on Windows.

I finally had the idea of creating a build error if we inadvertently
do this:

	[Obsolete("You should not use the 'Log' property directly for AsyncTask. Use the 'Log*' methods instead.", error: true)]
	public new TaskLoggingHelper Log { get; }

This uncovered 26 build errors!  Some were actually on the main
thread and completely OK, but several were not OK.

In most of the places, I could simply change `Log.LogDebugMessage()`
to `LogDebugMessage()`.

However a few of the more troublesome places:

  * `ManifestDocument` required a `TaskLoggingHelper` for its ctor.
    I moved this to only the methods that need it.

  * `NdkUtils` and `NdkUtilsOld` I used `Action<string>` and
    `Action<string, string>` callbacks instead.

  * The `<Aot/>` task had an empty `ValidateAotConfiguration()`
    method I removed.

Going forward, it should be a lot harder for us to introduce a hang
by using `AsyncTask.Log`.
jonpryor pushed a commit that referenced this pull request Jan 31, 2020
… unused (#4199)

Context: #4187 (comment)
Context: #4187 (comment)

We have been able to repro a hang when using ReSharper +
Xamarin.Android with this stack trace:

    Xamarin.Build.AsyncTask.dll!Xamarin.Build.AsyncTask.WaitForCompletion() Line 285
    Xamarin.Build.AsyncTask.dll!Xamarin.Build.AsyncTask.Execute() Line 226
    Xamarin.Android.Build.Tasks.dll!Xamarin.Android.Tasks.AndroidAsyncTask.RunTask()
    Xamarin.Android.Build.Tasks.dll!Xamarin.Android.Tasks.AndroidAsyncTask.Execute()

This is happening inside the `<CalculateLayoutCodeBehind/>` MSBuild
task.

Using ReSharper's special "new class" dialog, a design-time build
causes the UI to lock up in VS.

Commit 9fca138/PR #4187 did not fully solve the problem, however the
problem *does* go away if I make `_FindLayoutsToBind` conditional.

`_FindLayoutsToBind` can be completely skipped if you aren't using the
new code-behind features in Xamarin.Android.

This is a good fix for now to cover most Xamarin.Android projects, and
we can revisit `<CalculateLayoutCodeBehind/>` at a later time.
jonpryor pushed a commit that referenced this pull request Jan 31, 2020
… unused (#4199)

Context: #4187 (comment)
Context: #4187 (comment)

We have been able to repro a hang when using ReSharper +
Xamarin.Android with this stack trace:

    Xamarin.Build.AsyncTask.dll!Xamarin.Build.AsyncTask.WaitForCompletion() Line 285
    Xamarin.Build.AsyncTask.dll!Xamarin.Build.AsyncTask.Execute() Line 226
    Xamarin.Android.Build.Tasks.dll!Xamarin.Android.Tasks.AndroidAsyncTask.RunTask()
    Xamarin.Android.Build.Tasks.dll!Xamarin.Android.Tasks.AndroidAsyncTask.Execute()

This is happening inside the `<CalculateLayoutCodeBehind/>` MSBuild
task.

Using ReSharper's special "new class" dialog, a design-time build
causes the UI to lock up in VS.

Commit 9fca138/PR #4187 did not fully solve the problem, however the
problem *does* go away if I make `_FindLayoutsToBind` conditional.

`_FindLayoutsToBind` can be completely skipped if you aren't using the
new code-behind features in Xamarin.Android.

This is a good fix for now to cover most Xamarin.Android projects, and
we can revisit `<CalculateLayoutCodeBehind/>` at a later time.
@github-actions github-actions bot locked and limited conversation to collaborators Jan 28, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants