dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.48k stars 4.76k forks source link

Missing compiler required member 'System.Threading.Monitor.Enter/Exit' #14935

Closed shiftkey closed 4 years ago

shiftkey commented 9 years ago

Found this one while trying to build a netcore45 snippet under CoreCLR:

public class Class1
{
    object myLock = new object();

    public Class1()
    {
        lock(myLock) { }
    }
}

Build FAILED.

"C:\Users\shifkey\Documents\GitHub\dnx-netcore45-monitor-issue\ExperimentWithLock.sln" (default target) (1) -> "C:\Users\shifkey\Documents\GitHub\dnx-netcore45-monitor-issue\src\ExperimentWithLock\ExperimentWithLock.xproj" (default target) (2) -> (CoreCompile target) -> C:\Users\shifkey\Documents\GitHub\dnx-netcore45-monitor-issue\src\ExperimentWithLock\Class1.cs(12,13): error CS0656: Missing compiler required member 'System.Threading.Monitor.Exit' [C:\Users\shifkey\Documents\GitHub\dnx-netcore45-monitor-issue\src\ExperimentWithLock\ExperimentWithLock.xproj] C:\Users\shifkey\Documents\GitHub\dnx-netcore45-monitor-issue\src\ExperimentWithLock\Class1.cs(12,13): error CS0656: Missing compiler required member 'System.Threading.Monitor.Enter' [C:\Users\shifkey\Documents\GitHub\dnx-netcore45-monitor-issue\src\ExperimentWithLock\ExperimentWithLock.xproj]

0 Warning(s) 2 Error(s)

Repo: https://github.com/shiftkey/dnx-netcore45-monitor-issue

Clone that down and build the project with .\build.ps1. MSBuild v14/VS2015 RTM should be enough to see it.

I also added a standalone project which shows that this is valid code for the existing platform.

shiftkey commented 9 years ago

Looks like Monitor hasn't come across yet.

davidfowl commented 9 years ago

Try changing https://github.com/shiftkey/dnx-netcore45-monitor-issue/blob/master/src/ExperimentWithLock/project.json#L12 to "System.Threading": "4.0.0-beta-23019"

davidfowl commented 9 years ago

Actually just try adding System.Threading as a frameworkAssembly.

shiftkey commented 9 years ago

@davidfowl thanks, that seems to be the right fix https://github.com/shiftkey/dnx-netcore45-monitor-issue/commit/6514b69336c2dcc0b8d3129fe73baa7b398f6818

Any pointers for troubleshooting this sort of issue as I'm migrating things over?

mellinoe commented 9 years ago

Any sort of "missing type" or "missing compiler required member" basically means that you haven't referenced the right set of things via NuGet.

davidfowl commented 9 years ago

I would love to explain it but I fear it the explanation might scare you :smile:. First, read this blog post http://oren.codes/2015/06/16/demystifying-pcls-net-core-dnx-and-uwp-redux/. A frameworkAssembly is something that's a part of the "system or framework" if you target netcore50, there are a set of reference assemblies in C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETCore\v4.5, when you need something from System.* for certain targets, you can use a framework assembly. You don't need to do this for typical csproj based PCLs today because they automatically add everything by default. That's why you see that single .NET node in a PCL.

With xproj, we decided not to go this route so dependencies need to be specified manually. That's why you sometimes see some bizarre errors (like not being able to use lock).

So the summarize the first point:

frameworkAssemblies maps to assemblies in either C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework or assemblies in the GAC (on .NET Framework targets).

The second part is about understanding the System.* packages and how they relate to frameworkAssemblies. This is where things start to get fuzzy and when things don't just work and the tooling doesn't help you, you're left to fend for yourself. Before I explain though, we're working really hard on making NuGet a much much better first class experience in xproj. We're adding more features to VS to help people understand how packages affect compilation, runtime, which packages are compatible with which frameworks and why they aren't if they aren't.

With that out of the way, take a look at the above example:

{
  "version": "1.0.0-*",
  "dependencies": {
    "System.Collections": "4.0.10-beta-23019",
    "System.Linq": "4.0.0-beta-23019",
    "System.Threading": "4.0.10-beta-23019",
    "System.Runtime": "4.0.10-beta-23019",
    "Microsoft.CSharp": "4.0.0-beta-23019"
  },

  "frameworks": {
    "netcore45": { }
  }
}

VS gives no indication that anything is wrong (like I said, we're fixing that) but there's something wrong here.

image

The NuGet package for System.Threading is 4.0.10 but the dll in reference assemblies is 4.0.0.0. The only version of System.Threading that will work on netcore50 is 4.0.0 but our project is referencing 4.0.10. Now there's no real way to know this unless you dig into the package contents itself and this is what we're trying to improve today. Here's the System.Threading 4.0.10 nuspec:

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/01/nuspec.xsd">
  <metadata minClientVersion="3.0">
    <id>System.Threading</id>
    <version>4.0.10-beta-23109</version>
    <title>System.Threading</title>
    <authors>Microsoft</authors>
    <owners>microsoft,dotnetframework</owners>
    <licenseUrl>http://go.microsoft.com/fwlink/?LinkId=329770</licenseUrl>
    <iconUrl>http://go.microsoft.com/fwlink/?LinkID=288859</iconUrl>
    <requireLicenseAcceptance>true</requireLicenseAcceptance>
    <description>Provides the fundamental synchronization primitives, including System.Threading.Monitor and System.Threading.Mutex, that are required when writing asynchronous code.

Commonly Used Types:
System.Threading.Monitor
System.Threading.SynchronizationContext
System.Threading.ManualResetEvent
System.Threading.AutoResetEvent
System.Threading.ThreadLocal&lt;T&gt;
System.Threading.EventWaitHandle
System.Threading.SemaphoreSlim
System.Threading.Mutex</description>
    <copyright>© Microsoft Corporation.  All rights reserved.</copyright>
    <dependencies>
      <group targetFramework=".NETFramework4.6" />
      <group targetFramework=".NETPlatform5.0">
        <dependency id="System.Runtime" version="4.0.0-beta-23109" />
        <dependency id="System.Threading.Tasks" version="4.0.0-beta-23109" />
      </group>
    </dependencies>
  </metadata>
</package>

This is the nuspec for System.Threading 4.0.0 (notice the Windows8.0 section, that's the same as netcore45, I know it's weird...):

<?xml version="1.0"?>
<package xmlns="http://schemas.microsoft.com/packaging/2012/06/nuspec.xsd">
  <metadata>
    <id>System.Threading</id>
    <version>4.0.0-beta-23109</version>
    <title>System.Threading</title>
    <authors>Microsoft</authors>
    <owners>Microsoft</owners>
    <licenseUrl>http://go.microsoft.com/fwlink/?LinkId=329770</licenseUrl>
    <iconUrl>http://go.microsoft.com/fwlink/?LinkID=288859</iconUrl>
    <requireLicenseAcceptance>true</requireLicenseAcceptance>
    <description>Provides the fundamental synchronization primitives, including System.Threading.Monitor and System.Threading.Mutex, that are required when writing asynchronous code.

Commonly Used Types:
System.Threading.Monitor
System.Threading.SynchronizationContext
System.Threading.ManualResetEvent
System.Threading.AutoResetEvent
System.Threading.ThreadLocal&lt;T&gt;
System.Threading.EventWaitHandle
System.Threading.SemaphoreSlim
System.Threading.Mutex</description>
    <summary>Provides the fundamental synchronization primitives, including System.Threading.Monitor and System.Threading.Mutex, that are required when writing asynchronous code.</summary>
    <copyright>Copyright © Microsoft Corporation</copyright>
    <dependencies>
      <group targetFramework=".NETPlatform5.0">
        <dependency id="System.Runtime" version="4.0.0-beta-23109" />
        <dependency id="System.Threading.Tasks" version="4.0.0-beta-23109" />
      </group>
      <group targetFramework="Windows8.0" />
      <group targetFramework=".NETFramework4.5" />
      <group targetFramework="WindowsPhone8.0" />
      <group targetFramework="WindowsPhoneApp8.1" />
      <group targetFramework=".NETCore5.0">
        <dependency id="System.Runtime" version="4.0.0-beta-23109" />
        <dependency id="System.Threading.Tasks" version="4.0.0-beta-23109" />
      </group>
    </dependencies>
  </metadata>
</package>

Even stranger is that fact that if you do correct your package version to 4.0.0, you'll still see compile errors. This is because the package is making the assumption that the project system (csproj PCL) is already adding the entire set of framework assemblies on your behalf.

To understand how a package is affecting compilation/runtime, take a look at the project.lock.json:

project.json

{
  "dependencies": {
    "Newtonsoft.Json": "7.0.1"
  },

  "frameworks": {
    "netcore45": { }
  }
}

project.lock.json

{
  "locked": false,
  "version": -9996,
  "targets": {
    ".NETCore,Version=v4.5": {
      "Newtonsoft.Json/7.0.1": {
        "compile": {
          "lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll": {}
        },
        "runtime": {
          "lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll": {}
        }
      }
    }
  }
}

The lock file has a few sections but some were left out for brevity.

The targets section is basically a reflection of what you had in frameworks. For each framework, it will list each package and the assets that apply for compilation and runtime. You can see in this example for netcore45, it chose the asset under lib/portable-net40+sl5+wp80+win8+wpa81/Newtonsoft.Json.dll and it chose the same for runtime. There are cases where these differ but I won't get into those now.

If we go back to the broken example, you'll see that there's nothing being contributed to either compile or runtime for the packages specified:

{
  "locked": false,
  "version": -9996,
  "targets": {
    ".NETCore,Version=v4.5": {
      "Microsoft.CSharp/4.0.0-beta-23019": {},
      "System.Collections/4.0.0-beta-23019": {},
      "System.Linq/4.0.0-beta-23019": {},
      "System.Runtime/4.0.0-beta-23019": {},
      "System.Threading/4.0.0-beta-23019": {}
    }
  }
}

You can use the lock file in the future to determine why you're getting errors and go a bit deeper. There still much that we need to do to polish this experience, it's still pretty rough...

/cc @ericstj for a fact check and to add any additional details.

shiftkey commented 9 years ago

@davidfowl that's great, will definitely me to navigate this!

I'll leave this open for @ericstj to chime in, but that gives me enough to push onwards.

ericstj commented 9 years ago

The only version of System.Threading that will work ... there's no real way to know this unless you dig into the package contents

Actually, nuget will tell you this during a restore. When restoring an incompatible package nuget will emit an error that the package not supported by netcore45 since it provides no implementation matching the reference assembly. I think this functionality has not yet been enabled in DNX.

package is making the assumption that the project system (csproj PCL) is already adding the entire set of framework assemblies on your behalf.

The problem here is that DNX isn't matching the MSBuild behavior that it is trying to replace. For .NETCore 4.5 MSBuild targets automatically add a reference to every frameworkAssembly in the targeting pack. DNX needs to do this too.

For a while we tried making the packages add these references (to help in the DNX case) but it caused issues with the existing MSBuild-based projects which we couldn't change (eg: VS 2013 projects, for example).

The package format shared by NuGet and DNX does not give us the ability to represent a frameworkReference which will only be used by DNX, so we have no way of making a change to get the right behavior everywhere. We opted to maintain compat with the existing MSBuild based projects since we expected the majority of customers would be using these for these older frameworks. If DNX wants to support targeting these older framework versions it needs to either match their functionality or add features so that we can represent the assets that they require.

ericstj commented 9 years ago

Closing this since it is a DNX issue and not a problem with the packages or CoreFX.