Emit lock statement using Monitor.Enter (object, ref bool) when available
authorMarek Safar <marek.safar@gmail.com>
Tue, 2 Nov 2010 15:20:51 +0000 (15:20 +0000)
committerMarek Safar <marek.safar@gmail.com>
Tue, 2 Nov 2010 15:21:46 +0000 (15:21 +0000)
mcs/mcs/statement.cs
mcs/mcs/typemanager.cs
mcs/tests/ver-il-dmcs.xml

index 333b37e21861cfe06663368ab5614f5e20cfe44b..1fdedf27212b8111b7509aba959801c4732cdce0 100644 (file)
@@ -4069,7 +4069,8 @@ namespace Mono.CSharp {
 
        public class Lock : ExceptionStatement {
                Expression expr;
-               TemporaryVariableReference temp;
+               TemporaryVariableReference expr_copy;
+               TemporaryVariableReference lock_taken;
                        
                public Lock (Expression expr, Statement stmt, Location loc)
                        : base (stmt, loc)
@@ -4085,8 +4086,8 @@ namespace Mono.CSharp {
 
                        if (!TypeManager.IsReferenceType (expr.Type)){
                                ec.Report.Error (185, loc,
-                                             "`{0}' is not a reference type as required by the lock statement",
-                                             TypeManager.CSharpName (expr.Type));
+                                       "`{0}' is not a reference type as required by the lock statement",
+                                       expr.Type.GetSignatureForError ());
                                return false;
                        }
 
@@ -4096,36 +4097,93 @@ namespace Mono.CSharp {
 
                        ok &= base.Resolve (ec);
 
-                       temp = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
-                       temp.Resolve (ec);
+                       //
+                       // Have to keep original lock value around to unlock same location
+                       // in the case the original has changed or is null
+                       //
+                       expr_copy = TemporaryVariableReference.Create (expr.Type, ec.CurrentBlock.Parent, loc);
+                       expr_copy.Resolve (ec);
 
-                       if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
-                               TypeSpec monitor_type = TypeManager.CoreLookupType (ec.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
-                               TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
-                                       monitor_type, "Enter", loc, TypeManager.object_type);
-                               TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
-                                       monitor_type, "Exit", loc, TypeManager.object_type);
+                       //
+                       // Ensure Monitor methods are available
+                       //
+                       if (ResolvePredefinedMethods (ec) > 1) {
+                               lock_taken = TemporaryVariableReference.Create (TypeManager.bool_type, ec.CurrentBlock.Parent, loc);
+                               lock_taken.Resolve (ec);
                        }
-                       
+
                        return ok;
                }
                
                protected override void EmitPreTryBody (EmitContext ec)
                {
-                       temp.EmitAssign (ec, expr);
-                       temp.Emit (ec);
-                       ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+                       expr_copy.EmitAssign (ec, expr);
                }
 
                protected override void EmitTryBody (EmitContext ec)
                {
+                       //
+                       // Monitor.Enter (expr_copy, ref lock_taken)
+                       //
+                       expr_copy.Emit (ec);
+
+                       if (lock_taken != null) {
+                               lock_taken.LocalInfo.CreateBuilder (ec);
+                               lock_taken.AddressOf (ec, AddressOp.Load);
+                       }
+
+                       ec.Emit (OpCodes.Call, TypeManager.void_monitor_enter_object);
+
                        Statement.Emit (ec);
                }
 
                protected override void EmitFinallyBody (EmitContext ec)
                {
-                       temp.Emit (ec);
+                       //
+                       // if (lock_taken) Monitor.Exit (expr_copy)
+                       //
+                       Label skip = ec.DefineLabel ();
+
+                       if (lock_taken != null) {
+                               lock_taken.Emit (ec);
+                               ec.Emit (OpCodes.Brfalse_S, skip);
+                       }
+
+                       expr_copy.Emit (ec);
                        ec.Emit (OpCodes.Call, TypeManager.void_monitor_exit_object);
+                       ec.MarkLabel (skip);
+               }
+
+               int ResolvePredefinedMethods (ResolveContext rc)
+               {
+                       if (TypeManager.void_monitor_enter_object == null || TypeManager.void_monitor_exit_object == null) {
+                               TypeSpec monitor_type = TypeManager.CoreLookupType (rc.Compiler, "System.Threading", "Monitor", MemberKind.Class, true);
+
+                               if (monitor_type == null)
+                                       return 0;
+
+                               // Try 4.0 Monitor.Enter (object, ref bool) overload first
+                               var filter = MemberFilter.Method ("Enter", 0, new ParametersImported (
+                                       new[] {
+                                                       new ParameterData (null, Parameter.Modifier.NONE),
+                                                       new ParameterData (null, Parameter.Modifier.REF)
+                                               },
+                                       new[] {
+                                                       TypeManager.object_type,
+                                                       TypeManager.bool_type
+                                               }, false), null);
+
+                               TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (monitor_type, filter, true, loc);
+                               if (TypeManager.void_monitor_enter_object == null) {
+                                       TypeManager.void_monitor_enter_object = TypeManager.GetPredefinedMethod (
+                                               monitor_type, "Enter", loc, TypeManager.object_type);
+                               }
+
+                               TypeManager.void_monitor_exit_object = TypeManager.GetPredefinedMethod (
+                                       monitor_type, "Exit", loc, TypeManager.object_type);
+                       }
+
+                       return TypeManager.void_monitor_enter_object.Parameters.Count;
                }
 
                protected override void CloneTo (CloneContext clonectx, Statement t)
index 28d7dcf3c45b04ebb8164cad37c10571dba0c1a6..d94cfafe06ebf80899bced0a0c9b8a596fcf36a3 100644 (file)
@@ -248,13 +248,16 @@ namespace Mono.CSharp {
                return ts;
        }
 
-       static MemberSpec GetPredefinedMember (TypeSpec t, MemberFilter filter, Location loc)
+       static MemberSpec GetPredefinedMember (TypeSpec t, MemberFilter filter, bool optional, Location loc)
        {
                var member = MemberCache.FindMember (t, filter, BindingRestriction.DeclaredOnly);
 
                if (member != null && member.IsAccessible (InternalType.FakeInternalType))
                        return member;
 
+               if (optional)
+                       return member;
+
                string method_args = null;
                if (filter.Parameters != null)
                        method_args = filter.Parameters.GetSignatureForError ();
@@ -271,7 +274,7 @@ namespace Mono.CSharp {
        public static MethodSpec GetPredefinedConstructor (TypeSpec t, Location loc, params TypeSpec [] args)
        {
                var pc = ParametersCompiled.CreateFullyResolved (args);
-               return GetPredefinedMember (t, MemberFilter.Constructor (pc), loc) as MethodSpec;
+               return GetPredefinedMember (t, MemberFilter.Constructor (pc), false, loc) as MethodSpec;
        }
 
        //
@@ -281,22 +284,27 @@ namespace Mono.CSharp {
        public static MethodSpec GetPredefinedMethod (TypeSpec t, string name, Location loc, params TypeSpec [] args)
        {
                var pc = ParametersCompiled.CreateFullyResolved (args);
-               return GetPredefinedMethod (t, MemberFilter.Method (name, 0, pc, null), loc);
+               return GetPredefinedMethod (t, MemberFilter.Method (name, 0, pc, null), false, loc);
        }
 
        public static MethodSpec GetPredefinedMethod (TypeSpec t, MemberFilter filter, Location loc)
        {
-               return GetPredefinedMember (t, filter, loc) as MethodSpec;
+               return GetPredefinedMethod (t, filter, false, loc);
+       }
+
+       public static MethodSpec GetPredefinedMethod (TypeSpec t, MemberFilter filter, bool optional, Location loc)
+       {
+               return GetPredefinedMember (t, filter, optional, loc) as MethodSpec;
        }
 
        public static FieldSpec GetPredefinedField (TypeSpec t, string name, Location loc, TypeSpec type)
        {
-               return GetPredefinedMember (t, MemberFilter.Field (name, type), loc) as FieldSpec;
+               return GetPredefinedMember (t, MemberFilter.Field (name, type), false, loc) as FieldSpec;
        }
 
        public static PropertySpec GetPredefinedProperty (TypeSpec t, string name, Location loc, TypeSpec type)
        {
-               return GetPredefinedMember (t, MemberFilter.Property (name, type), loc) as PropertySpec;
+               return GetPredefinedMember (t, MemberFilter.Property (name, type), false, loc) as PropertySpec;
        }
 
        public static IList<PredefinedTypeSpec> InitCoreTypes ()
index 662cbe05bda323f26bd21e3abb1d3b25a6523a5f..1bb8348a6113421a0189f9ff6b2ae9b784717d7c 100644 (file)
     </type>
     <type name="Test+&lt;Lock&gt;c__AnonStorey4`1[T]">
       <method name="T &lt;&gt;m__4()">
-        <size>65</size>
+        <size>70</size>
       </method>
       <method name="Void .ctor()">
         <size>7</size>
         <size>7</size>
       </method>
       <method name="Void T()">
-        <size>59</size>
+        <size>69</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Int32 Main()">
-        <size>42</size>
+        <size>47</size>
       </method>
     </type>
   </test>
         <size>7</size>
       </method>
       <method name="Boolean MoveNext()">
-        <size>163</size>
+        <size>177</size>
       </method>
       <method name="Void Dispose()">
-        <size>55</size>
+        <size>63</size>
       </method>
       <method name="Void Reset()">
         <size>6</size>