[sgen] Fix pointer access.
Guidelines][1]. We have been using a coding style for many years,
please make your patches conform to these guidelines.
-[1] http://www.mono-project.com/Coding_Guidelines
+[1] http://www.mono-project.com/community/contributing/coding-guidelines/
Etiquette
=========
a maintainer will likely merge it. Otherwise, you can amend your pull
request to fix build breakage and Jenkins will test it again.
-[2] http://monojenkins.cloudapp.net
+[2] http://jenkins.mono-project.com/
# Inactivity
AC_DEFINE(MINGW_CROSS_COMPILE,1,[Cross-compiling using MinGW])
fi
HOST_CC="gcc"
- # Windows 2000 is required that includes Internet Explorer 5.01
+ # Windows XP SP2 is required
CPPFLAGS="$CPPFLAGS -DWINVER=0x0502 -D_WIN32_WINNT=0x0502 -D_WIN32_IE=0x0501 -D_UNICODE -DUNICODE -DWIN32_THREADS -DFD_SETSIZE=1024"
LDFLAGS="$LDFLAGS -lmswsock -lws2_32 -lole32 -loleaut32 -lpsapi -lversion -ladvapi32 -lwinmm -lkernel32"
libmono_cflags="-mms-bitfields -mwindows"
;;
esac
;;
+ i686*-linux-*)
+ TARGET=X86;
+ arch_target=x86;
+ AC_DEFINE(TARGET_X86, 1, [...])
+ AC_DEFINE(TARGET_ANDROID, 1, [...])
+ JIT_SUPPORTED=yes
+ CPPFLAGS="$CPPFLAGS"
+ jit_wanted=true
+ sgen_supported=true
+ # Can't use tls, since it depends on the runtime detection of tls offsets
+ # in mono-compiler.h
+ with_tls=pthread
+ target_mach=no
+ ;;
aarch64-*)
TARGET=ARM64
JIT_SUPPORTED=yes
program but will obviously use more memory. The default nursery size
4 MB.
.TP
-\fBmajor=\fIcollector\fR
-Specifies which major collector to use. Options are `marksweep' for
-the Mark&Sweep collector, `marksweep-conc' for concurrent Mark&Sweep,
-`marksweep-par' for parallel Mark&Sweep, `marksweep-fixed' for
-Mark&Sweep with a fixed heap, and `marksweep-fixed-par' for parallel
-Mark&Sweep with a fixed heap. The Mark&Sweep collector is the default.
-.TP
-\fBmajor-heap-size=\fIsize\fR
-Sets the size of the major heap (not including the large object space)
-for the fixed-heap Mark&Sweep collector (i.e. `marksweep-fixed' and
-`marksweep-fixed-par'). The size is in bytes, with optional suffixes
-`k', `m' and `g' to specify kilo-, mega- and gigabytes, respectively.
-The default is 512 megabytes.
+\fBmajor=\fIcollector\fR Specifies which major collector to use.
+Options are `marksweep' for the Mark&Sweep collector, and
+`marksweep-conc' for concurrent Mark&Sweep. The non-concurrent
+Mark&Sweep collector is the default.
.TP
\fBsoft-heap-limit=\fIsize\fR
Once the heap size gets larger than this size, ignore what the default
sgen-gc.c. You can then use this command to explore the output
.nf
sgen-grep-binprot 0x1234 0x5678 < file
+.TP
+\fBnursery-canaries\fR
+If set, objects allocated in the nursery are suffixed with a canary (guard)
+word, which is checked on each minor collection. Can be used to detect/debug
+heap corruption issues.
.fi
.ne
.RE
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.COMException))]
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.ComMemberType))]
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.ComSourceInterfacesAttribute))]
+[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.ComInterfaceType))]
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.ComTypes.IStream))]
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.ComTypes.STATSTG))]
[assembly: System.Runtime.CompilerServices.TypeForwardedToAttribute(typeof(System.Runtime.InteropServices.CriticalHandle))]
Rectangle text_rectangle;
Rectangle image_rectangle;
- ThemeEngine.Current.CalculateButtonTextAndImageLayout (this, out text_rectangle, out image_rectangle);
+ ThemeEngine.Current.CalculateButtonTextAndImageLayout (pevent.Graphics, this, out text_rectangle, out image_rectangle);
// Draw our button
if (this.FlatStyle == FlatStyle.Standard)
#region Button
public abstract Size CalculateButtonAutoSize (Button button);
- public abstract void CalculateButtonTextAndImageLayout (ButtonBase b, out Rectangle textRectangle, out Rectangle imageRectangle);
+ public abstract void CalculateButtonTextAndImageLayout (Graphics g, ButtonBase b, out Rectangle textRectangle, out Rectangle imageRectangle);
public abstract void DrawButton (Graphics g, Button b, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle);
public abstract void DrawFlatButton (Graphics g, ButtonBase b, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle);
public abstract void DrawPopupButton (Graphics g, Button b, Rectangle textBounds, Rectangle imageBounds, Rectangle clipRectangle);
return ret_size;
}
- public override void CalculateButtonTextAndImageLayout (ButtonBase button, out Rectangle textRectangle, out Rectangle imageRectangle)
+ public override void CalculateButtonTextAndImageLayout (Graphics g, ButtonBase button, out Rectangle textRectangle, out Rectangle imageRectangle)
{
Image image = button.Image;
string text = button.Text;
Rectangle content_rect = button.PaddingClientRectangle;
- Size text_size = TextRenderer.MeasureTextInternal (text, button.Font, content_rect.Size, button.TextFormatFlags, button.UseCompatibleTextRendering);
+ if (button.TextImageRelation != TextImageRelation.Overlay)
+ content_rect.Inflate(-4, -4);
+ Size text_size = TextRenderer.MeasureTextInternal (g, text, button.Font, content_rect.Size, button.TextFormatFlags, button.UseCompatibleTextRendering);
Size image_size = image == null ? Size.Empty : image.Size;
textRectangle = Rectangle.Empty;
imageRectangle = new Rectangle (image_x, image_y, image_width, image_height);
break;
case TextImageRelation.ImageAboveText:
- content_rect.Inflate (-4, -4);
LayoutTextAboveOrBelowImage (content_rect, false, text_size, image_size, button.TextAlign, button.ImageAlign, out textRectangle, out imageRectangle);
break;
case TextImageRelation.TextAboveImage:
- content_rect.Inflate (-4, -4);
LayoutTextAboveOrBelowImage (content_rect, true, text_size, image_size, button.TextAlign, button.ImageAlign, out textRectangle, out imageRectangle);
break;
case TextImageRelation.ImageBeforeText:
- content_rect.Inflate (-4, -4);
LayoutTextBeforeOrAfterImage (content_rect, false, text_size, image_size, button.TextAlign, button.ImageAlign, out textRectangle, out imageRectangle);
break;
case TextImageRelation.TextBeforeImage:
- content_rect.Inflate (-4, -4);
LayoutTextBeforeOrAfterImage (content_rect, true, text_size, image_size, button.TextAlign, button.ImageAlign, out textRectangle, out imageRectangle);
break;
}
<Compile Include="Microsoft.Build.BuildEngine\TaskBatchingImpl.cs" />\r
<Compile Include="Microsoft.Build.BuildEngine\TaskDatabase.cs" />\r
<Compile Include="Microsoft.Build.BuildEngine\TaskEngine.cs" />\r
+ <Compile Include="Microsoft.Build.BuildEngine\TaskExecutionMode.cs" />\r
<Compile Include="Microsoft.Build.BuildEngine\Token.cs" />\r
<Compile Include="Microsoft.Build.BuildEngine\Toolset.cs" />\r
<Compile Include="Microsoft.Build.BuildEngine\ToolsetCollection.cs" />\r
[Flags]
enum InvokeFlags {
- NONE = 0x0,
- DISABLE_BREAKPOINTS = 0x1,
- SINGLE_THREADED = 0x2,
- OUT_THIS = 0x4,
- OUT_ARGS = 0x8,
+ NONE = 0,
+ DISABLE_BREAKPOINTS = 1,
+ SINGLE_THREADED = 2,
+ OUT_THIS = 4,
+ OUT_ARGS = 8,
+ VIRTUAL = 16,
}
enum ElementType {
* with newer runtimes, and vice versa.
*/
internal const int MAJOR_VERSION = 2;
- internal const int MINOR_VERSION = 35;
+ internal const int MINOR_VERSION = 37;
enum WPSuspendPolicy {
NONE = 0,
/*
* Return the values of out arguments
*/
- ReturnOutArgs = 8
+ ReturnOutArgs = 8,
+ /*
+ * Do a virtual invoke
+ * Since protocol version 2.37
+ */
+ Virtual = 16
}
}
f |= InvokeFlags.OUT_THIS;
if ((options & InvokeOptions.ReturnOutArgs) != 0)
f |= InvokeFlags.OUT_ARGS;
+ if ((options & InvokeOptions.Virtual) != 0)
+ f |= InvokeFlags.VIRTUAL;
InvokeAsyncResult r = new InvokeAsyncResult { AsyncState = state, AsyncWaitHandle = new ManualResetEvent (false), VM = vm, Thread = thread, Callback = callback };
thread.InvalidateFrames ();
static string base_static_s = "C";
#pragma warning restore 0414
#pragma warning restore 0169
+
+ public virtual string virtual_method () {
+ return "V1";
+ }
}
public enum AnEnum {
j = 5;
set_ip_2 ();
}
+
+ public override string virtual_method () {
+ return "V2";
+ }
}
class TypeLoadClass {
v = this_obj.InvokeMethod (e.Thread, m, null);
AssertValue (42, v);
+ // virtual call
+ m = t.BaseType.GetMethod ("virtual_method");
+ v = this_obj.InvokeMethod (e.Thread, m, null, InvokeOptions.Virtual);
+ AssertValue ("V2", v);
+
#if NET_4_5
// instance
m = t.GetMethod ("invoke_pass_ref");
string msg = Locale.GetText ("Could not create user key store '{0}'.");
throw new CryptographicException (String.Format (msg, _userPath), e);
}
-
- if (!ProtectUser (_userPath)) {
- string msg = Locale.GetText ("Could not secure user key store '{0}'.");
- throw new IOException (String.Format (msg, _userPath));
- }
-
_userPathExists = true;
}
}
+ if (!IsUserProtected (_userPath) && !ProtectUser (_userPath)) {
+ string msg = Locale.GetText ("Could not secure user key store '{0}'.");
+ throw new IOException (String.Format (msg, _userPath));
+ }
}
// is it properly protected ?
if (!IsUserProtected (_userPath)) {
string msg = Locale.GetText ("Could not create machine key store '{0}'.");
throw new CryptographicException (String.Format (msg, _machinePath), e);
}
-
- if (!ProtectMachine (_machinePath)) {
- string msg = Locale.GetText ("Could not secure machine key store '{0}'.");
- throw new IOException (String.Format (msg, _machinePath));
- }
-
_machinePathExists = true;
}
}
+ if (!IsMachineProtected (_machinePath) && !ProtectMachine (_machinePath)) {
+ string msg = Locale.GetText ("Could not secure machine key store '{0}'.");
+ throw new IOException (String.Format (msg, _machinePath));
+ }
}
// is it properly protected ?
if (!IsMachineProtected (_machinePath)) {
// methods
public void Clear ()
+ {
+ /*
+ * Both _certificates and _crls extend CollectionBase, whose Clear() method calls OnClear() and
+ * OnClearComplete(), which should be overridden in derivative classes. So we should not worry about
+ * other threads that might be holding references to _certificates or _crls. They should be smart enough
+ * to handle this gracefully. And if not, it's their own fault.
+ */
+ ClearCertificates ();
+ ClearCrls ();
+ }
+
+ void ClearCertificates()
{
if (_certificates != null)
_certificates.Clear ();
_certificates = null;
+ }
+
+ void ClearCrls ()
+ {
if (_crls != null)
_crls.Clear ();
_crls = null;
fs.Write (data, 0, data.Length);
fs.Close ();
}
+ ClearCertificates (); // We have modified the store on disk. So forget the old state.
}
#if !NET_2_1
// Try to save privateKey if available..
byte[] data = crl.RawData;
fs.Write (data, 0, data.Length);
}
+ ClearCrls (); // We have modified the store on disk. So forget the old state.
}
}
string filename = Path.Combine (_storePath, GetUniqueName (certificate));
if (File.Exists (filename)) {
File.Delete (filename);
+ ClearCertificates (); // We have modified the store on disk. So forget the old state.
}
}
string filename = Path.Combine (_storePath, GetUniqueName (crl));
if (File.Exists (filename)) {
File.Delete (filename);
+ ClearCrls (); // We have modified the store on disk. So forget the old state.
}
}
{
IntPtr value = IntPtr.Zero;
int n = 0;
- string defaultTimeZone;
+ string defaultTimeZone = Environment.GetEnvironmentVariable ("__XA_OVERRIDE_TIMEZONE_ID__");
+
+ if (!string.IsNullOrEmpty (defaultTimeZone))
+ return defaultTimeZone;
// Used by the tests
if (Environment.GetEnvironmentVariable ("__XA_USE_JAVA_DEFAULT_TIMEZONE_ID__") == null)
#if SELF_TEST
/*
* Compile:
- * mcs /out:tzi.exe /unsafe "/d:INSIDE_CORLIB;MONODROID;NET_4_0;LIBC;SELF_TEST" System/TimeZone*.cs ../../build/common/Consts.cs ../Mono.Options/Mono.Options/Options.cs
+ * mcs /debug+ /out:tzi.exe /unsafe "/d:INSIDE_CORLIB;MONODROID;NET_4_0;LIBC;SELF_TEST" ../corlib/System/AndroidPlatform.cs System/TimeZone*.cs ../../build/common/Consts.cs ../Mono.Options/Mono.Options/Options.cs
* Prep:
* mkdir -p usr/share/zoneinfo
+ * mkdir -p misc/zoneinfo/zoneinfo
* android_root=`adb shell echo '$ANDROID_ROOT' | tr -d "\r"`
+ * android_data=`adb shell echo '$ANDROID_DATA' | tr -d "\r"`
* adb pull $android_root/usr/share/zoneinfo usr/share/zoneinfo
+ * adb pull $android_data/misc/zoneinfo/tzdata misc/zoneinfo
* Run:
- * ANDROID_ROOT=`pwd` mono tzi.exe
+ * __XA_OVERRIDE_TIMEZONE_ID__=America/New_York ANDROID_ROOT=`pwd` ANDROID_DATA=`pwd` mono --debug tzi.exe --offset=1969-01-01
*/
static void Main (string[] args)
{
+ DateTime? offset = null;
Func<IAndroidTimeZoneDB> c = () => GetDefaultTimeZoneDB ();
Mono.Options.OptionSet p = null;
p = new Mono.Options.OptionSet () {
{ "Z=", "Create ZoneInfoDB from {DIR}.", v => {
c = () => new ZoneInfoDB (v);
} },
+ { "offset=", "Show timezone info offset for DateTime {OFFSET}.", v => {
+ offset = DateTime.Parse (v);
+ Console.WriteLine ("Using DateTime Offset: {0}", offset);
+ } },
{ "help", "Show this message and exit", v => {
p.WriteOptionDescriptions (Console.Out);
Environment.Exit (0);
Console.Write ("name={0,-40}", id);
try {
TimeZoneInfo zone = _GetTimeZone (id);
- if (zone != null)
- Console.Write (" {0}", zone);
+ if (zone != null) {
+ Console.Write (" {0,-40}", zone);
+ if (offset.HasValue) {
+ Console.Write ("From Offset: {0}", zone.GetUtcOffset (offset.Value));
+ }
+ }
else {
Console.Write (" ERROR:null");
}
<Compile Include="System.Data.SqlClient\SqlCommandBuilder.cs" />\r
<Compile Include="System.Data.SqlClient\SqlConnection.cs" />\r
<Compile Include="System.Data.SqlClient\SqlConnectionStringBuilder.cs" />\r
+ <Compile Include="System.Data.SqlClient\SqlCredential.cs" />\r
<Compile Include="System.Data.SqlClient\SqlDataAdapter.cs" />\r
<Compile Include="System.Data.SqlClient\SqlDataReader.cs" />\r
<Compile Include="System.Data.SqlClient\SqlDataSourceConverter.cs" />\r
System.Data.SqlClient/SqlCommandBuilder.cs
System.Data.SqlClient/SqlConnection.cs
System.Data.SqlClient/SqlConnectionStringBuilder.cs
+System.Data.SqlClient/SqlCredential.cs
System.Data.SqlClient/SqlDataAdapter.cs
System.Data.SqlClient/SqlDataReader.cs
System.Data.SqlClient/SqlDataSourceConverter.cs
--- /dev/null
+//
+// BootstrapContext.cs
+//
+// Author:
+// Robert J. van der Boon (rjvdboon@gmail.com)
+//
+// Copyright (C) 2014 Robert J. van der Boon
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+#if NET_4_5
+using System;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Text;
+using System.Xml;
+
+namespace System.IdentityModel.Tokens {
+ [Serializable]
+ public class BootstrapContext : ISerializable {
+ /// <summary>Gets the string that was used to initialize the context.</summary>
+ public string Token { get; private set; }
+ /// <summary>Gets the array that was used to initialize the context.</summary>
+ public byte [] TokenBytes { get; private set; }
+ /// <summary>Gets the security token that was used to initialize the context.</summary>
+ public SecurityToken SecurityToken { get; private set; }
+ /// <summary>Gets the token handler that was used to initialize the context.</summary>
+ public SecurityTokenHandler SecurityTokenHandler { get; private set; }
+
+ /// <summary>Initializes a new instance of the <see cref="BootstrapContext"/> class by using the specified string.</summary>
+ public BootstrapContext (string token)
+ {
+ if (token == null)
+ throw new ArgumentNullException ("token");
+ Token = token;
+ }
+
+ /// <summary>Initializes a new instance of the <see cref="BootstrapContext"/> class by using the specified array.</summary>
+ public BootstrapContext (byte [] token)
+ {
+ if (token == null)
+ throw new ArgumentNullException ("token");
+ TokenBytes = token;
+ }
+
+ /// <summary>Initializes a new instance of the <see cref="BootstrapContext"/> class by using the specified security token and token handler.</summary>
+ public BootstrapContext (SecurityToken token, SecurityTokenHandler handler)
+ {
+ if (token == null)
+ throw new ArgumentNullException ("token");
+ if (handler == null)
+ throw new ArgumentNullException ("handler");
+ SecurityToken = token;
+ SecurityTokenHandler = handler;
+ }
+
+ /// <summary>Initializes a new instance of the <see cref="BootstrapContext"/> class from a stream.</summary>
+ protected BootstrapContext (SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException ("info");
+ char type = info.GetChar ("K");
+ switch (type) {
+ case 'S':
+ Token = info.GetString ("T");
+ break;
+ case 'B':
+ TokenBytes = (byte [])info.GetValue ("T", typeof (byte []));
+ break;
+ case 'T':
+ Token = Encoding.UTF8.GetString (Convert.FromBase64String (info.GetString ("T")));
+ break;
+ }
+ }
+
+ /// <summary>Populates the <see cref="SerializationInfo"/> with data needed to serialize the current <see cref="BootstrapContext"/> object.</summary>
+ public void GetObjectData (SerializationInfo info, StreamingContext context)
+ {
+ if (info == null)
+ throw new ArgumentNullException ("info");
+ if (Token != null) {
+ info.AddValue ("K", 'S');
+ info.AddValue ("T", Token);
+ } else if (TokenBytes != null) {
+ info.AddValue ("K", 'B');
+ info.AddValue ("T", TokenBytes);
+ } else if (SecurityToken != null && SecurityTokenHandler != null) {
+ info.AddValue ("K", 'T');
+ using (var ms = new MemoryStream ())
+ using (var streamWriter = new StreamWriter (ms, new UTF8Encoding (false)))
+ using (var writer = XmlWriter.Create (streamWriter, new XmlWriterSettings { OmitXmlDeclaration = true })) {
+ SecurityTokenHandler.WriteToken (writer, SecurityToken);
+ writer.Flush ();
+ info.AddValue ("T", Convert.ToBase64String (ms.ToArray ()));
+ }
+ }
+ }
+ }
+}
+#endif
System.IdentityModel.Tokens/AuthenticationContext.cs
System.IdentityModel.Tokens/AuthenticationInformation.cs
System.IdentityModel.Tokens/BinaryKeyIdentifierClause.cs
+System.IdentityModel.Tokens/BootstrapContext.cs
System.IdentityModel.Tokens/EncryptedKeyIdentifierClause.cs
System.IdentityModel.Tokens/EncryptingCredentials.cs
System.IdentityModel.Tokens/GenericXmlSecurityToken.cs
System.IdentityModel.Selectors/TestEvaluationContext.cs
System.IdentityModel.Selectors/X509SecurityTokenAuthenticatorTest.cs
System.IdentityModel.Selectors/X509SecurityTokenProviderTest.cs
+System.IdentityModel.Tokens/BootstrapContextTest.cs
System.IdentityModel.Tokens/EncryptedKeyIdentifierClauseTest.cs
System.IdentityModel.Tokens/InMemorySymmetricSecurityKeyTest.cs
System.IdentityModel.Tokens/LocalIdKeyIdentifierClauseTest.cs
--- /dev/null
+//
+// BootstrapContextTest.cs - NUnit Test Cases for System.IdentityModel.Tokens.BootstrapContext
+//
+
+#if NET_4_5
+using System;
+using System.IO;
+using System.IdentityModel.Tokens;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+using System.Text;
+using System.Xml;
+using NUnit.Framework;
+
+namespace MonoTests.System.IdentityModel.Tokens.net_4_5 {
+ [TestFixture]
+ public class BootstrapContextTest {
+ // The following byte arrays are the serialized bytes as emitted on Microsoft .Net 4.5.
+ private static readonly byte [] SerializedBootstrapContextByteArray = new byte [] { 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, 0x00, 0x57, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2C, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x34, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x2C, 0x20, 0x43, 0x75, 0x6C, 0x74, 0x75, 0x72, 0x65, 0x3D, 0x6E, 0x65, 0x75, 0x74, 0x72, 0x61, 0x6C, 0x2C, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 0x62, 0x37, 0x37, 0x61, 0x35, 0x63, 0x35, 0x36, 0x31, 0x39, 0x33, 0x34, 0x65, 0x30, 0x38, 0x39, 0x05, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2E, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x73, 0x2E, 0x42, 0x6F, 0x6F, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x02, 0x00, 0x00, 0x00, 0x01, 0x4B, 0x01, 0x54, 0x00, 0x07, 0x03, 0x02, 0x02, 0x00, 0x00, 0x00, 0x42, 0x09, 0x03, 0x00, 0x00, 0x00, 0x0F, 0x03, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x02, 0x01, 0x0B };
+ private static readonly byte [] SerializedBootstrapContextString = new byte [] { 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, 0x00, 0x57, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2C, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x34, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x2C, 0x20, 0x43, 0x75, 0x6C, 0x74, 0x75, 0x72, 0x65, 0x3D, 0x6E, 0x65, 0x75, 0x74, 0x72, 0x61, 0x6C, 0x2C, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 0x62, 0x37, 0x37, 0x61, 0x35, 0x63, 0x35, 0x36, 0x31, 0x39, 0x33, 0x34, 0x65, 0x30, 0x38, 0x39, 0x05, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2E, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x73, 0x2E, 0x42, 0x6F, 0x6F, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x02, 0x00, 0x00, 0x00, 0x01, 0x4B, 0x01, 0x54, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00, 0x53, 0x06, 0x03, 0x00, 0x00, 0x00, 0x05, 0x74, 0x6F, 0x6B, 0x65, 0x6E, 0x0B };
+
+ // Put in some non-ascii/latin1 characters to test the encoding scheme
+ // \u018E == Latin capital letter Reversed E
+ private const string user = "us\u018Er";
+ // \u00BD == Vulgar Fraction one half
+ private const string password = "pass\u00BDword";
+ private static readonly string SerializedBootstrapContextSecurityTokenString = "<UserNameSecurityToken Id=\"uuid-927c0b98-ba18-49d2-a653-306d60f85751-3\" Username=\"" + user + "\" Password=\"" + password + "\"/>";
+ private static readonly byte [] SerializedBootstrapContextSecurityToken = new byte [] { 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x00, 0x00, 0x00, 0x57, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2C, 0x20, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x3D, 0x34, 0x2E, 0x30, 0x2E, 0x30, 0x2E, 0x30, 0x2C, 0x20, 0x43, 0x75, 0x6C, 0x74, 0x75, 0x72, 0x65, 0x3D, 0x6E, 0x65, 0x75, 0x74, 0x72, 0x61, 0x6C, 0x2C, 0x20, 0x50, 0x75, 0x62, 0x6C, 0x69, 0x63, 0x4B, 0x65, 0x79, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x3D, 0x62, 0x37, 0x37, 0x61, 0x35, 0x63, 0x35, 0x36, 0x31, 0x39, 0x33, 0x34, 0x65, 0x30, 0x38, 0x39, 0x05, 0x01, 0x00, 0x00, 0x00, 0x2C, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x2E, 0x49, 0x64, 0x65, 0x6E, 0x74, 0x69, 0x74, 0x79, 0x4D, 0x6F, 0x64, 0x65, 0x6C, 0x2E, 0x54, 0x6F, 0x6B, 0x65, 0x6E, 0x73, 0x2E, 0x42, 0x6F, 0x6F, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x43, 0x6F, 0x6E, 0x74, 0x65, 0x78, 0x74, 0x02, 0x00, 0x00, 0x00, 0x01, 0x4B, 0x01, 0x54, 0x00, 0x01, 0x03, 0x02, 0x00, 0x00, 0x00, 0x54, 0x06, 0x03, 0x00, 0x00, 0x00, 0x98, 0x01, 0x50, 0x46, 0x56, 0x7A, 0x5A, 0x58, 0x4A, 0x4F, 0x59, 0x57, 0x31, 0x6C, 0x55, 0x32, 0x56, 0x6A, 0x64, 0x58, 0x4A, 0x70, 0x64, 0x48, 0x6C, 0x55, 0x62, 0x32, 0x74, 0x6C, 0x62, 0x69, 0x42, 0x4A, 0x5A, 0x44, 0x30, 0x69, 0x64, 0x58, 0x56, 0x70, 0x5A, 0x43, 0x30, 0x35, 0x4D, 0x6A, 0x64, 0x6A, 0x4D, 0x47, 0x49, 0x35, 0x4F, 0x43, 0x31, 0x69, 0x59, 0x54, 0x45, 0x34, 0x4C, 0x54, 0x51, 0x35, 0x5A, 0x44, 0x49, 0x74, 0x59, 0x54, 0x59, 0x31, 0x4D, 0x79, 0x30, 0x7A, 0x4D, 0x44, 0x5A, 0x6B, 0x4E, 0x6A, 0x42, 0x6D, 0x4F, 0x44, 0x55, 0x33, 0x4E, 0x54, 0x45, 0x74, 0x4D, 0x79, 0x49, 0x67, 0x56, 0x58, 0x4E, 0x6C, 0x63, 0x6D, 0x35, 0x68, 0x62, 0x57, 0x55, 0x39, 0x49, 0x6E, 0x56, 0x7A, 0x78, 0x6F, 0x35, 0x79, 0x49, 0x69, 0x42, 0x51, 0x59, 0x58, 0x4E, 0x7A, 0x64, 0x32, 0x39, 0x79, 0x5A, 0x44, 0x30, 0x69, 0x63, 0x47, 0x46, 0x7A, 0x63, 0x38, 0x4B, 0x39, 0x64, 0x32, 0x39, 0x79, 0x5A, 0x43, 0x49, 0x76, 0x50, 0x67, 0x3D, 0x3D, 0x0B };
+
+ [Test]
+ public void Ctor_StringToken_Works ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext ("token");
+
+ Assert.AreEqual ("token", bootstrapContext.Token, "#1");
+ Assert.IsNull (bootstrapContext.TokenBytes, "#2");
+ Assert.IsNull (bootstrapContext.SecurityToken, "#3");
+ Assert.IsNull (bootstrapContext.SecurityTokenHandler, "#4");
+ }
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void Ctor_StringToken_NullToken_Throws ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext ((string)null);
+ Assert.Fail ("Should have thrown");
+ }
+
+ [Test]
+ public void Serialize_StringToken_Works ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext ("token");
+ BinaryFormatter binaryFormatter = new BinaryFormatter ();
+ using (var s = new MemoryStream ()) {
+ binaryFormatter.Serialize (s, bootstrapContext);
+ s.Position = 0;
+ BootstrapContext bootstrapContext2 = binaryFormatter.Deserialize (s) as BootstrapContext;
+ Assert.IsNotNull (bootstrapContext2, "#1");
+ Assert.AreEqual (bootstrapContext.Token, bootstrapContext2.Token, "#2");
+ Assert.AreEqual (bootstrapContext.TokenBytes, bootstrapContext2.TokenBytes, "#3");
+ Assert.AreEqual (bootstrapContext.SecurityToken, bootstrapContext2.SecurityToken, "#4");
+ Assert.AreEqual (bootstrapContext.SecurityTokenHandler, bootstrapContext2.SecurityTokenHandler, "#5");
+ }
+ }
+
+ [Test]
+ public void Deserialize_StringToken_Works ()
+ {
+ BinaryFormatter binaryFormatter = new BinaryFormatter ();
+ using (var s = new MemoryStream (SerializedBootstrapContextString)) {
+ BootstrapContext bootstrapContext = binaryFormatter.Deserialize (s) as BootstrapContext;
+ Assert.IsNotNull (bootstrapContext, "#1");
+ Assert.AreEqual ("token", bootstrapContext.Token, "#2");
+ Assert.IsNull (bootstrapContext.TokenBytes, "#3");
+ Assert.IsNull (bootstrapContext.SecurityToken, "#4");
+ Assert.IsNull (bootstrapContext.SecurityTokenHandler, "#5");
+ }
+ }
+
+ [Test]
+ public void Ctor_ByteArrayToken_Works ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext (new byte [] { 0x01 });
+
+ Assert.IsNotNull (bootstrapContext.TokenBytes, "#1");
+ Assert.AreEqual (1, bootstrapContext.TokenBytes.Length, "#2");
+ Assert.AreEqual (1, bootstrapContext.TokenBytes [0], "#3");
+ Assert.IsNull (bootstrapContext.Token, "#4");
+ Assert.IsNull (bootstrapContext.SecurityToken, "#5");
+ Assert.IsNull (bootstrapContext.SecurityTokenHandler, "#6");
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void Ctor_ByteArrayToken_NullToken_Throws ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext ((byte [])null);
+ Assert.Fail ("Should have thrown");
+ }
+
+ [Test]
+ public void Serialize_ByteArrayToken_Works ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext (new byte [] { 0x1 });
+ BinaryFormatter binaryFormatter = new BinaryFormatter ();
+ using (var s = new MemoryStream ()) {
+ binaryFormatter.Serialize (s, bootstrapContext);
+ s.Position = 0;
+ BootstrapContext bootstrapContext2 = binaryFormatter.Deserialize (s) as BootstrapContext;
+ Assert.IsNotNull (bootstrapContext2, "#1");
+ Assert.AreEqual (bootstrapContext.Token, bootstrapContext2.Token, "#2");
+ Assert.AreEqual (bootstrapContext.TokenBytes, bootstrapContext2.TokenBytes, "#3");
+ Assert.AreEqual (bootstrapContext.SecurityToken, bootstrapContext2.SecurityToken, "#4");
+ Assert.AreEqual (bootstrapContext.SecurityTokenHandler, bootstrapContext2.SecurityTokenHandler, "#5");
+ }
+ }
+
+ [Test]
+ public void Deserialize_ByteArrayToken_Works ()
+ {
+ BinaryFormatter binaryFormatter = new BinaryFormatter ();
+ using (var s = new MemoryStream (SerializedBootstrapContextByteArray)) {
+ BootstrapContext bootstrapContext = binaryFormatter.Deserialize (s) as BootstrapContext;
+ Assert.IsNotNull (bootstrapContext, "#1");
+ Assert.IsNotNull (bootstrapContext.TokenBytes, "#2");
+ Assert.AreEqual (1, bootstrapContext.TokenBytes.Length, "#3");
+ Assert.AreEqual (1, bootstrapContext.TokenBytes [0], "#4");
+ Assert.IsNull (bootstrapContext.Token, "#5");
+ Assert.IsNull (bootstrapContext.SecurityToken, "#6");
+ Assert.IsNull (bootstrapContext.SecurityTokenHandler, "#7");
+ }
+ }
+
+ [Test]
+ public void Ctor_SecurityToken_Works ()
+ {
+ var securityToken = new UserNameSecurityToken (user, password);
+ var securityTokenHandler = new SimpleSecurityTokenHandler ();
+ BootstrapContext bootstrapContext = new BootstrapContext (securityToken, securityTokenHandler);
+
+ Assert.IsNotNull (bootstrapContext.SecurityToken, "#1");
+ Assert.AreEqual (user, securityToken.UserName, "#2");
+ Assert.AreEqual (password, securityToken.Password, "#3");
+ Assert.AreEqual (securityTokenHandler, bootstrapContext.SecurityTokenHandler, "#4");
+
+ Assert.IsNull (bootstrapContext.Token, "#5");
+ Assert.IsNull (bootstrapContext.TokenBytes, "#6");
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void Ctor_SecurityToken_NullToken_Throws ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext (null, new SimpleSecurityTokenHandler ());
+ Assert.Fail ("Should have thrown");
+ }
+
+ [Test]
+ [ExpectedException (typeof (ArgumentNullException))]
+ public void Ctor_SecurityToken_NullHandler_Throws ()
+ {
+ BootstrapContext bootstrapContext = new BootstrapContext (new UserNameSecurityToken ("user", "password"), null);
+ Assert.Fail ("Should have thrown");
+ }
+
+ [Test]
+ public void Serialize_SecurityTokenAndHandler_Works ()
+ {
+ var securityToken = new UserNameSecurityToken (user, password, "uuid-927c0b98-ba18-49d2-a653-306d60f85751-3");
+ var securityTokenHandler = new SimpleSecurityTokenHandler ();
+ BootstrapContext bootstrapContext = new BootstrapContext (securityToken, securityTokenHandler);
+
+ BinaryFormatter binaryFormatter = new BinaryFormatter ();
+ using (var s = new MemoryStream ()) {
+ binaryFormatter.Serialize (s, bootstrapContext);
+ s.Position = 0;
+ BootstrapContext bootstrapContext2 = binaryFormatter.Deserialize (s) as BootstrapContext;
+ Assert.IsNotNull (bootstrapContext2, "#1");
+ // Deserialize does not restore the SecurityToken, but restores into the Token.
+ Assert.IsNotNull (bootstrapContext2.Token, "#3");
+ // We replace ' /' by '/' to accomodate the xml writer differences between mono and .net
+ Assert.AreEqual (SerializedBootstrapContextSecurityTokenString.Replace (" /", "/"), bootstrapContext2.Token.Replace (" /", "/"), "#2");
+ Assert.AreEqual (bootstrapContext.TokenBytes, bootstrapContext2.TokenBytes, "#3");
+ Assert.IsNull (bootstrapContext2.SecurityToken, "#4");
+ Assert.IsNull (bootstrapContext2.SecurityTokenHandler, "#5");
+ }
+ }
+
+ [Test]
+ public void Deserialize_SecurityTokenAndHandler_Works ()
+ {
+ BinaryFormatter binaryFormatter = new BinaryFormatter ();
+ using (var s = new MemoryStream (SerializedBootstrapContextSecurityToken)) {
+ BootstrapContext bootstrapContext = binaryFormatter.Deserialize (s) as BootstrapContext;
+ Assert.IsNotNull (bootstrapContext, "#1");
+ Assert.AreEqual (SerializedBootstrapContextSecurityTokenString, bootstrapContext.Token, "#2");
+ Assert.IsNull (bootstrapContext.SecurityToken, "#3");
+ Assert.IsNull (bootstrapContext.SecurityTokenHandler, "#4");
+ Assert.IsNull (bootstrapContext.TokenBytes, "#5");
+ }
+ }
+
+ private static void DumpAsText (byte [] data)
+ {
+ Console.WriteLine ("{0}", Encoding.ASCII.GetString (data));
+ }
+
+ private static void Dump (byte [] data)
+ {
+ var sb = new StringBuilder ();
+ sb.Append ("new byte[] { ");
+ bool first = true;
+ foreach (byte b in data) {
+ if (!first)
+ sb.Append (", ");
+ else
+ first = false;
+ sb.AppendFormat ("0x{0:X2}", b);
+ }
+ sb.Append (" };");
+ Console.WriteLine (sb.ToString ());
+ }
+
+ private class SimpleSecurityTokenHandler : SecurityTokenHandler {
+ public override string [] GetTokenTypeIdentifiers ()
+ {
+ throw new NotImplementedException ();
+ }
+
+ public override Type TokenType {
+ get { return typeof (UserNameSecurityToken); }
+ }
+
+ public override bool CanWriteToken {
+ get { return true; }
+ }
+
+ public override void WriteToken (XmlWriter writer, SecurityToken token)
+ {
+ UserNameSecurityToken unst = token as UserNameSecurityToken;
+ if (unst == null)
+ throw new ArgumentException ("Token must be of type UserNameSecurityToken", "token");
+ writer.WriteStartElement ("UserNameSecurityToken");
+ writer.WriteAttributeString ("Id", unst.Id);
+ writer.WriteAttributeString ("Username", unst.UserName);
+ writer.WriteAttributeString ("Password", unst.Password);
+ writer.WriteEndElement ();
+ }
+ }
+ }
+}
+#endif
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
-
using System.ComponentModel;
namespace System.Management
{
- [MonoTODO ("System.Management is not implemented")]
[ToolboxItem (false)]
public class ManagementObjectSearcher : Component
{
+ ObjectQuery mQueryObject;
+
public ManagementObjectSearcher ()
{
- throw new NotImplementedException ();
+ mQueryObject = new ObjectQuery ();
}
public ManagementObjectSearcher (ObjectQuery query)
{
- throw new NotImplementedException ();
+ mQueryObject = (ObjectQuery)query.Clone ();
}
public ManagementObjectSearcher (string queryString)
{
- throw new NotImplementedException ();
+ mQueryObject = new ObjectQuery (queryString);
}
public ManagementObjectSearcher (ManagementScope scope, ObjectQuery query)
public ObjectQuery Query {
get {
- throw new NotImplementedException ();
+ return mQueryObject;
}
set {
- throw new NotImplementedException ();
+ mQueryObject = value;
}
}
throw new ArgumentOutOfRangeException ("maxItemsInObjectGraph");
this.type = type;
- known_types = new ReadOnlyCollection<Type> (knownTypes != null ? knownTypes.ToArray () : Type.EmptyTypes);
+
+ var knownTypesFromAttributes = new List<Type> ();
+
+ foreach (var attr in type.GetCustomAttributes (typeof (KnownTypeAttribute), false))
+ knownTypesFromAttributes.Add ((attr as KnownTypeAttribute).Type);
+
+ if (knownTypes != null)
+ knownTypesFromAttributes.AddRange (knownTypes);
+
+ known_types = new ReadOnlyCollection<Type> (knownTypesFromAttributes);
+
root = rootName;
max_items = maxItemsInObjectGraph;
ignore_extension = ignoreExtensionDataObject;
public bool IgnoreExtensionDataObject {
get { return ignore_extension; }
}
-
- [MonoTODO]
public ReadOnlyCollection<Type> KnownTypes {
get { return known_types; }
}
if (ct != null) {
return DeserializeGenericCollection (type, ct, instance);
} else {
- TypeMap map = GetTypeMap (type);
- return map.Deserialize (this, instance);
+ string typeHint = reader.GetAttribute ("__type");
+ if (typeHint != null) {
+ // this might be a derived & known type. We allow it when it's both.
+ Type exactType = GetRuntimeType (typeHint, type);
+ if (exactType == null)
+ throw SerializationError (String.Format ("Cannot load type '{0}'", typeHint));
+ TypeMap map = GetTypeMap (exactType);
+ return map.Deserialize (this, instance);
+ } else { // no type hint
+ TypeMap map = GetTypeMap (type);
+ return map.Deserialize (this, instance);
+ }
}
}
else
}
- Type GetRuntimeType (string name)
+ Type GetRuntimeType (string name, Type baseType)
{
- name = ToRuntimeTypeName (name);
+ string properName = ToRuntimeTypeName (name);
+
+ if (baseType != null && baseType.FullName.Equals (properName))
+ return baseType;
+
if (serializer.KnownTypes != null)
foreach (Type t in serializer.KnownTypes)
- if (t.FullName == name)
+ if (t.FullName.Equals (properName))
return t;
- var ret = root_type.Assembly.GetType (name, false) ?? Type.GetType (name, false);
- if (ret != null)
- return ret;
- // We probably have to iterate all the existing
- // assemblies that are loaded in current domain.
- foreach (var ass in AppDomain.CurrentDomain.GetAssemblies ()) {
- ret = ass.GetType (name, false);
- if (ret != null)
- return ret;
- }
+ if (baseType != null)
+ foreach (var attr in baseType.GetCustomAttributes (typeof (KnownTypeAttribute), false))
+ if ((attr as KnownTypeAttribute).Type.FullName.Equals (properName))
+ return (attr as KnownTypeAttribute).Type;
return null;
}
case "object":
string runtimeType = reader.GetAttribute ("__type");
if (runtimeType != null) {
- Type t = GetRuntimeType (runtimeType);
+ Type t = GetRuntimeType (runtimeType, null);
if (t == null)
throw SerializationError (String.Format ("Cannot load type '{0}'", runtimeType));
return ReadObject (t);
if (double.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dbl))
return dbl;
decimal dec;
- if (decimal.TryParse (v, NumberStyles.None, CultureInfo.InvariantCulture, out dec))
+ if (decimal.TryParse (v, NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out dec))
return dec;
throw SerializationError (String.Format ("Invalid JSON input: {0}", v));
default:
Item.Links.Add (l);
continue;
case "guid":
+ Item.Id = reader.ReadElementContentAsString ();
if (reader.GetAttribute ("isPermaLink") == "true")
- Item.AddPermalink (CreateUri (reader.ReadElementContentAsString ()));
- else
- Item.Id = reader.ReadElementContentAsString ();
+ Item.AddPermalink (CreateUri (Item.Id));
continue;
case "pubDate":
Item.PublishDate = FromRFC822DateString (reader.ReadElementContentAsString ());
return ser.ReadObject (xr);
}
+ public T Deserialize<T>(string json)
+ {
+ var bytes = Encoding.Unicode.GetBytes (json);
+ using (MemoryStream stream = new MemoryStream (bytes)) {
+ var serializer = new DataContractJsonSerializer (typeof(T));
+ return (T)serializer.ReadObject (stream);
+ }
+ }
+
[Test]
public void IsStartObject ()
{
serializer.WriteObject (stream, o);
}
+ // properly deserialize object with a polymorphic property (known derived type)
+ [Test]
+ public void Bug23058()
+ {
+ string serializedObj = @"{""PolymorphicProperty"":{""__type"":""KnownDerivedType:#MonoTests.System.Runtime.Serialization.Json"",""BaseTypeProperty"":""Base"",""DerivedProperty"":""Derived 1""},""Name"":""Parent2""}";
+ ParentType deserializedObj = Deserialize<ParentType> (serializedObj);
+
+ Assert.AreEqual (deserializedObj.PolymorphicProperty.GetType ().FullName, "MonoTests.System.Runtime.Serialization.Json.KnownDerivedType");
+ Assert.AreEqual (deserializedObj.PolymorphicProperty.BaseTypeProperty, "Base");
+ Assert.AreEqual ((deserializedObj.PolymorphicProperty as KnownDerivedType).DerivedProperty, "Derived 1");
+ Assert.AreEqual (deserializedObj.Name, "Parent2");
+ }
+
+ // properly deserialize object with a polymorphic property (base type with __type hint)
+ [Test]
+ public void DeserializeBaseTypePropHint()
+ {
+ string serializedObj = @"{""PolymorphicProperty"":{""__type"":""BaseType:#MonoTests.System.Runtime.Serialization.Json"",""BaseTypeProperty"":""Base""},""Name"":""Parent2""}";
+ ParentType deserializedObj = Deserialize<ParentType> (serializedObj);
+
+ Assert.AreEqual (deserializedObj.PolymorphicProperty.GetType ().FullName, "MonoTests.System.Runtime.Serialization.Json.BaseType");
+ Assert.AreEqual (deserializedObj.PolymorphicProperty.BaseTypeProperty, "Base");
+ }
+
+ // properly deserialize object with a polymorphic property (base type with __type hint)
+ [Test]
+ public void DeserializeBaseTypePropNoHint()
+ {
+ string serializedObj = @"{""PolymorphicProperty"":{""BaseTypeProperty"":""Base""},""Name"":""Parent2""}";
+ ParentType deserializedObj = Deserialize<ParentType> (serializedObj);
+
+ Assert.AreEqual (deserializedObj.PolymorphicProperty.GetType ().FullName, "MonoTests.System.Runtime.Serialization.Json.BaseType");
+ Assert.AreEqual (deserializedObj.PolymorphicProperty.BaseTypeProperty, "Base");
+ }
+
+ // properly fail deserializing object with a polymorphic property (unknown derived type)
+ [ExpectedException (typeof (SerializationException))]
+ [Test]
+ public void FailDeserializingUnknownTypeProp()
+ {
+ string serializedObj = @"{""PolymorphicProperty"":{""__type"":""UnknownDerivedType:#MonoTests.System.Runtime.Serialization.Json"",""BaseTypeProperty"":""Base"",""DerivedProperty"":""Derived 1""},""Name"":""Parent2""}";
+ ParentType deserializedObj = Deserialize<ParentType> (serializedObj);
+ }
+
#endregion
}
public long CodedServerTimeUTC { get; set; }
public DateTime ServerTimeUTC { get; set; }
}
+
+ #region polymorphism test helper classes
+
+ [DataContract]
+ [KnownType (typeof (KnownDerivedType))]
+ public class ParentType
+ {
+ [DataMember]
+ public string Name { get; set; }
+
+ [DataMember]
+ public BaseType PolymorphicProperty { get; set; }
+ }
+
+ [DataContract]
+ public class BaseType
+ {
+ [DataMember]
+ public string BaseTypeProperty { get; set; }
+ }
+
+ [DataContract]
+ public class KnownDerivedType : BaseType
+ {
+ [DataMemberAttribute]
+ public string DerivedProperty { get; set; }
+ }
+
+ [DataContract]
+ public class UnknownDerivedType : BaseType
+ {
+ [DataMember]
+ public string DerivedProperty { get; set; }
+ }
+
+ #endregion
}
[DataContract]
r.ReadStartElement ();
r.Read ();
}
+
+ [Test]
+ public void ReadNumberAsObject ()
+ {
+ const double testValue = 42.42D;
+ var serializer = new DataContractJsonSerializer (typeof (object));
+ var serializedStream = GetInput (testValue.ToString (CultureInfo.InvariantCulture));
+ var deserializedValue = serializer.ReadObject (serializedStream);
+ Assert.AreEqual (typeof (decimal), deserializedValue.GetType ());
+ Assert.AreEqual (testValue, (decimal) deserializedValue);
+ }
}
}
{
Assert.IsNull (((IXmlSerializable) new Rss20ItemFormatter ()).GetSchema ());
}
+
+ [Test]
+ public void ReadFromGuidPermaLink ()
+ {
+ const string xml1 = "<item><guid isPermaLink=\"false\">urn:myid</guid><description /></item>";
+ using (XmlReader r = CreateReader (xml1)) {
+ var rss = new Rss20ItemFormatter ();
+ rss.ReadFrom (r);
+ Assert.AreEqual ("urn:myid", rss.Item.Id);
+ }
+
+ const string xml2 = "<item><guid isPermaLink=\"true\">urn:myid</guid><description /></item>";
+ using (XmlReader r = CreateReader (xml2)) {
+ var rss = new Rss20ItemFormatter ();
+ rss.ReadFrom (r);
+ Assert.AreEqual ("urn:myid", rss.Item.Id);
+ }
+ }
}
}
#endif
\ No newline at end of file
internal void UpdateUser ()
{
- MembershipUser newUser = Provider.GetUser (name, false);
+ MembershipUser newUser = Provider.GetUser (UserName, false);
UpdateSelf (newUser);
}
StringBuilder sb = new StringBuilder ();
string [] keys = AllKeys;
for (int i = 0; i < count; i++) {
- sb.AppendFormat ("{0}={1}&", keys [i], this [keys [i]]);
+ sb.AppendFormat ("{0}={1}&", keys [i], UrlEncode (this [keys [i]]));
}
if (sb.Length > 0)
sb.Length--;
@" ¡¢£¤¥¦§¨©ª«¬­®¯°±²³´µ¶·¸¹º»¼½¾¿ÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖ×ØÙÚÛÜÝÞßàáâãäåæçèéêëìíîïðñòóôõö÷øùúûüýþÿ",
};
+ [Test]
+ public void ToStringEncoding ()
+ {
+ var queryStringNameValues = HttpUtility.ParseQueryString(string.Empty);
+ queryStringNameValues.Add("ReturnUrl", @"http://localhost/login/authenticate?ReturnUrl=http://localhost/secured_area&__provider__=google");
+
+ var expected = "ReturnUrl=http%3a%2f%2flocalhost%2flogin%2fauthenticate%3fReturnUrl%3dhttp%3a%2f%2flocalhost%2fsecured_area%26__provider__%3dgoogle";
+ Assert.AreEqual (expected, queryStringNameValues.ToString());
+ }
}
}
{
XmlTypeMapMemberElement mem = (XmlTypeMapMemberElement) map.XmlTextCollector;
XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) mem.ElementInfo [0];
+ string str = GetStrTempVar ();
+ WriteLine ("string " + str + " = Reader.ReadString();");
if (info.TypeData.Type == typeof (string))
- GenerateSetMemberValue (mem, ob, "ReadString (" + GenerateGetMemberValue (mem, ob, isValueList) + ")", isValueList);
+ GenerateSetMemberValue (mem, ob, str, isValueList);
else {
- WriteLineInd ("{");
- string str = GetStrTempVar ();
- WriteLine ("string " + str + " = Reader.ReadString();");
GenerateSetMemberValue (mem, ob, GenerateGetValueFromXmlString (str, info.TypeData, info.MappedType, info.IsNullable), isValueList);
- WriteLineUni ("}");
}
GenerateEndHook ();
}
XmlTypeMapMemberElement mem = (XmlTypeMapMemberElement) map.XmlTextCollector;
XmlTypeMapElementInfo info = (XmlTypeMapElementInfo) mem.ElementInfo [0];
if (info.TypeData.Type == typeof (string))
- SetMemberValue (mem, ob, ReadString ((string) GetMemberValue (mem, ob, isValueList)), isValueList);
+ SetMemberValue (mem, ob, Reader.ReadString (), isValueList);
else
SetMemberValue (mem, ob, GetValueFromXmlString (Reader.ReadString(), info.TypeData, info.MappedType), isValueList);
}
InitializeContext (url, context, fragment, fragType);
}
- Uri ResolveUri (string url)
- {
- return resolver == null ? null : resolver.ResolveUri (null, url);
- }
-
Stream GetStreamFromUrl (string url, out string absoluteUriString)
{
#if NET_2_1
if (url.Length == 0)
throw new ArgumentException ("url");
#endif
- Uri uri = ResolveUri (url);
+ //
+ // This needs to work even if resolver is explicitly set to null
+ //
+ var res = resolver ?? new XmlUrlResolver ();
+ var uri = res.ResolveUri (null, url);
absoluteUriString = uri != null ? uri.ToString () : String.Empty;
- return resolver == null ? null : resolver.GetEntity (uri, null, typeof (Stream)) as Stream;
+ return res.GetEntity (uri, null, typeof (Stream)) as Stream;
}
#endregion
get { return entity != null ? ReadState.Interactive : source.ReadState; }
}
+#if NET_4_0
+ [MonoTODO]
+ public DtdProcessing DtdProcessing { get; set; }
+#endif
+
#if !NET_4_5
public override XmlReaderSettings Settings {
get { return base.Settings; }
[XmlArrayItem ("Element")]
public List<SimpleClass> Elements;
}
+
+ [Serializable]
+ public sealed class ClassWithDefaultTextNotNull
+ {
+ [XmlText]
+ public string Value;
+
+ public const string DefaultValue = "NotNull";
+
+ public ClassWithDefaultTextNotNull (string v) {
+ Value = v;
+ }
+
+ public ClassWithDefaultTextNotNull () {
+ Value = DefaultValue;
+ }
+ }
}
}
}
}
+
+ private static void TestClassWithDefaultTextNotNullAux (string value, string expected)
+ {
+ var obj = new ClassWithDefaultTextNotNull (value);
+ var ser = new XmlSerializer (typeof (ClassWithDefaultTextNotNull));
+
+ using (var mstream = new MemoryStream ())
+ using (var writer = new XmlTextWriter (mstream, Encoding.ASCII)) {
+ ser.Serialize (writer, obj);
+
+ mstream.Seek (0, SeekOrigin.Begin);
+ using (var reader = new XmlTextReader (mstream)) {
+ var result = (ClassWithDefaultTextNotNull) ser.Deserialize (reader);
+ Assert.AreEqual (expected, result.Value);
+ }
+ }
+ }
+
+ [Test]
+ public void TestClassWithDefaultTextNotNull ()
+ {
+ TestClassWithDefaultTextNotNullAux ("my_text", "my_text");
+ TestClassWithDefaultTextNotNullAux ("", ClassWithDefaultTextNotNull.DefaultValue);
+ TestClassWithDefaultTextNotNullAux (null, ClassWithDefaultTextNotNull.DefaultValue);
+ }
}
// Test generated serialization code.
//
using System;
+using System.Linq;
using System.Xml;
namespace System.Xml.Linq
public override void WriteTo (XmlWriter writer)
{
- writer.WriteComment (value);
+ var v = value.Replace ("--", "- -");
+ v = v.LastOrDefault () == '-' ? v.Substring (0, v.Length - 1) +"D;" : v;
+ writer.WriteComment (v);
}
}
}
System.Xml.Linq/ExtensionsTest.cs
System.Xml.Linq/XAttributeTest.cs
+System.Xml.Linq/XCommentTest.cs
System.Xml.Linq/XDocumentTest.cs
System.Xml.Linq/XElementTest.cs
System.Xml.Linq/XNameTest.cs
--- /dev/null
+//
+// Authors:
+// Atsushi Enomoto
+//
+// Copyright 2014 Xamarin Inc. (http://www.xamarin.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Xml;
+using System.Xml.Linq;
+using System.Linq;
+
+using NUnit.Framework;
+
+namespace MonoTests.System.Xml.Linq
+{
+ [TestFixture]
+ public class XCommentTest
+ {
+ [Test]
+ public void EscapeSequentialDashes ()
+ {
+ XComment c;
+
+ c = new XComment ("<--foo-->");
+ Assert.AreEqual ("<--foo-->", c.Value, "#1");
+ // bug #23318
+ // Unlike XmlWriter.WriteComment(), XComment.ToString() seems to accept "--" in the value.
+ Assert.AreEqual ("<!--<- -foo- ->-->", c.ToString (), "#2");
+ // make sure if it can be read...
+ XmlReader.Create (new StringReader (c.ToString ())).Read ();
+
+ // The last '-' causes some glitch...
+ c = new XComment ("--foo--");
+ Assert.AreEqual ("--foo--", c.Value, "#3");
+ Assert.AreEqual ("<!--- -foo- D;-->", c.ToString (), "#4");
+ XmlReader.Create (new StringReader (c.ToString ())).Read ();
+
+ // What if <!-- appears in the value?
+ c = new XComment ("<!--foo-->");
+ Assert.AreEqual ("<!--foo-->", c.Value, "#5");
+ Assert.AreEqual ("<!--<!- -foo- ->-->", c.ToString (), "#6");
+ XmlReader.Create (new StringReader (c.ToString ())).Read ();
+ }
+ }
+}
int cachedRemoveId = removeId;
int itemsIn = cachedAddId - cachedRemoveId;
+ // Check our transaction id against completed stored one
+ if (isComplete.Value && cachedAddId >= completeId)
+ ThrowCompleteException ();
+
// If needed, we check and wait that the collection isn't full
- if (upperBound != -1 && itemsIn > upperBound) {
+ if (upperBound != -1 && itemsIn >= upperBound) {
if (millisecondsTimeout == 0)
return false;
continue;
}
- // Check our transaction id against completed stored one
- if (isComplete.Value && cachedAddId >= completeId)
- ThrowCompleteException ();
-
// Validate the steps we have been doing until now
if (Interlocked.CompareExchange (ref addId, cachedAddId + 1, cachedAddId) != cachedAddId)
continue;
public static int AddToAny (BlockingCollection<T>[] collections, T item)
{
- CheckArray (collections);
- int index = 0;
- foreach (var coll in collections) {
- try {
- coll.Add (item);
- return index;
- } catch {}
- index++;
- }
- return -1;
+ return AddToAny (collections, item, CancellationToken.None);
}
public static int AddToAny (BlockingCollection<T>[] collections, T item, CancellationToken cancellationToken)
{
CheckArray (collections);
- int index = 0;
- foreach (var coll in collections) {
- try {
- coll.Add (item, cancellationToken);
- return index;
- } catch {}
- index++;
+ WaitHandle[] wait_table = null;
+ while (true) {
+ for (int i = 0; i < collections.Length; ++i) {
+ if (collections [i].TryAdd (item))
+ return i;
+ }
+ cancellationToken.ThrowIfCancellationRequested ();
+ if (wait_table == null) {
+ wait_table = new WaitHandle [collections.Length + 1];
+ for (int i = 0; i < collections.Length; ++i)
+ wait_table [i] = collections [i].mreAdd.WaitHandle;
+ wait_table [collections.Length] = cancellationToken.WaitHandle;
+ }
+ WaitHandle.WaitAny (wait_table);
+ cancellationToken.ThrowIfCancellationRequested ();
}
- return -1;
}
public static int TryAddToAny (BlockingCollection<T>[] collections, T item)
public static int TakeFromAny (BlockingCollection<T>[] collections, out T item)
{
- item = default (T);
- CheckArray (collections);
- WaitHandle[] wait_table = null;
- while (true) {
- for (int i = 0; i < collections.Length; ++i) {
- if (collections [i].TryTake (out item))
- return i;
- }
- if (wait_table == null) {
- wait_table = new WaitHandle [collections.Length];
- for (int i = 0; i < collections.Length; ++i)
- wait_table [i] = collections [i].mreRemove.WaitHandle;
- }
- WaitHandle.WaitAny (wait_table);
- }
+ return TakeFromAny (collections, out item, CancellationToken.None);
}
public static int TakeFromAny (BlockingCollection<T>[] collections, out T item, CancellationToken cancellationToken)
//
using System;
-using System.Collections;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
DateFormat, CultureInfo.InvariantCulture);
DateTime timeWritten = File.GetLastWriteTime (file);
int stringNums = int.Parse (tr.ReadLine ().Substring (20));
- ArrayList replacementTemp = new ArrayList ();
+ var replacementTemp = new List<string> ();
StringBuilder sb = new StringBuilder ();
while (replacementTemp.Count < stringNums) {
char c = (char) tr.Read ();
sb.Append (c);
}
}
- string [] replacementStrings = new string [replacementTemp.Count];
- replacementTemp.CopyTo (replacementStrings, 0);
+ string [] replacementStrings = replacementTemp.ToArray ();
string message = FormatMessage (source, instanceID, replacementStrings);
int eventID = EventLog.GetEventID (instanceID);
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Security.Permissions;
-using System.Collections;
+using System.Collections.Generic;
using System.Security;
using System.Threading;
[MethodImplAttribute(MethodImplOptions.InternalCall)]
private extern static int[] GetProcesses_internal();
- public static Process[] GetProcesses()
+ public static Process[] GetProcesses ()
{
int [] pids = GetProcesses_internal ();
if (pids == null)
return new Process [0];
- ArrayList proclist = new ArrayList (pids.Length);
+ var proclist = new List<Process> (pids.Length);
for (int i = 0; i < pids.Length; i++) {
try {
proclist.Add (GetProcessById (pids [i]));
}
}
- return ((Process []) proclist.ToArray (typeof (Process)));
+ return proclist.ToArray ();
}
[MonoTODO ("There is no support for retrieving process information from a remote machine")]
if (pids == null)
return new Process [0];
- ArrayList proclist = new ArrayList (pids.Length);
+ var proclist = new List<Process> (pids.Length);
for (int i = 0; i < pids.Length; i++) {
try {
Process p = GetProcessById (pids [i]);
}
}
- return ((Process []) proclist.ToArray (typeof (Process)));
+ return proclist.ToArray ();
}
[MonoTODO]
ref proc_info);
} finally {
if (proc_info.Password != IntPtr.Zero)
- Marshal.FreeBSTR (proc_info.Password);
+ Marshal.ZeroFreeBSTR (proc_info.Password);
proc_info.Password = IntPtr.Zero;
}
if (!ret) {
ref proc_info);
} finally {
if (proc_info.Password != IntPtr.Zero)
- Marshal.FreeBSTR (proc_info.Password);
+ Marshal.ZeroFreeBSTR (proc_info.Password);
proc_info.Password = IntPtr.Zero;
}
if (!ret) {
//
using System;
-using System.Collections;
+using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Globalization;
string [] sources = (string []) logKey.GetValue ("Sources");
if (sources != null) {
- ArrayList temp = new ArrayList ();
+ var temp = new List<string> ();
for (int i = 0; i < sources.Length; i++)
if (sources [i] != source)
temp.Add (sources [i]);
- string [] newSources = new string [temp.Count];
- temp.CopyTo (newSources, 0);
+ string [] newSources = temp.ToArray ();
logKey.SetValue ("Sources", newSources);
}
}
using System;
using System.Collections;
+using System.Collections.Generic;
using System.IO;
using System.Threading;
return;
/* Removed files */
- ArrayList removed = null;
+ List<string> removed = null;
foreach (string filename in data.Files.Keys) {
FileData fd = (FileData) data.Files [filename];
if (fd.NotExists) {
if (removed == null)
- removed = new ArrayList ();
+ removed = new List<string> ();
removed.Add (filename);
DispatchEvents (data.FSW, FileAction.Removed, filename);
} catch {
/* Deleted */
if (removed == null)
- removed = new ArrayList ();
+ removed = new List<string> ();
removed.Add (filename);
DispatchEvents (data.FSW, FileAction.Removed, filename);
}
}
+ internal struct CFStreamClientContext {
+ public IntPtr Version;
+ public IntPtr Info;
+ public IntPtr Retain;
+ public IntPtr Release;
+ public IntPtr CopyDescription;
+ }
+
internal class CFString : CFObject
{
string str;
}
}
+ internal class CFRunLoop : CFObject
+ {
+ [DllImport (CFObject.CoreFoundationLibrary)]
+ static extern void CFRunLoopAddSource (IntPtr rl, IntPtr source, IntPtr mode);
+
+ [DllImport (CFObject.CoreFoundationLibrary)]
+ static extern void CFRunLoopRemoveSource (IntPtr rl, IntPtr source, IntPtr mode);
+
+ [DllImport (CFObject.CoreFoundationLibrary)]
+ static extern int CFRunLoopRunInMode (IntPtr mode, double seconds, bool returnAfterSourceHandled);
+
+ [DllImport (CFObject.CoreFoundationLibrary)]
+ static extern IntPtr CFRunLoopGetCurrent ();
+
+ [DllImport (CFObject.CoreFoundationLibrary)]
+ static extern void CFRunLoopStop (IntPtr rl);
+
+ public CFRunLoop (IntPtr handle, bool own): base (handle, own)
+ {
+ }
+
+ public static CFRunLoop CurrentRunLoop {
+ get { return new CFRunLoop (CFRunLoopGetCurrent (), false); }
+ }
+
+ public void AddSource (IntPtr source, CFString mode)
+ {
+ CFRunLoopAddSource (Handle, source, mode.Handle);
+ }
+
+ public void RemoveSource (IntPtr source, CFString mode)
+ {
+ CFRunLoopRemoveSource (Handle, source, mode.Handle);
+ }
+
+ public int RunInMode (CFString mode, double seconds, bool returnAfterSourceHandled)
+ {
+ return CFRunLoopRunInMode (mode.Handle, seconds, returnAfterSourceHandled);
+ }
+
+ public void Stop ()
+ {
+ CFRunLoopStop (Handle);
+ }
+ }
+
internal enum CFProxyType {
None,
AutoConfigurationUrl,
// CFArrayRef CFNetworkCopyProxiesForAutoConfigurationScript (CFStringRef proxyAutoConfigurationScript, CFURLRef targetURL, CFErrorRef* error);
extern static IntPtr CFNetworkCopyProxiesForAutoConfigurationScriptSequential (IntPtr proxyAutoConfigurationScript, IntPtr targetURL, out IntPtr error);
+ [DllImport (CFNetworkLibrary)]
+ extern static IntPtr CFNetworkExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, IntPtr targetURL, CFProxyAutoConfigurationResultCallback cb, ref CFStreamClientContext clientContext);
+
+
class GetProxyData : IDisposable {
public IntPtr script;
public IntPtr targetUri;
return proxies;
}
+
+ delegate void CFProxyAutoConfigurationResultCallback (IntPtr client, IntPtr proxyList, IntPtr error);
+
+ public static CFProxy[] ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetURL)
+ {
+ CFUrl url = CFUrl.Create (targetURL.AbsoluteUri);
+ if (url == null)
+ return null;
+
+ CFProxy[] proxies = null;
+
+ var runLoop = CFRunLoop.CurrentRunLoop;
+
+ // Callback that will be called after executing the configuration script
+ CFProxyAutoConfigurationResultCallback cb = delegate (IntPtr client, IntPtr proxyList, IntPtr error) {
+ if (proxyList != IntPtr.Zero) {
+ var array = new CFArray (proxyList, false);
+ proxies = new CFProxy [array.Count];
+ for (int i = 0; i < proxies.Length; i++) {
+ CFDictionary dict = new CFDictionary (array[i], false);
+ proxies[i] = new CFProxy (dict);
+ }
+ array.Dispose ();
+ }
+ runLoop.Stop ();
+ };
+
+ var clientContext = new CFStreamClientContext ();
+ var loopSource = CFNetworkExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, url.Handle, cb, ref clientContext);
+
+ // Create a private mode
+ var mode = CFString.Create ("Mono.MacProxy");
+
+ runLoop.AddSource (loopSource, mode);
+ runLoop.RunInMode (mode, double.MaxValue, false);
+ runLoop.RemoveSource (loopSource, mode);
+
+ return proxies;
+ }
[DllImport (CFNetworkLibrary)]
// CFArrayRef CFNetworkCopyProxiesForURL (CFURLRef url, CFDictionaryRef proxySettings);
static Uri GetProxyUriFromScript (IntPtr script, Uri targetUri, out NetworkCredential credentials)
{
CFProxy[] proxies = CFNetwork.GetProxiesForAutoConfigurationScript (script, targetUri);
-
+ return SelectProxy (proxies, targetUri, out credentials);
+ }
+
+ static Uri ExecuteProxyAutoConfigurationURL (IntPtr proxyAutoConfigURL, Uri targetUri, out NetworkCredential credentials)
+ {
+ CFProxy[] proxies = CFNetwork.ExecuteProxyAutoConfigurationURL (proxyAutoConfigURL, targetUri);
+ return SelectProxy (proxies, targetUri, out credentials);
+ }
+
+
+ static Uri SelectProxy (CFProxy[] proxies, Uri targetUri, out NetworkCredential credentials)
+ {
if (proxies == null) {
credentials = null;
return targetUri;
proxy = GetProxyUriFromScript (proxies[i].AutoConfigurationJavaScript, targetUri, out credentials);
break;
case CFProxyType.AutoConfigurationUrl:
- // unsupported proxy type (requires fetching script from remote url)
+ proxy = ExecuteProxyAutoConfigurationURL (proxies[i].AutoConfigurationUrl, targetUri, out credentials);
break;
case CFProxyType.HTTPS:
case CFProxyType.HTTP:
if (ch == '%'){
if (!Uri.IsHexEncoding (part, index))
return false;
+ var oldIndex = index;
ch = Uri.HexUnescape (part, ref index);
+ index--;
+ if (ch == '@') {
+ sb.Append (part.Substring (oldIndex, index - oldIndex + 1));
+ continue;
+ }
}
if (Char.IsLetterOrDigit (ch) || IsUnreserved (ch) || IsSubDelim (ch) || ch == ':'){
[ExpectedException (typeof (SecurityException))]
public void LinkDemand_IsDefinedExtension_Deny_Anything ()
{
- MethodInfo mi = mi = typeof (CodeDomProvider).GetMethod ("IsDefinedExtension");
+ MethodInfo mi = typeof (CodeDomProvider).GetMethod ("IsDefinedExtension");
Assert.IsNotNull (mi, "IsDefinedExtension");
Assert.IsFalse ((bool) mi.Invoke (null, new object[1] { String.Empty }), "IsDefinedExtension('')");
// requires full trust (i.e. unrestricted permission set)
[ExpectedException (typeof (SecurityException))]
public void LinkDemand_IsDefinedLanguage_Deny_Anything ()
{
- MethodInfo mi = mi = typeof (CodeDomProvider).GetMethod ("IsDefinedLanguage");
+ MethodInfo mi = typeof (CodeDomProvider).GetMethod ("IsDefinedLanguage");
Assert.IsNotNull (mi, "IsDefinedLanguage");
Assert.IsFalse ((bool) mi.Invoke (null, new object[1] { String.Empty }), "IsDefinedLanguage('')");
// requires full trust (i.e. unrestricted permission set)
t = Task.Factory.StartNew (() => {
try {
return BlockingCollection<string>.TakeFromAny (arr, out res, cts.Token);
- } catch (OperationCanceledException WE_GOT_CANCELED) {
+ } catch (OperationCanceledException) {
res = "canceled";
return -10;
}
Assert.AreEqual (-10, t.Result, "#5");
Assert.AreEqual ("canceled", res, "#6");
}
+
+ [Test, ExpectedException (typeof(OperationCanceledException))]
+ public void BoundedAddLimit ()
+ {
+ const int elNumber = 5;
+
+ var c = new BlockingCollection <int> (elNumber);
+ var token = new CancellationTokenSource (100);
+
+ for (var i = 0; i < elNumber + 1; i++) {
+ c.Add (1, token.Token);
+ }
+ }
+
+ [Test]
+ public void AddAnyCancellable ()
+ {
+ const int elNumber = 5;
+ const int colNumber = 5;
+
+ var cols = new BlockingCollection <int> [colNumber];
+ for (var i = 0; i < colNumber; i++) {
+ cols[i] = new BlockingCollection <int> (elNumber);
+ }
+
+ var token = new CancellationTokenSource (100);
+ for (var i = 0; i < colNumber * elNumber; i++) {
+ BlockingCollection <int>.AddToAny (cols, 1, token.Token);
+ }
+
+ foreach (var col in cols) {
+ Assert.AreEqual (elNumber, col.Count);
+ }
+ }
}
}
#endif
}
}
- class InstanceField
+ public class InstanceField
{
public string Name;
}
public class CharConverterTest
{
private CharConverter converter;
- private string pattern;
[SetUp]
public void SetUp ()
converter = new CharConverter ();
DateTimeFormatInfo info = CultureInfo.CurrentCulture.DateTimeFormat;
- pattern = info.ShortDatePattern + " " + info.ShortTimePattern;
}
[Test]
container.Add (this);
return container;
}
-
- public Container Container {
- get { return container; }
- }
}
class MyContainer : IContainer
}
}
- int bytesRead = -1;
+ public int bytesRead = -1;
#if NET_2_0
// Not technically a 2.0 only test, but I use lambdas, so I need gmcs
try {
var x = p.Handle;
Assert.Fail ("Handle should throw for unstated procs, but returned " + x);
- } catch (InvalidOperationException ex) {
+ } catch (InvalidOperationException) {
}
}
}
static ManualResetEvent reset;
private string message;
- private string uri = "http://www.google.com";
[TestFixtureSetUp]
public void FixtureSetUp ()
try {
client = new UdpClient (port);
break;
- } catch (Exception ex) {
+ } catch (Exception) {
if (i == 5)
throw;
}
[Test]
public void ServerHandshakeReturnCrapStatusCodeTest ()
{
+ // On purpose,
+ #pragma warning disable 4014
HandleHttpRequestAsync ((req, resp) => resp.StatusCode = 418);
+ #pragma warning restore 4014
try {
Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
} catch (AggregateException e) {
[Test]
public void ServerHandshakeReturnWrongUpgradeHeader ()
{
+ #pragma warning disable 4014
HandleHttpRequestAsync ((req, resp) => {
resp.StatusCode = 101;
resp.Headers["Upgrade"] = "gtfo";
});
+ #pragma warning restore 4014
try {
Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
} catch (AggregateException e) {
[Test]
public void ServerHandshakeReturnWrongConnectionHeader ()
{
+ #pragma warning disable 4014
HandleHttpRequestAsync ((req, resp) => {
resp.StatusCode = 101;
resp.Headers["Upgrade"] = "websocket";
// Mono http request doesn't like the forcing, test still valid since the default connection header value is empty
//ForceSetHeader (resp.Headers, "Connection", "Foo");
});
+ #pragma warning restore 4014
try {
Assert.IsTrue (socket.ConnectAsync (new Uri ("ws://localhost:" + Port), CancellationToken.None).Wait (5000));
} catch (AggregateException e) {
static ManualResetEvent reset;
private string message;
private string hostname;
- private IPAddress ip;
[TestFixtureSetUp]
public void FixtureSetUp ()
{
reset = new ManualResetEvent (false);
hostname = Dns.GetHostName ();
- ip = Dns.Resolve (site).AddressList[0];
+ var ip = Dns.Resolve (site).AddressList[0];
}
[TestFixtureTearDown]
namespace MonoTests.System.Net {
[TestFixture]
public class HttpListenerTest {
+
+ int port;
+
+ [SetUp]
+ public void SetUp () {
+ port = new Random ().Next (7777, 8000);
+ }
+
[Test]
public void DefaultProperties ()
{
socket.Listen(1);
}
}
- catch(Exception ex) {
+ catch(Exception) {
//Can be AccessDeniedException(ports 80/443 need root access) or
//SocketException because other application is listening
return false;
[Test]
public void TwoListeners_SameAddress ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener1 = new HttpListener ();
- listener1.Prefixes.Add ("http://127.0.0.1:7777/");
+ listener1.Prefixes.Add ("http://127.0.0.1:" + port + "/");
HttpListener listener2 = new HttpListener ();
- listener2.Prefixes.Add ("http://127.0.0.1:7777/hola/");
+ listener2.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
listener1.Start ();
listener2.Start ();
}
[ExpectedException (typeof (HttpListenerException))]
public void TwoListeners_SameURL ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener1 = new HttpListener ();
- listener1.Prefixes.Add ("http://127.0.0.1:7777/hola/");
+ listener1.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
HttpListener listener2 = new HttpListener ();
- listener2.Prefixes.Add ("http://127.0.0.1:7777/hola/");
+ listener2.Prefixes.Add ("http://127.0.0.1:" + port + "/hola/");
listener1.Start ();
listener2.Start ();
}
[ExpectedException (typeof (HttpListenerException))]
public void MultipleSlashes ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener = new HttpListener ();
- listener.Prefixes.Add ("http://localhost:7777/hola////");
+ listener.Prefixes.Add ("http://localhost:" + port + "/hola////");
// this one throws on Start(), not when adding it.
listener.Start ();
}
[ExpectedException (typeof (HttpListenerException))]
public void PercentSign ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener = new HttpListener ();
- listener.Prefixes.Add ("http://localhost:7777/hola%3E/");
+ listener.Prefixes.Add ("http://localhost:" + port + "/hola%3E/");
// this one throws on Start(), not when adding it.
listener.Start ();
}
[Test]
public void CloseTwice ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener = new HttpListener ();
- listener.Prefixes.Add ("http://localhost:7777/hola/");
+ listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
listener.Start ();
listener.Close ();
listener.Close ();
[Test]
public void StartStopStart ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener = new HttpListener ();
- listener.Prefixes.Add ("http://localhost:7777/hola/");
+ listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
listener.Start ();
listener.Stop ();
listener.Start ();
[Test]
public void StartStopDispose ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
using (HttpListener listener = new HttpListener ()){
- listener.Prefixes.Add ("http://localhost:7777/hola/");
+ listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
listener.Start ();
listener.Stop ();
}
[Test]
public void AbortTwice ()
{
+ if (!CanOpenPort (port))
+ Assert.Ignore ("port");
HttpListener listener = new HttpListener ();
- listener.Prefixes.Add ("http://localhost:7777/hola/");
+ listener.Prefixes.Add ("http://localhost:" + port + "/hola/");
listener.Start ();
listener.Abort ();
listener.Abort ();
private static byte[] cert_a_issuer_raw = new byte[] { 0x30, 0x5F, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x20, 0x30, 0x1E, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x17, 0x52, 0x53, 0x41, 0x20, 0x44, 0x61, 0x74, 0x61, 0x20, 0x53, 0x65, 0x63, 0x75, 0x72, 0x69, 0x74, 0x79, 0x2C, 0x20, 0x49, 0x6E, 0x63, 0x2E, 0x31, 0x2E, 0x30, 0x2C, 0x06, 0x03, 0x55, 0x04, 0x0B, 0x13, 0x25, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x65,
0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x41, 0x75, 0x74, 0x68, 0x6F, 0x72, 0x69, 0x74, 0x79 };
+#if false
private static byte[] cert_b = { 0x30,0x82,0x03,0x04,0x30,0x82,0x02,0xC4,0xA0,0x03,0x02,0x01,0x02,0x02,0x01,0x03,0x30,0x09,0x06,0x07,0x2A,0x86,0x48,0xCE,0x38,0x04,0x03,0x30,0x51,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x18,0x30,0x16,0x06,0x03,0x55,0x04,0x0A,0x13,0x0F,0x55,0x2E,0x53,0x2E,0x20,0x47,0x6F,0x76,0x65,0x72,0x6E,0x6D,0x65,0x6E,0x74,0x31,0x0C,0x30,0x0A,0x06,0x03,0x55,0x04,0x0B,0x13,0x03,0x44,0x6F,0x44,0x31,0x1A,0x30,0x18,0x06,0x03,0x55,0x04,0x03,0x13,0x11,0x41,0x72,0x6D,0x65,0x64,0x20,0x46,0x6F,
0x72,0x63,0x65,0x73,0x20,0x52,0x6F,0x6F,0x74,0x30,0x1E,0x17,0x0D,0x30,0x30,0x31,0x30,0x32,0x35,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x17,0x0D,0x30,0x33,0x30,0x31,0x30,0x31,0x30,0x30,0x30,0x30,0x30,0x30,0x5A,0x30,0x51,0x31,0x0B,0x30,0x09,0x06,0x03,0x55,0x04,0x06,0x13,0x02,0x55,0x53,0x31,0x18,0x30,0x16,0x06,0x03,0x55,0x04,0x0A,0x13,0x0F,0x55,0x2E,0x53,0x2E,0x20,0x47,0x6F,0x76,0x65,0x72,0x6E,0x6D,0x65,0x6E,0x74,0x31,0x0C,0x30,0x0A,0x06,0x03,0x55,0x04,0x0B,0x13,0x03,0x44,0x6F,0x44,0x31,0x1A,0x30,0x18,
0x06,0x03,0x55,0x04,0x03,0x13,0x11,0x41,0x72,0x6D,0x65,0x64,0x20,0x46,0x6F,0x72,0x63,0x65,0x73,0x20,0x52,0x6F,0x6F,0x74,0x30,0x82,0x01,0xB6,0x30,0x82,0x01,0x2B,0x06,0x07,0x2A,0x86,0x48,0xCE,0x38,0x04,0x01,0x30,0x82,0x01,0x1E,0x02,0x81,0x81,0x00,0x90,0x89,0x3E,0x18,0x1B,0xFE,0xA3,0x1D,0x16,0x89,0x00,0xB4,0xD5,0x40,0x82,0x4C,0x2E,0xEC,0x3D,0x66,0x0D,0x0D,0xB9,0x17,0x40,0x6E,0x3A,0x5C,0x03,0x7B,0x1B,0x93,0x28,0x0C,0xEF,0xB9,0x97,0xE3,0xA1,0xEB,0xE2,0xA3,0x7C,0x61,0xDD,0x6F,0xD5,0xAD,0x15,0x69,0x00,
0x54,0x4B,0xC0,0xA8,0x40,0xEF,0x71,0xE8,0x56,0x6B,0xA2,0x29,0xCB,0x1E,0x09,0x7D,0x27,0x39,0x91,0x3B,0x20,0x4F,0x98,0x39,0xE8,0x39,0xCA,0x98,0xC5,0xAF,0x54,0x03,0x81,0x84,0x00,0x02,0x81,0x80,0x54,0xA8,0x88,0xB5,0x8F,0x01,0x56,0xCE,0x18,0x8F,0xA6,0xD6,0x7C,0x29,0x29,0x75,0x45,0xE8,0x31,0xA4,0x07,0x17,0xED,0x1E,0x5D,0xB2,0x7B,0xBB,0xCE,0x3C,0x97,0x67,0x1E,0x88,0x0A,0xFE,0x7D,0x00,0x22,0x27,0x1D,0x66,0xEE,0xF6,0x1B,0xB6,0x95,0x7F,0x5A,0xFF,0x06,0x34,0x02,0x43,0xC3,0x83,0xC4,0x66,0x2C,0xA1,0x05,0x0E,
0x68,0xB3,0xCA,0xDC,0xD3,0xF9,0x0C,0xC0,0x66,0xDF,0x85,0x84,0x4B,0x20,0x5D,0x41,0xAC,0xC0,0xEC,0x37,0x92,0x0E,0x97,0x19,0xBF,0x53,0x35,0x63,0x27,0x18,0x33,0x35,0x42,0x4D,0xF0,0x2D,0x6D,0xA7,0xA4,0x98,0xAA,0x57,0xF3,0xD2,0xB8,0x6E,0x4E,0x8F,0xFF,0xBE,0x6F,0x4E,0x0F,0x0B,0x44,0x24,0xEE,0xDF,0x4C,0x22,0x5B,0x44,0x98,0x94,0xCB,0xB8,0xA3,0x2F,0x30,0x2D,0x30,0x1D,0x06,0x03,0x55,0x1D,0x0E,0x04,0x16,0x04,0x14,0x9D,0x2D,0x73,0xC3,0xB8,0xE3,0x4D,0x29,0x28,0xC3,0x65,0xBE,0xA9,0x98,0xCB,0xD6,0x8A,0x06,0x68,
0x9C,0x30,0x0C,0x06,0x03,0x55,0x1D,0x13,0x04,0x05,0x30,0x03,0x01,0x01,0xFF,0x30,0x09,0x06,0x07,0x2A,0x86,0x48,0xCE,0x38,0x04,0x03,0x03,0x2F,0x00,0x30,0x2C,0x02,0x14,0x5A,0x1B,0x2D,0x08,0x0E,0xE6,0x99,0x38,0x8F,0xB5,0x09,0xC9,0x89,0x79,0x7E,0x01,0x30,0xBD,0xCE,0xF0,0x02,0x14,0x71,0x7B,0x08,0x51,0x97,0xCE,0x4D,0x1F,0x6A,0x84,0x47,0x3A,0xC0,0xBD,0x13,0x89,0x81,0xB9,0x01,0x97 };
+#endif
static public byte[] RFC3280MandatoryAttributeTypesCACert_crt = { 0x30, 0x82, 0x02, 0xC1, 0x30, 0x82, 0x02, 0x2A, 0xA0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x01, 0x60, 0x30, 0x0D, 0x06, 0x09, 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x40, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x31, 0x15, 0x30, 0x13, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x0C, 0x54, 0x72, 0x75, 0x73, 0x74, 0x20, 0x41, 0x6E, 0x63, 0x68, 0x6F, 0x72, 0x30, 0x1E, 0x17, 0x0D,
0x30, 0x31, 0x30, 0x34, 0x31, 0x39, 0x31, 0x34, 0x35, 0x37, 0x32, 0x30, 0x5A, 0x17, 0x0D, 0x31, 0x31, 0x30, 0x34, 0x31, 0x39, 0x31, 0x34, 0x35, 0x37, 0x32, 0x30, 0x5A, 0x30, 0x81, 0x8E, 0x31, 0x0B, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x55, 0x53, 0x31, 0x1A, 0x30, 0x18, 0x06, 0x03, 0x55, 0x04, 0x0A, 0x13, 0x11, 0x54, 0x65, 0x73, 0x74, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x73, 0x31, 0x13, 0x30, 0x11, 0x06, 0x0A, 0x09, 0x92, 0x26, 0x89, 0x93, 0xF2, 0x2C, 0x64, 0x01, 0x19, 0x16, 0x03, 0x67, 0x6F, 0x76, 0x31, 0x20, 0x30, 0x1E, 0x06, 0x0A, 0x09,
static public AsnEncodedData emptyData = new AsnEncodedData (new byte[0]);
private X509Certificate2 x509a;
- private X509Certificate2 x509b;
+ //private X509Certificate2 x509b;
[TestFixtureSetUp]
public void FixtureSetUp ()
{
x509a = new X509Certificate2 (cert_a);
- x509b = new X509Certificate2 (cert_b);
+ //x509b = new X509Certificate2 (cert_b);
}
private void Empty (X500DistinguishedName dn)
[TestFixture]
public class X509ChainTest {
- private X509Certificate2Collection empty;
+ //private X509Certificate2Collection empty;
private X509Certificate2Collection collection;
private X509Certificate2 cert_empty;
cert1 = new X509Certificate2 (X509Certificate2Test.farscape_pfx, "farscape", X509KeyStorageFlags.Exportable);
cert2 = new X509Certificate2 (Encoding.ASCII.GetBytes (X509Certificate2Test.base64_cert));
- empty = new X509Certificate2Collection ();
+ //empty = new X509Certificate2Collection ();
collection = new X509Certificate2Collection ();
collection.Add (cert1);
collection.Add (cert2);
try {
result = Regex.Replace (original, pattern, replacement);
}
- catch (Exception e) {
+ catch (Exception) {
result = "Error.";
}
Assert.AreEqual (expected, result, "rr#: {0} ~ s,{1},{2},",
Match match = Regex.Match (original, pattern);
result = match.Result (replacement);
}
- catch (Exception e) {
+ catch (Exception) {
result = "Error.";
}
Assert.AreEqual (expected, result, "rr#: {0} ~ s,{1},{2},",
public void GetComponents_Ftp2 ()
{
UnitTestUriParser p = new UnitTestUriParser ();
- Assert.AreEqual ("ftp", p._GetComponents (ftp, UriComponents.Scheme, UriFormat.Unescaped), "ftp.Scheme");
- Assert.AreEqual ("username:password", p._GetComponents (ftp, UriComponents.UserInfo, UriFormat.Unescaped), "ftp.UserInfo");
- Assert.AreEqual ("ftp.go-mono.com", p._GetComponents (ftp, UriComponents.Host, UriFormat.Unescaped), "ftp.Host");
- Assert.AreEqual (String.Empty, p._GetComponents (ftp, UriComponents.Port, UriFormat.Unescaped), "ftp.Port");
- Assert.AreEqual ("with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.Path, UriFormat.Unescaped), "ftp.Path");
- Assert.AreEqual ("with%20some%20spaces/mono.tgz", p._GetComponents (ftp, UriComponents.Path, UriFormat.UriEscaped), "ftp.Path-UriEscaped");
- Assert.AreEqual ("with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.Path, UriFormat.SafeUnescaped), "ftp.Path-SafeUnescaped");
- Assert.AreEqual (String.Empty, p._GetComponents (ftp, UriComponents.Query, UriFormat.Unescaped), "ftp.Query");
- Assert.AreEqual (String.Empty, p._GetComponents (ftp, UriComponents.Fragment, UriFormat.Unescaped), "ftp.Fragment");
- Assert.AreEqual ("21", p._GetComponents (ftp, UriComponents.StrongPort, UriFormat.Unescaped), "ftp.StrongPort");
- Assert.AreEqual (String.Empty, p._GetComponents (ftp, UriComponents.KeepDelimiter, UriFormat.Unescaped), "http.KeepDelimiter");
- Assert.AreEqual ("ftp.go-mono.com:21", p._GetComponents (ftp, UriComponents.HostAndPort, UriFormat.Unescaped), "http.HostAndPort");
- Assert.AreEqual ("username:password@ftp.go-mono.com:21", p._GetComponents (ftp, UriComponents.StrongAuthority, UriFormat.Unescaped), "http.StrongAuthority");
- Assert.AreEqual ("ftp://username:password@ftp.go-mono.com/with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.AbsoluteUri, UriFormat.Unescaped), "http.AbsoluteUri");
- Assert.AreEqual ("/with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.PathAndQuery, UriFormat.Unescaped), "http.PathAndQuery");
- Assert.AreEqual ("ftp://ftp.go-mono.com/with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.HttpRequestUrl, UriFormat.Unescaped), "http.HttpRequestUrl");
- Assert.AreEqual ("ftp://ftp.go-mono.com", p._GetComponents (ftp, UriComponents.SchemeAndServer, UriFormat.Unescaped), "http.SchemeAndServer");
- Assert.AreEqual ("ftp://username:password@ftp.go-mono.com/with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.SerializationInfoString, UriFormat.Unescaped), "http.SerializationInfoString");
+ Assert.AreEqual ("ftp", p._GetComponents (ftp2, UriComponents.Scheme, UriFormat.Unescaped), "ftp.Scheme");
+ Assert.AreEqual ("username:password", p._GetComponents (ftp2, UriComponents.UserInfo, UriFormat.Unescaped), "ftp.UserInfo");
+ Assert.AreEqual ("ftp.go-mono.com", p._GetComponents (ftp2, UriComponents.Host, UriFormat.Unescaped), "ftp.Host");
+ Assert.AreEqual (String.Empty, p._GetComponents (ftp2, UriComponents.Port, UriFormat.Unescaped), "ftp.Port");
+ Assert.AreEqual ("with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.Path, UriFormat.Unescaped), "ftp.Path");
+ Assert.AreEqual ("with%20some%20spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.Path, UriFormat.UriEscaped), "ftp.Path-UriEscaped");
+ Assert.AreEqual ("with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.Path, UriFormat.SafeUnescaped), "ftp.Path-SafeUnescaped");
+ Assert.AreEqual (String.Empty, p._GetComponents (ftp2, UriComponents.Query, UriFormat.Unescaped), "ftp.Query");
+ Assert.AreEqual (String.Empty, p._GetComponents (ftp2, UriComponents.Fragment, UriFormat.Unescaped), "ftp.Fragment");
+ Assert.AreEqual ("21", p._GetComponents (ftp2, UriComponents.StrongPort, UriFormat.Unescaped), "ftp.StrongPort");
+ Assert.AreEqual (String.Empty, p._GetComponents (ftp2, UriComponents.KeepDelimiter, UriFormat.Unescaped), "http.KeepDelimiter");
+ Assert.AreEqual ("ftp.go-mono.com:21", p._GetComponents (ftp2, UriComponents.HostAndPort, UriFormat.Unescaped), "http.HostAndPort");
+ Assert.AreEqual ("username:password@ftp.go-mono.com:21", p._GetComponents (ftp2, UriComponents.StrongAuthority, UriFormat.Unescaped), "http.StrongAuthority");
+ Assert.AreEqual ("ftp://username:password@ftp.go-mono.com/with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.AbsoluteUri, UriFormat.Unescaped), "http.AbsoluteUri");
+ Assert.AreEqual ("/with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.PathAndQuery, UriFormat.Unescaped), "http.PathAndQuery");
+ Assert.AreEqual ("ftp://ftp.go-mono.com/with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.HttpRequestUrl, UriFormat.Unescaped), "http.HttpRequestUrl");
+ Assert.AreEqual ("ftp://ftp.go-mono.com", p._GetComponents (ftp2, UriComponents.SchemeAndServer, UriFormat.Unescaped), "http.SchemeAndServer");
+ Assert.AreEqual ("ftp://username:password@ftp.go-mono.com/with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.SerializationInfoString, UriFormat.Unescaped), "http.SerializationInfoString");
Assert.AreSame (p, p._OnNewUri (), "OnNewUri");
// strange mixup
- Assert.AreEqual ("ftp://username:password@", p._GetComponents (ftp, UriComponents.Scheme | UriComponents.UserInfo, UriFormat.Unescaped), "ftp.Scheme+UserInfo");
- Assert.AreEqual (":21/with some spaces/mono.tgz", p._GetComponents (ftp, UriComponents.Path | UriComponents.StrongPort, UriFormat.Unescaped), "ftp.Path+StrongPort");
+ Assert.AreEqual ("ftp://username:password@", p._GetComponents (ftp2, UriComponents.Scheme | UriComponents.UserInfo, UriFormat.Unescaped), "ftp.Scheme+UserInfo");
+ Assert.AreEqual (":21/with some spaces/mono.tgz", p._GetComponents (ftp2, UriComponents.Path | UriComponents.StrongPort, UriFormat.Unescaped), "ftp.Path+StrongPort");
}
// Test case for Xamarin#17665
var uri = new Uri ("/relative/path", UriKind.Relative);
uri.GetComponents (UriComponents.SerializationInfoString | UriComponents.Host, UriFormat.UriEscaped);
}
+
+ [Test]
+ public void UserInfo_EscapedLetter ()
+ {
+ var uri = new Uri ("https://first%61second@host");
+ Assert.AreEqual ("firstasecond", uri.UserInfo);
+ }
+
+ [Test]
+ public void UserInfo_EscapedAt ()
+ {
+ var userinfo = "first%40second";
+ var uri = new Uri ("https://" + userinfo + "@host");
+ Assert.AreEqual (userinfo, uri.UserInfo);
+ }
}
// Tests non default IriParsing
private bool originalIriParsing;
[TestFixtureSetUp]
- public void GetReady ()
+ public void GetReady2 ()
{
isWin32 = (Path.DirectorySeparatorChar == '\\');
protected override void Dispose (bool disposing)
{
if (!_disposed) {
+ _disposed = true;
base.Dispose (disposing);
}
}
uint leftOverBits = 0;
uint leftOverCount = 0;
DecoderFallbackBuffer buf = null;
- byte [] bufferArg = null;
return InternalGetChars (bytes, byteIndex, byteCount, chars,
charIndex, ref leftOverBits, ref leftOverCount, DecoderFallback, ref buf, true);
}
if (charCount < 0) {
throw new ArgumentOutOfRangeException ("charCount", _("ArgRange_NonNegative"));
}
- return charCount * 2;
+ return charCount * 2 + 2;
}
// Get the maximum number of characters needed to decode a
throw new ArgumentOutOfRangeException
("byteCount", _("ArgRange_NonNegative"));
}
- return byteCount / 2;
+ return (byteCount + 1) / 2 + 1;
}
// Get a Unicode-specific decoder that is attached to this instance.
#region Cancel and Wait related method
- internal void CancelReal ()
+ internal void CancelReal (bool notifyParent = false)
{
Status = TaskStatus.Canceled;
wait_handle.Set ();
ProcessCompleteDelegates ();
+
+ if (notifyParent && parent != null && NotifyParentOnFinish ())
+ parent = null;
}
void HandleGenericException (Exception e)
public void Execute ()
{
if (!ContinuationStatusCheck (continuationOptions)) {
- task.CancelReal ();
+ task.CancelReal (notifyParent : true);
task.Dispose ();
return;
}
[method: SecurityPermission (SecurityAction.LinkDemand, ControlAppDomain = true)]
public event UnhandledExceptionEventHandler UnhandledException;
+#if NET_4_5
+ public event EventHandler<FirstChanceExceptionEventArgs> FirstChanceException;
+#endif
+
#if NET_4_0
[MonoTODO]
public bool IsHomogenous {
namespace System {
[ComVisible (true)]
- public static class Environment {
+ public static partial class Environment {
/*
* This is the version number of the corlib-runtime interface. When
}
#endif
- [MethodImplAttribute (MethodImplOptions.InternalCall)]
- private extern static string GetWindowsFolderPath (int folder);
-
/// <summary>
/// Returns the fully qualified path of the
/// folder specified by the "folder" parameter
{
return GetFolderPath (folder, SpecialFolderOption.None);
}
+
+// for monotouch, not monotouch_runtime
+#if !(MONOTOUCH && FULL_AOT_RUNTIME)
+ [MethodImplAttribute (MethodImplOptions.InternalCall)]
+ private extern static string GetWindowsFolderPath (int folder);
+
#if NET_4_0
public
#endif
// personal == ~
case SpecialFolder.Personal:
-#if MONOTOUCH
- return Path.Combine (home, "Documents");
-#else
return home;
-#endif
+
// use FDO's CONFIG_HOME. This data will be synced across a network like the windows counterpart.
case SpecialFolder.ApplicationData:
-#if MONOTOUCH
- {
- string dir = Path.Combine (Path.Combine (home, "Documents"), ".config");
- if (option == SpecialFolderOption.Create){
- if (!Directory.Exists (dir))
- Directory.CreateDirectory (dir);
- }
- return dir;
- }
-#else
return config;
-#endif
+
//use FDO's DATA_HOME. This is *NOT* synced
case SpecialFolder.LocalApplicationData:
-#if MONOTOUCH
- {
- string dir = Path.Combine (home, "Documents");
- if (!Directory.Exists (dir))
- Directory.CreateDirectory (dir);
-
- return dir;
- }
-#else
return data;
-#endif
case SpecialFolder.Desktop:
case SpecialFolder.DesktopDirectory:
return "/usr/share";
default:
throw new ArgumentException ("Invalid SpecialFolder");
- }
- }
+ }
+ }
+#endif
[EnvironmentPermission (SecurityAction.Demand, Unrestricted=true)]
}
// private methods
-#if MOBILE
+#if (MONOTOUCH || MONODROID || XAMMAC)
internal const bool IsRunningOnWindows = false;
#else
internal static bool IsRunningOnWindows {
--- /dev/null
+//
+// System.FirstChangeExceptionEventArgs.cs
+//
+// Copyright 2014 Xamarin Inc (http://www.xamarin.com).
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#if NET_4_5
+using System;
+
+public class FirstChanceExceptionEventArgs : EventArgs
+{
+ Exception exception;
+
+ public FirstChanceExceptionEventArgs (Exception exception) {
+ this.exception = exception;
+ }
+
+ public Exception Exception {
+ get {
+ return exception;
+ }
+ }
+}
+
+#endif
\ No newline at end of file
}
}
- internal unsafe void InternalSetLength (int newLength)
- {
- if (newLength > length)
- throw new ArgumentOutOfRangeException ("newLength", "newLength as to be <= length");
-
- // zero terminate, we can pass string objects directly via pinvoke
- // we also zero the rest of the string, since the new GC needs to be
- // able to handle the changing size (it will skip the 0 bytes).
- fixed (char * pStr = &start_char) {
- char *p = pStr + newLength;
- char *end = pStr + length;
- while (p < end) {
- p [0] = '\0';
- p++;
- }
- }
- length = newLength;
- }
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal extern void InternalSetLength (int newLength);
[ReliabilityContractAttribute (Consistency.WillNotCorruptState, Cer.MayFail)]
// When modifying it, GetCaseInsensitiveHashCode() should be modified as well.
element = parser.GetNextElement ();
switch (element.Type) {
- case FormatElementType.Days:
- value = Math.Abs (Days);
- break;
- case FormatElementType.Hours:
- value = Math.Abs (Hours);
- break;
- case FormatElementType.Minutes:
- value = Math.Abs (Minutes);
- break;
- case FormatElementType.Seconds:
- value = Math.Abs (Seconds);
- break;
- case FormatElementType.Ticks:
- case FormatElementType.TicksUppercase:
- value = Math.Abs (Milliseconds);
- if (value == 0) {
- if (element.Type == FormatElementType.Ticks)
- break;
-
- continue;
- }
+ case FormatElementType.Days:
+ value = Math.Abs (Days);
+ break;
+ case FormatElementType.Hours:
+ value = Math.Abs (Hours);
+ break;
+ case FormatElementType.Minutes:
+ value = Math.Abs (Minutes);
+ break;
+ case FormatElementType.Seconds:
+ value = Math.Abs (Seconds);
+ break;
+ case FormatElementType.Ticks:
+ case FormatElementType.TicksUppercase:
+ //
+ // TODO: Unify with datetime ticks formatting
+ //
+ value = (int)(_ticks % TicksPerSecond);
+ if (value == 0) {
+ if (element.Type == FormatElementType.Ticks)
+ break;
- int threshold = (int)Math.Pow (10, element.IntValue);
- while (value >= threshold)
- value /= 10;
- sb.Append (value.ToString ());
continue;
- case FormatElementType.EscapedChar:
- sb.Append (element.CharValue);
+ }
+
+ int total_length = element.IntValue;
+ const int max_length = 7;
+ int digits = max_length;
+ for (var dv = (int)Math.Pow (10, max_length - 1); dv > value; dv /= 10, --digits)
+ ;
+
+ //
+ // Skip only leading zeros in F format
+ //
+ if (element.Type == FormatElementType.TicksUppercase && max_length - digits >= total_length)
continue;
- case FormatElementType.Literal:
- sb.Append (element.StringValue);
+
+ //
+ // Add leading zeros
+ //
+ int leading = 0;
+ for (; leading < total_length && leading < max_length - digits; ++leading) {
+ sb.Append ("0");
+ }
+
+ if (total_length == leading)
continue;
- default:
- throw new FormatException ("The format is not recognized.");
+
+ //
+ // Remove trailing zeros
+ //
+ if (element.Type == FormatElementType.TicksUppercase) {
+ while (value % 10 == 0)
+ value /= 10;
+ }
+
+ var max_value = (int)Math.Pow (10, total_length - leading);
+ while (value >= max_value)
+ value /= 10;
+
+ sb.Append (value.ToString (CultureInfo.InvariantCulture));
+ continue;
+ case FormatElementType.EscapedChar:
+ sb.Append (element.CharValue);
+ continue;
+ case FormatElementType.Literal:
+ sb.Append (element.StringValue);
+ continue;
+ default:
+ throw new FormatException ("The format is not recognized.");
}
sb.Append (value.ToString ("D" + element.IntValue.ToString ()));
m1.Invoke(null, new object[] { 5 });
}
+ // Disabl known warning, the Field is never used directly from C#
+ #pragma warning disable 414
class Host {
static string Field = "foo";
}
-
+ #pragma warning restore 414
[Test]
[Category ("NotDotNet")] // https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=297416
public void TestOwnerMemberAccess ()
{
AssemblyBuilder assembly;
ModuleBuilder module;
- int typeCount;
static string ASSEMBLY_NAME = "MonoTests.System.Reflection.Emit.TypeBuilderTest";
[SetUp]
assemblyName, mode, Path.GetTempPath ());
module = assembly.DefineDynamicModule ("module1");
- typeCount = 0;
}
[Test]
}
public class Tuple <A,B> {
- A a;
- B b;
+ public A a;
+ public B b;
}
private AssemblyBuilder assembly;
public class AssemblyNameCas {
private MonoTests.System.Reflection.AssemblyNameTest ant;
- private AssemblyName main;
[TestFixtureSetUp]
public void FixtureSetUp ()
{
}
+ // Disable this warning, as the purpose of this struct is to poke at the internal via reflection
+ #pragma warning disable 649
class FieldInvokeMatrix
{
public Byte field_Byte;
public Int64Enum field_Int64Enum;
public UInt64Enum field_UInt64Enum;
}
+ #pragma warning restore 649
public enum ByteEnum : byte
{
Assert.AreEqual (typeof (Marshal1), Type.GetType (attr.MarshalType), "#I4");
}
+ // Disable "field not used warning", this is intended.
+#pragma warning disable 649
class Foo {
public static int static_field;
public int field;
}
+#pragma warning restore 649
[ExpectedException (typeof (ArgumentException))]
public void GetValueWrongObject ()
}
+ // We do not refernece the field, that is expected
+#pragma warning disable 169
// Helper classes
class RefOnlyFieldClass
{
// Helper property
static int RefOnlyField;
}
-
+#pragma warning restore 169
+
class NonPublicFieldClass
{
protected int protectedField;
+
+ public void Dummy ()
+ {
+ protectedField = 1;
+ }
}
public class FieldInfoTest<T>
#endif
namespace A.B.C {
+ // Disable expected warning
+#pragma warning disable 169
public struct MethodInfoTestStruct {
int p;
}
+#pragma warning restore 169
}
namespace MonoTests.System.Reflection
{
public struct SimpleStruct
{
- int a;
+ public int a;
}
public static unsafe SimpleStruct* PtrFunc2 (SimpleStruct* a, A.B.C.MethodInfoTestStruct *b)
}
public class Bar<T> {
- public class Foo<T> {}
+ public class Foo<U> {}
}
[Test]
using System.Reflection;
using NUnit.Framework;
+// Various fields in this class are not used directly by the C# code, they are only here to be reflected upon
+#pragma warning disable 649
namespace MonoTests.System.Reflection
{
[TestFixture]
static int reachable = 0;
public class FinalizableLink {
+ // The sole purpose of this object is to keep a reference to another object, so it is fine to not use it.
+ #pragma warning disable 414
object obj;
- ConditionalWeakTable <object,object> cwt;
int id;
+ #pragma warning restore 414
+ ConditionalWeakTable <object,object> cwt;
public FinalizableLink (int id, object obj, ConditionalWeakTable <object,object> cwt) {
this.id = id;
RuntimeHelpers.RunClassConstructor (rth);
}
+ // Expected the handle here is that way, because we are going to test for an ArgumentException being thrown
+#pragma warning disable 649
static RuntimeTypeHandle handle;
-
+#pragma warning restore 649
[Test]
[ExpectedException (typeof (ArgumentException))]
public void RunClassConstructor_Uninitialized ()
}
static RuntimeFieldHandle rfh = typeof (Fielder).GetField ("array").FieldHandle;
+
+ // Disable expected warning: the point of the test is to validate that an exception is thrown for something with a null (the default value in this case)
+ #pragma warning disable 649
static RuntimeFieldHandle static_rfh;
+ #pragma warning restore 649
[Test]
[ExpectedException (typeof (ArgumentNullException))]
Interlocked.Increment (ref ic);
return false;
}
+
+ public override string ToString ()
+ {
+ return "Scheduler-" + name;
+ }
}
class SingleThreadSynchronizationContext : SynchronizationContext
[TestFixture]
public class GCHandleTest
{
+ // Expected warning, the tests that reference this handle are testing for the default values of the object
+ #pragma warning disable 649
static GCHandle handle;
-
+ #pragma warning restore 649
+
[Test]
public void DefaultZeroValue_Allocated ()
{
public int field;
}
- class ClsNoLayout {
+ public class ClsNoLayout {
public int field;
}
Marshal.FreeHGlobal (ptr);
}
- struct Foo {
- int a;
- static int b;
- long c;
- static char d;
- int e;
+ public struct Foo {
+ public int a;
+ public static int b;
+ public long c;
+ public static char d;
+ public int e;
}
[Test]
Assert.AreEqual (_char, obj._char, context + "._char");
Assert.AreEqual (_dateTime, obj._dateTime, context + "._dateTime");
Assert.AreEqual (_decimal, obj._decimal, context + "._decimal");
+ Assert.AreEqual (_double, obj._double, context + "._double");
+ Assert.AreEqual (_short, obj._short, context = "._short");
Assert.AreEqual (_int, obj._int, context + "._int");
Assert.AreEqual (_long, obj._long, context + "._long");
Assert.AreEqual (_sbyte, obj._sbyte, context + "._sbyte");
Stream readStream;
Stream writeStream;
ICryptoTransform encryptor;
- ICryptoTransform decryptor;
CryptoStream cs;
SymmetricAlgorithm aes;
writeStream = new MemoryStream (new byte [0], true);
aes = SymmetricAlgorithm.Create ();
encryptor = aes.CreateEncryptor ();
- decryptor = aes.CreateEncryptor ();
}
}
public class SelectableHmacSha384: HMAC {
+ // legacy parameter:
+ // http://blogs.msdn.com/shawnfa/archive/2007/01/31/please-do-not-use-the-net-2-0-hmacsha512-and-hmacsha384-classes.aspx
+
public SelectableHmacSha384 (byte[] key, bool legacy)
{
HashName = "SHA384";
public class HMACSHA384Test : KeyedHashAlgorithmTest {
protected HMACSHA384 algo;
- private bool legacy;
[SetUp]
public override void SetUp ()
algo = new HMACSHA384 ();
algo.Key = new byte [8];
hash = algo;
- // http://blogs.msdn.com/shawnfa/archive/2007/01/31/please-do-not-use-the-net-2-0-hmacsha512-and-hmacsha384-classes.aspx
- legacy = (new HS384 ().BlockSize == 64);
}
// the hash algorithm only exists as a managed implementation
public class SelectableHmacSha512: HMAC {
+ // Legacy parameter explanation:
+ // http://blogs.msdn.com/shawnfa/archive/2007/01/31/please-do-not-use-the-net-2-0-hmacsha512-and-hmacsha384-classes.aspx
+
public SelectableHmacSha512 (byte[] key, bool legacy)
{
HashName = "SHA512";
public class HMACSHA512Test : KeyedHashAlgorithmTest {
protected HMACSHA512 algo;
- private bool legacy;
[SetUp]
public override void SetUp ()
algo = new HMACSHA512 ();
algo.Key = new byte [8];
hash = algo;
- // http://blogs.msdn.com/shawnfa/archive/2007/01/31/please-do-not-use-the-net-2-0-hmacsha512-and-hmacsha384-classes.aspx
- legacy = (new HS512 ().BlockSize == 64);
}
// the hash algorithm only exists as a managed implementation
private static string className = "System.Security.Permissions.RegistryPermission, ";
private static string keyCurrentUser = @"HKEY_CURRENT_USER\Software\Novell iFolder\spouliot\Home";
- private static string keyCurrentUserSubset = @"HKEY_CURRENT_USER\Software\Novell iFolder\";
private static string keyLocalMachine = @"HKEY_LOCAL_MACHINE\SOFTWARE\Novell\Novell iFolder\1.00.000";
private static string keyLocalMachineSubset = @"HKEY_LOCAL_MACHINE\SOFTWARE\Novell\";
"*",
};
- // accepted as Url but fails to work (as expected) in some methods
- static string[] SemiBadUrls = {
- "www.mono-project.com:80",
- String.Empty,
- };
-
[Test]
public void PermissionState_None ()
{
}
[Test]
-#if NET_2_0
- [Category ("NotWorking")]
-#endif
public void TestMaxCharCount()
{
UnicodeEncoding UnicodeEnc = new UnicodeEncoding ();
-#if NET_2_0
- // where is this extra 1 coming from?
Assert.AreEqual (26, UnicodeEnc.GetMaxCharCount(50), "UTF #1");
Assert.AreEqual (27, UnicodeEnc.GetMaxCharCount(51), "UTF #2");
-#else
- Assert.AreEqual (25, UnicodeEnc.GetMaxCharCount(50), "UTF #1");
-#endif
}
[Test]
-#if NET_2_0
- [Category ("NotWorking")]
-#endif
public void TestMaxByteCount()
{
UnicodeEncoding UnicodeEnc = new UnicodeEncoding ();
-#if NET_2_0
- // is this extra 2 BOM?
Assert.AreEqual (102, UnicodeEnc.GetMaxByteCount(50), "UTF #1");
-#else
- Assert.AreEqual (100, UnicodeEnc.GetMaxByteCount(50), "UTF #1");
-#endif
}
[Test]
Assert.AreEqual (2, s.Length, "Length");
Assert.AreEqual (65533, (int) s [1], "1");
}
+
+ [Test]
+ public void GetMaxByteCountIncludesBOM ()
+ {
+ Assert.AreEqual (2, Encoding.Unicode.GetMaxByteCount (0), "#1");
+ Assert.AreEqual (4, Encoding.Unicode.GetMaxByteCount (1), "#2");
+ Assert.AreEqual (6, Encoding.Unicode.GetMaxByteCount (2), "#3");
+ Assert.AreEqual (10, Encoding.Unicode.GetMaxByteCount (4), "#4");
+ Assert.AreEqual (20, Encoding.Unicode.GetMaxByteCount (9), "#5");
+ Assert.AreEqual (22, Encoding.Unicode.GetMaxByteCount (10), "#6");
+ }
+
+ [Test]
+ public void GetMaxCharCountRoundsCorrectly ()
+ {
+ Assert.AreEqual (1, Encoding.Unicode.GetMaxCharCount (0), "#1");
+ Assert.AreEqual (2, Encoding.Unicode.GetMaxCharCount (1), "#2");
+ Assert.AreEqual (2, Encoding.Unicode.GetMaxCharCount (2), "#3");
+ Assert.AreEqual (3, Encoding.Unicode.GetMaxCharCount (4), "#4");
+ Assert.AreEqual (6, Encoding.Unicode.GetMaxCharCount (9), "#5");
+ Assert.AreEqual (6, Encoding.Unicode.GetMaxCharCount (10), "#6");
+ }
}
}
public void ContinueWithChildren ()
{
ParallelTestHelper.Repeat (delegate {
- bool result = false;
+ bool result = false;
- var t = Task.Factory.StartNew (() => Task.Factory.StartNew (() => {}, TaskCreationOptions.AttachedToParent));
+ var t = Task.Factory.StartNew (() => Task.Factory.StartNew (() => {}, TaskCreationOptions.AttachedToParent));
var mre = new ManualResetEvent (false);
- t.ContinueWith (l => {
+ t.ContinueWith (l => {
result = true;
mre.Set ();
});
Assert.IsTrue (mre.WaitOne (1000), "#1");
- Assert.IsTrue (result, "#2");
+ Assert.IsTrue (result, "#2");
}, 2);
}
};
var inner = new ApplicationException ();
Thread t = new Thread (delegate () {
- Task.Factory.StartNew (() => { Console.WriteLine ("HIT!"); throw inner; });
+ Task.Factory.StartNew (() => { throw inner; });
});
t.Start ();
t.Join ();
var t = new Task (() => {
new Task (() => { r1 = true; }, TaskCreationOptions.AttachedToParent).RunSynchronously ();
Task.Factory.StartNew (() => { Thread.Sleep (100); r2 = true; }, TaskCreationOptions.AttachedToParent);
- });
+ });
t.RunSynchronously ();
Assert.IsTrue (r1);
}
}
+ [Test]
+ public void ChildTaskWithUnscheduledContinuationAttachedToParent ()
+ {
+ Task inner = null;
+ var child = Task.Factory.StartNew (() => {
+ inner = Task.Run (() => {
+ throw new ApplicationException ();
+ }).ContinueWith (task => { }, TaskContinuationOptions.AttachedToParent | TaskContinuationOptions.NotOnFaulted | TaskContinuationOptions.ExecuteSynchronously);
+ });
+
+ int counter = 0;
+ var t = child.ContinueWith (t2 => ++counter, TaskContinuationOptions.ExecuteSynchronously);
+ Assert.IsTrue (t.Wait (5000), "#1");
+ Assert.AreEqual (1, counter, "#2");
+ Assert.AreEqual (TaskStatus.RanToCompletion, child.Status, "#3");
+ Assert.AreEqual (TaskStatus.Canceled, inner.Status, "#4");
+ }
+
[Test]
[Category("NotWorking")]
public void TaskContinuationChainLeak()
[Category("MobileNotWorking")] // Abort #10240
public class ThreadTest
{
- TimeSpan Infinite = new TimeSpan (-10000); // -10000 ticks == -1 ms
+ //TimeSpan Infinite = new TimeSpan (-10000); // -10000 ticks == -1 ms
TimeSpan SmallNegative = new TimeSpan (-2); // between 0 and -1.0 (infinite) ms
TimeSpan Negative = new TimeSpan (-20000); // really negative
- TimeSpan MaxValue = TimeSpan.FromMilliseconds ((long) Int32.MaxValue);
+ //TimeSpan MaxValue = TimeSpan.FromMilliseconds ((long) Int32.MaxValue);
TimeSpan TooLarge = TimeSpan.FromMilliseconds ((long) Int32.MaxValue + 1);
static bool is_win32;
if (!rename_finished)
Monitor.Wait (monitor);
}
- Assert.IsFalse (rename_failed);
+ Assert.IsTrue (!rename_failed);
}
void Rename_callback (object o) {
Thread.CurrentThread.Name = "a";
try {
Thread.CurrentThread.Name = "b";
- } catch (Exception) {
+ //Console.WriteLine ("Thread name is: {0}", Thread.CurrentThread.Name);
+ } catch (Exception e) {
+ //Console.Error.WriteLine (e);
rename_failed = true;
}
object monitor = o;
Assert.AreEqual (this, arg, "obj");
}
- bool set_name_failed;
-
[Test]
public void SetNameTpThread () {
ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadProc));
}
#endif
+
public class StuffToPick
{
public StuffToPick () {}
public event Action Event;
public int Field;
public void GenericMethod<T> () {}
+
+ public void Dummy ()
+ {
+ Event += delegate {};
+ }
}
public class StuffToPick<T>
public int Property { get; set; }
public event Action Event;
public int Field;
- public void GenericMethod<T> () {}
+ public void GenericMethod<U> () {}
+
+ public void Dummy ()
+ {
+ Event += delegate {};
+ }
}
static void TestSerialization (CrossDomainTester tester, object o)
private const string MyString3 = "127";
private string[] Formats1 = {"c", "d", "e", "f", "g", "n", "p", "x" };
private string[] Formats2 = {"c5", "d5", "e5", "f5", "g5", "n5", "p5", "x5" };
+#if false
+ // These are not currently being tested against, due to the locale-specific nature of the test, we need a different way of doing this
private string[] Results1 = {"("+NumberFormatInfo.CurrentInfo.CurrencySymbol+"128.00)",
"-128", "-1.280000e+002", "-128.00",
"-128", "-128.00", "-12,800.00 %", "80"};
private string[] Results2 = {NumberFormatInfo.CurrentInfo.CurrencySymbol+"127.00000",
"00127", "1.27000e+002", "127.00000",
"127", "127.00000", "12,700.00000 %", "0007f"};
+#endif
private string[] ResultsNfi1 = {"("+NumberFormatInfo.InvariantInfo.CurrencySymbol+"128.00)",
"-128", "-1.280000e+002", "-128.00",
"-128", "-128.00", "-12,800.00 %", "80"};
Assert.IsTrue(String.Compare(MyString2, MySByte2.ToString()) == 0, "MyString2, MySByte2.ToString()");
Assert.IsTrue(String.Compare(MyString3, MySByte3.ToString()) == 0, "MyString3, MySByte3.ToString()");
//test ToString(string format)
+
+#if false
/*
- TODO: These tests depend on the culture of the system running the test.
- So, this needs to be tested in a different way.
+ * TODO: These tests depend on the culture of the system running the test.
+ * So, this needs to be tested in a different way.
+ /
for (int i=0; i < Formats1.Length; i++) {
Assert.IsTrue("i="+i+", Results1[i]="+Results1[i]+", MySByte2.ToString(Formats1[i])="+MySByte2.ToString(Formats1[i]), String.Compare(Results1[i], MySByte2.ToString(Formats1[i])) == 0);
Assert.IsTrue(String.Compare(Results2[i], MySByte3.ToString(Formats2[i])) == 0, "Results2[i], MySByte3.ToString(Formats2[i])");
}
- */
+#endif
//test ToString(string format, IFormatProvider provider);
for (int i=0; i < Formats1.Length; i++) {
Assert.IsTrue(String.Compare(ResultsNfi1[i], MySByte2.ToString(Formats1[i], Nfi)) == 0, "i="+i+", ResultsNfi1[i]="+ResultsNfi1[i]+", MySByte2.ToString(Formats1[i]="+Formats1[i]+"): Expected "+ResultsNfi1[i]+" but got "+MySByte2.ToString(Formats1[i], Nfi));
ts = new TimeSpan (123456789);
Assert.AreEqual ("12.3", ts.ToString ("s\\.f"), "#F0");
Assert.AreEqual ("12.3", ts.ToString ("s\\.F"), "#F1");
+ Assert.AreEqual ("12.3456789", ts.ToString ("s\\.fffffff"), "#F2");
+ Assert.AreEqual ("12.345678", ts.ToString ("s\\.ffffff"), "#F3");
+
+ ts = new TimeSpan (1234);
+ Assert.AreEqual ("0.000123", ts.ToString ("s\\.ffffff"), "#G0");
+ Assert.AreEqual ("0.0001", ts.ToString ("s\\.ffff"), "#G1");
+ Assert.AreEqual ("0.", ts.ToString ("s\\.F"), "#G2");
+ Assert.AreEqual ("0.", ts.ToString ("s\\.FFF"), "#G3");
+
+ ts = TimeSpan.FromSeconds (0.05);
+ Assert.AreEqual (".0", ts.ToString ("\\.f"), "#H0");
+ Assert.AreEqual (".", ts.ToString ("\\.F"), "#H1");
}
[Test]
}
public event EventHandler E;
+ public void Dummy ()
+ {
+ E += delegate {};
+ }
}
class Derived1 : Base1
set { }
}
- public event Action E;
+ public new event Action E;
+ public new void Dummy ()
+ {
+ E += delegate {};
+ }
}
public class Foo<T>
struct B
{
+ #pragma warning disable 169
int value;
+ #pragma warning restore 169
}
[Test]
a1 = new string [10];
}
- class X
+ public class X
{
public static int Value;
}
<Compile Include="System\Exception.cs" />\r
<Compile Include="System\ExecutionEngineException.cs" />\r
<Compile Include="System\FieldAccessException.cs" />\r
+ <Compile Include="System\FirstChanceExceptionEventArgs.cs" />\r
<Compile Include="System\FlagsAttribute.cs" />\r
<Compile Include="System\FormatException.cs" />\r
<Compile Include="System\Funcs.cs" />\r
System/Exception.cs
System/ExecutionEngineException.cs
System/FieldAccessException.cs
+System/FirstChanceExceptionEventArgs.cs
System/FlagsAttribute.cs
System/FormatException.cs
System/Funcs.cs
--- /dev/null
+// CS0023: The `?' operator cannot be applied to operand of type `T'
+// Line: 8
+
+class C
+{
+ static void Foo<T> (T t) where T : struct
+ {
+ var r = t?.ToString ();
+ }
+}
\ No newline at end of file
+++ /dev/null
-// CS0429: Unreachable expression code detected
-// Line: 24
-// Compiler options: -warnaserror
-
-using System;
-
-struct S
-{
-}
-
-class C
-{
- public static implicit operator S (C c)
- {
- return new S ();
- }
-}
-
-class Program
-{
- static void Main ()
- {
- C c = new C ();
- Console.WriteLine (c ?? new S ());
- }
-}
--- /dev/null
+// CS0573: 'S': Structs cannot have instance property or field initializers
+// Line: 6
+
+struct S
+{
+ public int Prop { get; set; } = 3;
+}
--- /dev/null
+// CS0573: 'S': Structs cannot have instance property or field initializers
+// Line: 6
+
+struct S
+{
+ int v = 0;
+}
+++ /dev/null
-// CS1644: Feature `struct instance member initializer' cannot be used because it is not part of the C# 5.0 language specification
-// Line: 12
-// Compiler options: -langversion:5
-
-struct S
-{
- int i = 0;
-}
\ No newline at end of file
--- /dev/null
+// CS1736: The expression being assigned to optional parameter `s' must be a constant or default value
+// Line: 11
+
+struct S
+{
+ public S ()
+ {
+ }
+}
+
+class X
+{
+ public void Foo (S s = new S ())
+ {
+ }
+}
\ No newline at end of file
initialized_static_fields.Add (expression);
} else {
- if (Kind == MemberKind.Struct && Compiler.Settings.Version < LanguageVersion.V_6) {
- Report.FeatureIsNotAvailable (Compiler, expression.Location, "struct instance member initializer");
+ if (Kind == MemberKind.Struct) {
+ if (Compiler.Settings.Version != LanguageVersion.Experimental) {
+ Report.Error (573, expression.Location, "'{0}': Structs cannot have instance property or field initializers",
+ GetSignatureForError ());
+ }
}
if (initialized_fields == null)
switch (type.BuiltinType) {
case BuiltinTypeSpec.Type.Bool:
//
- // Workaround MSIL limitation. Load bool element as single bit,
- // bool array can actually store any byte value
+ // bool array can actually store any byte value in underlying byte slot
+ // and C# spec does not specify any normalization rule, except the result
+ // is undefined
//
- ig.Emit (OpCodes.Ldelem_U1);
- ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Cgt_Un);
- break;
case BuiltinTypeSpec.Type.Byte:
ig.Emit (OpCodes.Ldelem_U1);
break;
ig.Emit (OpCodes.Ldind_U1);
break;
case BuiltinTypeSpec.Type.SByte:
- ig.Emit (OpCodes.Ldind_I1);
- break;
case BuiltinTypeSpec.Type.Bool:
ig.Emit (OpCodes.Ldind_I1);
- ig.Emit (OpCodes.Ldc_I4_0);
- ig.Emit (OpCodes.Cgt_Un);
break;
case BuiltinTypeSpec.Type.ULong:
case BuiltinTypeSpec.Type.Long:
var ie = new InstanceEmitter (instance_copy, IsAddressCall (instance_copy, call_op, method.DeclaringType));
if (Arguments == null) {
- ie.EmitLoad (ec);
+ ie.EmitLoad (ec, true);
}
} else if (!InstanceExpressionOnStack) {
var ie = new InstanceEmitter (InstanceExpression, IsAddressCall (InstanceExpression, call_op, method.DeclaringType));
instance_address = instance as LocalTemporary;
if (instance_address == null) {
- EmitLoad (ec);
+ EmitLoad (ec, false);
ec.Emit (OpCodes.Dup);
ec.EmitLoadFromPtr (instance.Type);
} else {
instance.Emit (ec);
}
-
- if (instance.Type.Kind == MemberKind.TypeParameter)
- ec.Emit (OpCodes.Box, instance.Type);
} else {
- EmitLoad (ec);
+ EmitLoad (ec, !conditionalAccess);
if (conditionalAccess) {
conditional_access_dup = !IsInexpensiveLoad ();
}
if (conditionalAccess) {
+ if (instance.Type.Kind == MemberKind.TypeParameter)
+ ec.Emit (OpCodes.Box, instance.Type);
+
ec.Emit (OpCodes.Brtrue_S, NullOperatorLabel);
if (conditional_access_dup)
}
}
- public void EmitLoad (EmitContext ec)
+ public void EmitLoad (EmitContext ec, bool boxInstance)
{
var instance_type = instance.Type;
instance.Emit (ec);
// Only to make verifier happy
- if (RequiresBoxing ())
+ if (boxInstance && RequiresBoxing ()) {
ec.Emit (OpCodes.Box, instance_type);
+ }
}
public TypeSpec GetStackType (EmitContext ec)
return false;
}
+ //
+ // Returns true for cheap race-free load, where we can avoid using dup
+ //
bool IsInexpensiveLoad ()
{
if (instance is Constant)
return false;
var vr = instance as VariableReference;
- if (vr != null)
- return !vr.IsRef;
+ if (vr != null) {
+ // Load from captured local would be racy without dup
+ return !vr.IsRef && !vr.IsHoisted;
+ }
if (instance is LocalTemporary)
return true;
int next_token;
int parens = 0;
int generics = 0;
+ int brackets = 0;
var nt = xtoken ();
switch (nt) {
++parens;
goto default;
+ case Token.OPEN_BRACKET:
+ case Token.OPEN_BRACKET_EXPR:
+ ++brackets;
+ goto default;
+
case Token.CLOSE_PARENS:
--parens;
goto default;
int interrs = 1;
int colons = 0;
int braces = 0;
- int brackets = 0;
//
// All shorcuts failed, do it hard way
//
return false;
case MemberKind.InternalCompilerType:
return type.BuiltinType == BuiltinTypeSpec.Type.Dynamic;
+ case MemberKind.TypeParameter:
+ return !((TypeParameterSpec) type).IsValueType;
default:
return true;
}
static Expression GetOperatorTrueOrFalse (ResolveContext ec, Expression e, bool is_true, Location loc)
{
var op = is_true ? Operator.OpType.True : Operator.OpType.False;
- var methods = MemberCache.GetUserOperator (e.type, op, false);
+ var type = e.type;
+ if (type.IsNullableType)
+ type = Nullable.NullableInfo.GetUnderlyingType (type);
+
+ var methods = MemberCache.GetUserOperator (type, op, false);
if (methods == null)
return null;
return expr;
}
- enum_type = rc.Module.PredefinedTypes.Nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type });
+ var nullable = rc.Module.PredefinedTypes.Nullable;
+
+ //
+ // Don't try nullable version when nullable type is undefined
+ //
+ if (!nullable.IsDefined)
+ return null;
+
+ enum_type = nullable.TypeSpec.MakeGenericType (rc.Module, new[] { enum_type });
}
expr = ResolveOperatorPredefined (rc, rc.Module.GetPredefinedEnumAritmeticOperators (enum_type, true), false);
}
//
- // Returns true for resolved `new S()'
+ // Returns true for resolved `new S()' when S does not declare parameterless constructor
//
- public bool IsDefaultStruct {
+ public bool IsGeneratedStructConstructor {
get {
- return arguments == null && type.IsStruct && GetType () == typeof (New);
+ return arguments == null && method == null && type.IsStruct && GetType () == typeof (New);
}
}
get {
return source;
}
+ set {
+ source = value;
+ }
}
public override bool ContainsEmitWithAwait ()
return ReducedExpression.Create (right, this, false).Resolve (ec);
left = Convert.ImplicitConversion (ec, unwrap ?? left, rtype, loc);
-
- if (TypeSpec.IsValueType (left.Type) && !left.Type.IsNullableType) {
- Warning_UnreachableExpression (ec, right.Location);
- return ReducedExpression.Create (left, this, false).Resolve (ec);
- }
-
type = rtype;
return this;
}
return;
}
- left.Emit (ec);
- ec.Emit (OpCodes.Dup);
+ //
+ // Null check is done on original expression not after expression is converted to
+ // result type. This is in most cases same but when user conversion is involved
+ // we can end up in situation when use operator does the null handling which is
+ // not what the operator is supposed to do
+ //
+ var op_expr = left as UserCast;
+ if (op_expr != null) {
+ op_expr.Source.Emit (ec);
+ LocalTemporary temp;
+
+ // TODO: More load kinds can be special cased
+ if (!(op_expr.Source is VariableReference)) {
+ temp = new LocalTemporary (op_expr.Source.Type);
+ temp.Store (ec);
+ temp.Emit (ec);
+ op_expr.Source = temp;
+ } else {
+ temp = null;
+ }
- // Only to make verifier happy
- if (left.Type.IsGenericParameter)
- ec.Emit (OpCodes.Box, left.Type);
+ var right_label = ec.DefineLabel ();
+ ec.Emit (OpCodes.Brfalse_S, right_label);
+ left.Emit (ec);
+ ec.Emit (OpCodes.Br, end_label);
+ ec.MarkLabel (right_label);
- ec.Emit (OpCodes.Brtrue, end_label);
+ if (temp != null)
+ temp.Release (ec);
+ } else {
+ //
+ // Common case where expression is not modified before null check and
+ // we generate better/smaller code
+ //
+ left.Emit (ec);
+ ec.Emit (OpCodes.Dup);
+
+ // Only to make verifier happy
+ if (left.Type.IsGenericParameter)
+ ec.Emit (OpCodes.Box, left.Type);
+
+ ec.Emit (OpCodes.Brtrue, end_label);
+
+ ec.Emit (OpCodes.Pop);
+ }
- ec.Emit (OpCodes.Pop);
right.Emit (ec);
ec.MarkLabel (end_label);
expr = Child;
- if (!(expr is Constant || expr is DefaultValueExpression || (expr is New && ((New) expr).IsDefaultStruct))) {
+ if (!(expr is Constant || expr is DefaultValueExpression || (expr is New && ((New) expr).IsGeneratedStructConstructor))) {
if (!(expr is ErrorExpression)) {
rc.Report.Error (1736, Location,
"The expression being assigned to optional parameter `{0}' must be a constant or default value",
}
}
- return sl;
+ if (sl == null || sl.SectionStart)
+ return sl;
+
+ //
+ // Always return section start, it simplifies handling of switch labels
+ //
+ for (int idx = case_labels.IndexOf (sl); ; --idx) {
+ var cs = case_labels [idx];
+ if (cs.SectionStart)
+ return cs;
+ }
}
protected override bool DoFlowAnalysis (FlowAnalysisContext fc)
var e = b ? () => { } : a;
}
+ void Test_19 (int[,] table)
+ {
+ var x = 1 > 0 ? table[5, 1] : 0;
+ }
+
static void Helper<T> (T arg)
{
}
--- /dev/null
+struct S
+{
+ public static bool operator true (S? S)
+ {
+ return true;
+ }
+
+ public static bool operator false (S? S)
+ {
+ return true;
+ }
+}
+
+class P
+{
+ static void Main ()
+ {
+ if (new S? ()) {
+ }
+ }
+}
\ No newline at end of file
+// Compiler options: -langversion:experimental
using System;
struct S
+// Compiler options: -langversion:experimental
struct S
{
public decimal P { get; } = -3;
+++ /dev/null
-// Compiler options: -unsafe
-
-using System;
-
-class BoolArrayWithByteValues
-{
-
- static int Foo (ref bool b)
- {
- bool b2 = true;
- bool r;
- r = b == true;
- if (!r)
- return 10;
-
- r = b == b2;
- if (!r)
- return 11;
-
- return 0;
- }
-
- static unsafe bool Ptr ()
- {
- bool rv;
-
- var arr = new byte [256];
- for (int i = 0; i < arr.Length; i++)
- arr [i] = (byte) i;
- fixed (byte* bptr = arr) {
- rv = true;
- for (int i = 0; i < arr.Length; i++) {
- bool* boptr = (bool*)(bptr + i);
- if (arr[i] > 0 && !*boptr)
- rv = false;
- System.Console.WriteLine ("#{0} = {1}", i, *boptr);
- }
- }
-
- return rv;
- }
-
- static int Main()
- {
- var a = new bool[1];
- Buffer.SetByte (a, 0, 5);
-
- var b = true;
- bool r;
- r = a [0];
- if (!r)
- return 1;
-
- r = a [0] == true;
- if (!r)
- return 2;
-
- r = a [0] == b;
- if (!r)
- return 3;
-
- r = a [0] != false;
- if (!r)
- return 4;
-
- r = a [0] != b;
- if (r)
- return 5;
-
- var res = Foo (ref a [0]);
- if (res != 0)
- return res;
-
- if (!Ptr ())
- return 6;
-
- return 0;
- }
-}
\ No newline at end of file
class C
{
+ public static int ConversionCalled;
+
public static implicit operator S (C c)
{
+ ++ConversionCalled;
return new S ();
}
}
class Program
{
- static void Main ()
+ static C field;
+
+ static int Main ()
{
C c = new C ();
var x = c ?? new S ();
+
+ if (C.ConversionCalled != 1)
+ return 1;
+
+ c = null;
+ x = c ?? new S ();
+ if (C.ConversionCalled != 1)
+ return 2;
+
+ x = field ?? new S ();
+ if (C.ConversionCalled != 1)
+ return 3;
+
+ return 0;
}
}
\ No newline at end of file
+// Compiler options: -langversion:experimental
using System;
struct S1
--- /dev/null
+public enum Foo { One, Two };
+
+class MainClass
+{
+ public static int Main ()
+ {
+ const Foo foo = Foo.Two;
+ int obj;
+
+ switch (foo) {
+ case Foo.One:
+ case Foo.Two:
+ obj = 2;
+ break;
+ }
+
+ if (obj != 2)
+ return 1;
+
+ return 0;
+ }
+}
--- /dev/null
+using System;
+
+class Test
+{
+ static void Main ()
+ {
+ Test_1 ("");
+ Test_1<object> (null);
+
+ Test_2<object> (null);
+ Test_2 ("z");
+ Test_2 (0);
+ Test_2 ((long?) -8);
+
+ Test_3 (new int[1]);
+ Test_3 (new int[] { 5 });
+ }
+
+ static void Test_1<T> (T x) where T : class
+ {
+ x?.Call ();
+ }
+
+ static void Test_2<T> (T x)
+ {
+ x?.Call ();
+ }
+
+ static void Test_3<T> (T[] x)
+ {
+ x[0]?.Call ();
+ }
+}
+
+static class Ext
+{
+ public static void Call<T> (this T t)
+ {
+ Console.WriteLine (typeof (T));
+ }
+}
+// Compiler options: -langversion:experimental
class Simple(int arg)
{
int Property { get; } = arg;
+// Compiler options: -langversion:experimental
+
using System;
struct S (int x)
<method name="Void <Test_18>m__1()" attrs="145">\r
<size>2</size>\r
</method>\r
+ <method name="Void Test_19(Int32[,])" attrs="129">\r
+ <size>11</size>\r
+ </method>\r
</type>\r
</test>\r
<test name="gtest-410.cs">\r
</method>\r
</type>\r
</test>\r
+ <test name="gtest-625.cs">\r
+ <type name="S">\r
+ <method name="Boolean op_True(System.Nullable`1[S])" attrs="2198">\r
+ <size>10</size>\r
+ </method>\r
+ <method name="Boolean op_False(System.Nullable`1[S])" attrs="2198">\r
+ <size>10</size>\r
+ </method>\r
+ </type>\r
+ <type name="P">\r
+ <method name="Void Main()" attrs="145">\r
+ <size>23</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="gtest-anontype-01.cs">\r
<type name="Test">\r
<method name="Int32 Main()" attrs="150">\r
<test name="test-146.cs">\r
<type name="Test">\r
<method name="Int32 Main()" attrs="150">\r
- <size>355</size>\r
+ <size>352</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
<test name="test-729.cs">\r
<type name="Primes.MainClass">\r
<method name="Int32 Main()" attrs="150">\r
- <size>149</size>\r
+ <size>146</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
</type>\r
</test>\r
- <test name="test-898.cs">\r
- <type name="BoolArrayWithByteValues">\r
- <method name="Int32 Foo(Boolean ByRef)" attrs="145">\r
- <size>58</size>\r
- </method>\r
- <method name="Int32 Main()" attrs="145">\r
- <size>191</size>\r
- </method>\r
- <method name="Void .ctor()" attrs="6278">\r
- <size>7</size>\r
- </method>\r
- <method name="Boolean Ptr()" attrs="145">\r
- <size>167</size>\r
- </method>\r
- </type>\r
- </test>\r
<test name="test-899.cs">\r
<type name="Test">\r
<method name="Void Main()" attrs="150">\r
<test name="test-903.cs">\r
<type name="C">\r
<method name="S op_Implicit(C)" attrs="2198">\r
- <size>18</size>\r
+ <size>30</size>\r
</method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
</type>\r
<type name="Program">\r
- <method name="Void Main()" attrs="145">\r
- <size>15</size>\r
- </method>\r
<method name="Void .ctor()" attrs="6278">\r
<size>7</size>\r
</method>\r
+ <method name="Int32 Main()" attrs="145">\r
+ <size>155</size>\r
+ </method>\r
</type>\r
</test>\r
<test name="test-904.cs">\r
</method>\r
</type>\r
</test>\r
+ <test name="test-907.cs">\r
+ <type name="MainClass">\r
+ <method name="Int32 Main()" attrs="150">\r
+ <size>37</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-91.cs">\r
<type name="Abstract">\r
<method name="Void .ctor()" attrs="6276">\r
</type>\r
<type name="Tester+<ArrayAccessTest_1>c__async0">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>338</size>\r
+ <size>335</size>\r
</method>\r
</type>\r
<type name="Tester+<ArrayAccessTest_2>c__async1">\r
</type>\r
<type name="Tester+<ArrayAccessTest_10>c__async9">\r
<method name="Void MoveNext()" attrs="486">\r
- <size>235</size>\r
+ <size>232</size>\r
</method>\r
<method name="Void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine)" attrs="486">\r
<size>13</size>\r
</method>\r
</type>\r
</test>\r
+ <test name="test-null-operator-010.cs">\r
+ <type name="Test">\r
+ <method name="Void Main()" attrs="145">\r
+ <size>79</size>\r
+ </method>\r
+ <method name="Void Test_1[T](T)" attrs="145">\r
+ <size>22</size>\r
+ </method>\r
+ <method name="Void Test_2[T](T)" attrs="145">\r
+ <size>21</size>\r
+ </method>\r
+ <method name="Void Test_3[T](T[])" attrs="145">\r
+ <size>28</size>\r
+ </method>\r
+ <method name="Void .ctor()" attrs="6278">\r
+ <size>7</size>\r
+ </method>\r
+ </type>\r
+ <type name="Ext">\r
+ <method name="Void Call[T](T)" attrs="150">\r
+ <size>17</size>\r
+ </method>\r
+ </type>\r
+ </test>\r
<test name="test-null-operator-02.cs">\r
<type name="CI">\r
<method name="Int32 get_Prop()" attrs="2182">\r
get { return ignoreAdded; }
}
+ static List<Regex> ignoreNew = new List<Regex> ();
+ public static List<Regex> IgnoreNew {
+ get { return ignoreNew; }
+ }
+
static List<Regex> ignoreRemoved = new List<Regex> ();
public static List<Regex> IgnoreRemoved {
get { return ignoreRemoved; }
var options = new OptionSet {
{ "h|help", "Show this help", v => showHelp = true },
{ "d|diff=", "HTML diff file out output (omit for stdout)", v => diff = v },
- { "i|ignore=", "Ignore both added and removed members whose description matches a given C# regular expression (see below).",
+ { "i|ignore=", "Ignore new, added, and removed members whose description matches a given C# regular expression (see below).",
v => {
var r = new Regex (v);
State.IgnoreAdded.Add (r);
State.IgnoreRemoved.Add (r);
+ State.IgnoreNew.Add (r);
}
},
{ "a|ignore-added=", "Ignore added members whose description matches a given C# regular expression (see below).",
{ "r|ignore-removed=", "Ignore removed members whose description matches a given C# regular expression (see below).",
v => State.IgnoreRemoved.Add (new Regex (v))
},
+ { "n|ignore-new=", "Ignore new namespaces and types whose description matches a given C# regular expression (see below).",
+ v => State.IgnoreNew.Add (new Regex (v))
+ },
{ "x|lax", "Ignore duplicate XML entries", v => State.Lax = true }
};
using System;
using System.Collections.Generic;
using System.IO;
+using System.Linq;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public override void Added (XElement target)
{
- Output.WriteLine ("<h3>New Type {0}.{1}</h3>", State.Namespace, target.Attribute ("name").Value);
+ string name = target.Attribute ("name").Value;
+ if (State.IgnoreNew.Any (re => re.IsMatch (name)))
+ return;
+ Output.WriteLine ("<h3>New Type {0}.{1}</h3>", State.Namespace, name);
Output.WriteLine ("<pre>");
State.Indent = 0;
AddedInner (target);
using System;
using System.IO;
+using System.Linq;
using System.Xml.Linq;
namespace Xamarin.ApiDiff {
public override void Added (XElement target)
{
- Output.WriteLine ("<h2>New Namespace {0}</h2>", target.Attribute ("name").Value);
+ string name = target.Attribute ("name").Value;
+ if (State.IgnoreNew.Any (re => re.IsMatch (name)))
+ return;
+
+ Output.WriteLine ("<h2>New Namespace {0}</h2>", name);
Output.WriteLine ();
// list all new types
foreach (var addedType in target.Element ("classes").Elements ("class"))
if (TypeHelper.IsDelegate(t))
return "delegate";
+ if (t.IsPointer)
+ return "pointer";
+
return "class";
}
AddAttribute (p, "abstract", "true");
if (mbase.IsVirtual)
AddAttribute (p, "virtual", "true");
+ if (mbase.IsFinal && mbase.IsVirtual && mbase.IsReuseSlot)
+ AddAttribute (p, "sealed", "true");
if (mbase.IsStatic)
AddAttribute (p, "static", "true");
Test/DocTest.dll:
$(CSCOMPILE) $(TEST_CSCFLAGS) -debug -unsafe -target:library -out:$@ Test/DocTest.cs
+Test/DocTest-InternalInterface.dll:
+ $(CSCOMPILE) $(TEST_CSCFLAGS) -debug -unsafe -target:library -out:$@ Test/DocTest-InternalInterface.cs
+
Test/DocTest.dll-v1:
-rm -f Test/DocTest.cs
cp Test/DocTest-v1.cs Test/DocTest.cs
update-monodocer-dropns-classic-secondary: $(PROGRAM)
$(MAKE) Test/DocTest-DropNS-classic-secondary.dll
$(MONO) $(PROGRAM) update --exceptions=all -o Test/en.actual Test/DocTest-DropNS-classic-secondary.dll
-
+
+check-monodocer-internal-interface: $(PROGRAM)
+ # Tests to make sure internal interfaces that are explicitly implemented are not documented
+ -rm -Rf Test/en.actual
+ $(MAKE) Test/DocTest-InternalInterface.dll
+ $(MONO) $(PROGRAM) update --exceptions=all -o Test/en.actual Test/DocTest-InternalInterface.dll
+ diff --exclude=.svn -rup Test/en.expected-internal-interface Test/en.actual
check-monodocer-update: $(PROGRAM)
find Test/en.expected -name \*.xml -exec rm "{}" \;
check-monodocer-update \
check-monodocer-dropns-classic \
check-monodocer-dropns-classic-withsecondary \
+ check-monodocer-internal-interface \
check-monodocer-delete-update \
check-mdoc-export-html-update \
check-mdoc-export-msxdoc-update \
throw new ArgumentException ("Unknown kind for type: " + type.FullName);
}
- private static bool IsPublic (TypeDefinition type)
+ public static bool IsPublic (TypeDefinition type)
{
TypeDefinition decl = type;
while (decl != null) {
string sig = memberFormatters [0].GetDeclaration (m);
if (sig == null) return false;
if (seenmembers.ContainsKey(sig)) return false;
+
+ // Verify that the member isn't an explicitly implemented
+ // member of an internal interface, in which case we shouldn't return true.
+ MethodDefinition methdef = null;
+ if (m is MethodDefinition)
+ methdef = m as MethodDefinition;
+ else if (m is PropertyDefinition) {
+ var prop = m as PropertyDefinition;
+ methdef = prop.GetMethod ?? prop.SetMethod;
+ }
+
+ if (methdef != null) {
+ TypeReference iface;
+ MethodReference imethod;
+
+ if (methdef.Overrides.Count == 1) {
+ DocUtils.GetInfoForExplicitlyImplementedMethod (methdef, out iface, out imethod);
+ if (!IsPublic (iface.Resolve ())) return false;
+ }
+ }
+
return true;
})
.ToArray();
if (!inheritedInterfaces.Contains (GetQualifiedTypeName (lookup)))
userInterfaces.Add (iface);
}
- return userInterfaces;
+ return userInterfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()));
}
private static string GetQualifiedTypeName (TypeReference type)
buf.Append (full.GetName (type.BaseType).Substring ("class ".Length));
}
bool first = true;
- foreach (var name in type.Interfaces
+ foreach (var name in type.Interfaces.Where (i => MDocUpdater.IsPublic (i.Resolve ()))
.Select (i => full.GetName (i))
.OrderBy (n => n)) {
if (first) {
--- /dev/null
+namespace MyNamespace {
+ internal interface MyInternalInterface {
+ bool Foo {get;set;}
+ string FooSet {set;}
+ void FooMeth ();
+ void BarMeth ();
+ }
+
+ public class MyClass : MyInternalInterface {
+ public string Bar {get;set;}
+ public void BarMeth () {} // part of the interface, but publicly implemented
+
+ string MyInternalInterface.FooSet {set {}}
+ bool MyInternalInterface.Foo {get;set;}
+ void MyInternalInterface.FooMeth () {}
+ }
+}
--- /dev/null
+<Type Name="MyClass" FullName="MyNamespace.MyClass">
+ <TypeSignature Language="C#" Value="public class MyClass" />
+ <TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit MyClass extends System.Object" />
+ <AssemblyInfo>
+ <AssemblyName>DocTest-InternalInterface</AssemblyName>
+ <AssemblyVersion>0.0.0.0</AssemblyVersion>
+ </AssemblyInfo>
+ <Base>
+ <BaseTypeName>System.Object</BaseTypeName>
+ </Base>
+ <Interfaces />
+ <Docs>
+ <summary>To be added.</summary>
+ <remarks>To be added.</remarks>
+ </Docs>
+ <Members>
+ <Member MemberName=".ctor">
+ <MemberSignature Language="C#" Value="public MyClass ();" />
+ <MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
+ <MemberType>Constructor</MemberType>
+ <AssemblyInfo>
+ <AssemblyVersion>0.0.0.0</AssemblyVersion>
+ </AssemblyInfo>
+ <Parameters />
+ <Docs>
+ <summary>To be added.</summary>
+ <remarks>To be added.</remarks>
+ </Docs>
+ </Member>
+ <Member MemberName="Bar">
+ <MemberSignature Language="C#" Value="public string Bar { get; set; }" />
+ <MemberSignature Language="ILAsm" Value=".property instance string Bar" />
+ <MemberType>Property</MemberType>
+ <AssemblyInfo>
+ <AssemblyVersion>0.0.0.0</AssemblyVersion>
+ </AssemblyInfo>
+ <ReturnValue>
+ <ReturnType>System.String</ReturnType>
+ </ReturnValue>
+ <Docs>
+ <summary>To be added.</summary>
+ <value>To be added.</value>
+ <remarks>To be added.</remarks>
+ </Docs>
+ </Member>
+ <Member MemberName="BarMeth">
+ <MemberSignature Language="C#" Value="public void BarMeth ();" />
+ <MemberSignature Language="ILAsm" Value=".method public hidebysig newslot virtual instance void BarMeth() cil managed" />
+ <MemberType>Method</MemberType>
+ <AssemblyInfo>
+ <AssemblyVersion>0.0.0.0</AssemblyVersion>
+ </AssemblyInfo>
+ <ReturnValue>
+ <ReturnType>System.Void</ReturnType>
+ </ReturnValue>
+ <Parameters />
+ <Docs>
+ <summary>To be added.</summary>
+ <remarks>To be added.</remarks>
+ </Docs>
+ </Member>
+ </Members>
+</Type>
--- /dev/null
+<Overview>
+ <Assemblies>
+ <Assembly Name="DocTest-InternalInterface" Version="0.0.0.0">
+ <Attributes>
+ <Attribute>
+ <AttributeName>System.Diagnostics.Debuggable(System.Diagnostics.DebuggableAttribute+DebuggingModes.IgnoreSymbolStoreSequencePoints)</AttributeName>
+ </Attribute>
+ <Attribute>
+ <AttributeName>System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows=true)</AttributeName>
+ </Attribute>
+ </Attributes>
+ </Assembly>
+ </Assemblies>
+ <Remarks>To be added.</Remarks>
+ <Copyright>To be added.</Copyright>
+ <Types>
+ <Namespace Name="MyNamespace">
+ <Type Name="MyClass" Kind="Class" />
+ </Namespace>
+ </Types>
+ <Title>DocTest-InternalInterface</Title>
+</Overview>
--- /dev/null
+<Namespace Name="MyNamespace">
+ <Docs>
+ <summary>To be added.</summary>
+ <remarks>To be added.</remarks>
+ </Docs>
+</Namespace>
return (process_handle->id);
}
-guint32 GetCurrentProcessId (void)
-{
- mono_once (&process_current_once, process_set_current);
-
- return (GetProcessId (current_process));
-}
-
/* Returns the process id as a convenience to the functions that call this */
static pid_t signal_process_if_gone (gpointer handle)
{
}
#endif /* UNUSED_CODE */
-#if defined(PLATFORM_MACOSX) || defined(__OpenBSD__)
-
-gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
-{
- guint32 count, fit, i, j;
- gint32 err;
- gboolean done;
- size_t proclength, size;
-#if defined(__OpenBSD__)
- struct kinfo_proc *result;
- int name[6];
- name[0] = CTL_KERN;
- name[1] = KERN_PROC;
- name[2] = KERN_PROC_ALL;
- name[3] = 0;
- name[4] = sizeof(struct kinfo_proc);
- name[5] = 0;
-#else
- struct kinfo_proc *result;
- static const int name[] = { CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 };
-#endif
-
- mono_once (&process_current_once, process_set_current);
-
- result = NULL;
- done = FALSE;
-
- do {
- proclength = 0;
-#if defined(__OpenBSD__)
- size = (sizeof(name) / sizeof(*name));
-#else
- size = (sizeof(name) / sizeof(*name)) - 1;
-#endif
- err = sysctl ((int *)name, size, NULL, &proclength, NULL, 0);
-
- if (err == 0) {
- result = malloc (proclength);
-
- if (result == NULL)
- return FALSE;
-
-#if defined(__OpenBSD__)
- name[5] = (int)(proclength / sizeof(struct kinfo_proc));
-#endif
-
- err = sysctl ((int *) name, size, result, &proclength, NULL, 0);
-
- if (err == 0)
- done = TRUE;
- else {
- free (result);
- result = NULL;
- }
- }
- } while (err == 0 && !done);
-
- if (err != 0) {
- if (result != NULL) {
- free (result);
- result = NULL;
- }
- return(FALSE);
- }
-
- count = proclength / sizeof(struct kinfo_proc);
-
- fit = len / sizeof(guint32);
- for (i = 0, j = 0; j< fit && i < count; i++) {
-#if defined(__OpenBSD__)
- pids [j++] = result [i].p_pid;
-#else
- if (result[i].kp_proc.p_pid > 0) /* Pid 0 not supported */
- pids [j++] = result [i].kp_proc.p_pid;
-#endif
- }
- free (result);
- result = NULL;
- *needed = j * sizeof(guint32);
-
- return(TRUE);
-}
-#elif defined(__HAIKU__)
-
-gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
-{
- guint32 fit, i = 0;
- int32 cookie = 0;
- team_info teamInfo;
-
- mono_once (&process_current_once, process_set_current);
-
- fit = len / sizeof (guint32);
- while (get_next_team_info (&cookie, &teamInfo) == B_OK && i < fit) {
- pids [i++] = teamInfo.team;
- }
- *needed = i * sizeof (guint32);
-
- return TRUE;
-}
-#else
-gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed)
-{
- guint32 fit, i;
- DIR *dir;
- struct dirent *entry;
-
- mono_once (&process_current_once, process_set_current);
-
- dir = opendir ("/proc");
- if (dir == NULL) {
- return(FALSE);
- }
-
- i = 0;
- fit = len / sizeof (guint32);
- while(i < fit && (entry = readdir (dir)) != NULL) {
- pid_t pid;
- char *endptr;
-
- if (!isdigit (entry->d_name[0]))
- continue;
-
- pid = (pid_t) strtol (entry->d_name, &endptr, 10);
- if (*endptr == '\0')
- pids [i++] = (guint32) pid;
- }
- closedir (dir);
- *needed = i * sizeof(guint32);
-
- return(TRUE);
-}
-#endif
-
static gboolean process_open_compare (gpointer handle, gpointer user_data)
{
pid_t wanted_pid;
extern gpointer GetCurrentProcess (void);
extern guint32 GetProcessId (gpointer handle);
-extern guint32 GetCurrentProcessId (void);
-extern gboolean EnumProcesses (guint32 *pids, guint32 len, guint32 *needed);
extern gboolean CloseProcess (gpointer handle);
extern gpointer OpenProcess (guint32 access, gboolean inherit, guint32 pid);
extern gboolean GetExitCodeProcess (gpointer process, guint32 *code);
icall.c \
icall-def.h \
image.c \
+ jit-info.c \
loader.c \
locales.c \
locales.h \
sgen-gc.c \
sgen-internal.c \
sgen-marksweep.c \
- sgen-marksweep-fixed.c \
- sgen-marksweep-par.c \
- sgen-marksweep-fixed-par.c \
sgen-los.c \
sgen-protocol.c \
sgen-bridge.c \
sgen-layout-stats.c \
sgen-layout-stats.h \
sgen-qsort.c \
- sgen-qsort.h
+ sgen-qsort.h \
+ sgen-tagged-pointer.h
libmonoruntime_la_SOURCES = $(common_sources) $(gc_dependent_sources) $(null_gc_sources) $(boehm_sources)
libmonoruntime_la_CFLAGS = $(BOEHM_DEFINES)
bytes_var = mono_mb_add_local (mb, &mono_defaults.int32_class->byval_arg);
if (atype == ATYPE_STRING) {
/* a string alloator method takes the args: (vtable, len) */
- /* bytes = (sizeof (MonoString) + ((len + 1) * 2)); */
+ /* bytes = (offsetof (MonoString, chars) + ((len + 1) * 2)); */
mono_mb_emit_ldarg (mb, 1);
mono_mb_emit_icon (mb, 1);
mono_mb_emit_byte (mb, MONO_CEE_ADD);
return G_MAXINT;
}
+void
+mono_gc_set_string_length (MonoString *str, gint32 new_length)
+{
+ mono_unichar2 *new_end = str->chars + new_length;
+
+ /* zero the discarded string. This null-delimits the string and allows
+ * the space to be reclaimed by SGen. */
+
+ memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
+ str->length = new_length;
+}
+
gboolean
mono_gc_user_markers_supported (void)
{
MONO_EXCEPTION_GENERIC_SHARING_FAILED = 11,
MONO_EXCEPTION_BAD_IMAGE = 12,
MONO_EXCEPTION_OBJECT_SUPPLIED = 13, /*The exception object is already created.*/
- MONO_EXCEPTION_OUT_OF_MEMORY = 14
- /* add other exception type */
+ MONO_EXCEPTION_OUT_OF_MEMORY = 14,
+ MONO_EXCEPTION_INLINE_FAILED = 15
};
/* This struct collects the info needed for the runtime use of a class,
mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Invalid negative field offset %d for %s", field->offset, field->name));
break;
}
+ if (class->generic_container) {
+ mono_class_set_failure (class, MONO_EXCEPTION_TYPE_LOAD, g_strdup_printf ("Generic class cannot have explicit layout."));
+ break;
+ }
}
}
gboolean cas_method_permitonly:1;
} MonoMethodCasInfo;
+typedef enum {
+ JIT_INFO_NONE = 0,
+ JIT_INFO_HAS_CAS_INFO = (1 << 0),
+ JIT_INFO_HAS_GENERIC_JIT_INFO = (1 << 1),
+ JIT_INFO_HAS_TRY_BLOCK_HOLES = (1 << 2),
+ JIT_INFO_HAS_ARCH_EH_INFO = (1 << 3)
+} MonoJitInfoFlags;
+
struct _MonoJitInfo {
/* NOTE: These first two elements (method and
next_jit_code_hash) must be in the same order and at the
void
mono_close_exe_image (void) MONO_INTERNAL;
+int
+mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes) MONO_INTERNAL;
+
+void
+mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size,
+ MonoJitInfoFlags flags, int num_clauses, int num_holes) MONO_INTERNAL;
+
+MonoJitInfoTable *
+mono_jit_info_table_new (MonoDomain *domain) MONO_INTERNAL;
+
+void
+mono_jit_info_table_free (MonoJitInfoTable *table) MONO_INTERNAL;
+
void
mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji) MONO_INTERNAL;
static const MonoRuntimeInfo *current_runtime = NULL;
-static MonoJitInfoFindInAot jit_info_find_in_aot_func = NULL;
-
/* This is the list of runtime versions supported by this JIT.
*/
static const MonoRuntimeInfo supported_runtimes[] = {
return offset;
}
-#define JIT_INFO_TABLE_FILL_RATIO_NOM 3
-#define JIT_INFO_TABLE_FILL_RATIO_DENOM 4
-#define JIT_INFO_TABLE_FILLED_NUM_ELEMENTS (MONO_JIT_INFO_TABLE_CHUNK_SIZE * JIT_INFO_TABLE_FILL_RATIO_NOM / JIT_INFO_TABLE_FILL_RATIO_DENOM)
-
-#define JIT_INFO_TABLE_LOW_WATERMARK(n) ((n) / 2)
-#define JIT_INFO_TABLE_HIGH_WATERMARK(n) ((n) * 5 / 6)
-
-#define JIT_INFO_TOMBSTONE_MARKER ((MonoMethod*)NULL)
-#define IS_JIT_INFO_TOMBSTONE(ji) ((ji)->d.method == JIT_INFO_TOMBSTONE_MARKER)
-
-#define JIT_INFO_TABLE_HAZARD_INDEX 0
-#define JIT_INFO_HAZARD_INDEX 1
-
-static int
-jit_info_table_num_elements (MonoJitInfoTable *table)
-{
- int i;
- int num_elements = 0;
-
- for (i = 0; i < table->num_chunks; ++i) {
- MonoJitInfoTableChunk *chunk = table->chunks [i];
- int chunk_num_elements = chunk->num_elements;
- int j;
-
- for (j = 0; j < chunk_num_elements; ++j) {
- if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j]))
- ++num_elements;
- }
- }
-
- return num_elements;
-}
-
-static MonoJitInfoTableChunk*
-jit_info_table_new_chunk (void)
-{
- MonoJitInfoTableChunk *chunk = g_new0 (MonoJitInfoTableChunk, 1);
- chunk->refcount = 1;
-
- return chunk;
-}
-
-static MonoJitInfoTable *
-jit_info_table_new (MonoDomain *domain)
-{
- MonoJitInfoTable *table = g_malloc0 (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*));
-
- table->domain = domain;
- table->num_chunks = 1;
- table->chunks [0] = jit_info_table_new_chunk ();
-
- return table;
-}
-
-static void
-jit_info_table_free (MonoJitInfoTable *table)
-{
- int i;
- int num_chunks = table->num_chunks;
- MonoDomain *domain = table->domain;
-
- mono_domain_lock (domain);
-
- table->domain->num_jit_info_tables--;
- if (table->domain->num_jit_info_tables <= 1) {
- GSList *list;
-
- for (list = table->domain->jit_info_free_queue; list; list = list->next)
- g_free (list->data);
-
- g_slist_free (table->domain->jit_info_free_queue);
- table->domain->jit_info_free_queue = NULL;
- }
-
- /* At this point we assume that there are no other threads
- still accessing the table, so we don't have to worry about
- hazardous pointers. */
-
- for (i = 0; i < num_chunks; ++i) {
- MonoJitInfoTableChunk *chunk = table->chunks [i];
- int num_elements;
- int j;
-
- if (--chunk->refcount > 0)
- continue;
-
- num_elements = chunk->num_elements;
- for (j = 0; j < num_elements; ++j) {
- MonoJitInfo *ji = chunk->data [j];
-
- if (IS_JIT_INFO_TOMBSTONE (ji))
- g_free (ji);
- }
-
- g_free (chunk);
- }
-
- mono_domain_unlock (domain);
-
- g_free (table);
-}
-
-/* The jit_info_table is sorted in ascending order by the end
- * addresses of the compiled methods. The reason why we have to do
- * this is that once we introduce tombstones, it becomes possible for
- * code ranges to overlap, and if we sort by code start and insert at
- * the back of the table, we cannot guarantee that we won't overlook
- * an entry.
- *
- * There are actually two possible ways to do the sorting and
- * inserting which work with our lock-free mechanism:
- *
- * 1. Sort by start address and insert at the front. When looking for
- * an entry, find the last one with a start address lower than the one
- * you're looking for, then work your way to the front of the table.
- *
- * 2. Sort by end address and insert at the back. When looking for an
- * entry, find the first one with an end address higher than the one
- * you're looking for, then work your way to the end of the table.
- *
- * We chose the latter out of convenience.
- */
-static int
-jit_info_table_index (MonoJitInfoTable *table, gint8 *addr)
-{
- int left = 0, right = table->num_chunks;
-
- g_assert (left < right);
-
- do {
- int pos = (left + right) / 2;
- MonoJitInfoTableChunk *chunk = table->chunks [pos];
-
- if (addr < chunk->last_code_end)
- right = pos;
- else
- left = pos + 1;
- } while (left < right);
- g_assert (left == right);
-
- if (left >= table->num_chunks)
- return table->num_chunks - 1;
- return left;
-}
-
-static int
-jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointers *hp, gint8 *addr)
-{
- int left = 0, right = chunk->num_elements;
-
- while (left < right) {
- int pos = (left + right) / 2;
- MonoJitInfo *ji = get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
- gint8 *code_end = (gint8*)ji->code_start + ji->code_size;
-
- if (addr < code_end)
- right = pos;
- else
- left = pos + 1;
- }
- g_assert (left == right);
-
- return left;
-}
-
-static MonoJitInfo*
-jit_info_table_find (MonoJitInfoTable *table, MonoThreadHazardPointers *hp, gint8 *addr)
-{
- MonoJitInfo *ji;
- int chunk_pos, pos;
-
- chunk_pos = jit_info_table_index (table, (gint8*)addr);
- g_assert (chunk_pos < table->num_chunks);
-
- pos = jit_info_table_chunk_index (table->chunks [chunk_pos], hp, (gint8*)addr);
-
- /* We now have a position that's very close to that of the
- first element whose end address is higher than the one
- we're looking for. If we don't have the exact position,
- then we have a position below that one, so we'll just
- search upward until we find our element. */
- do {
- MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos];
-
- while (pos < chunk->num_elements) {
- ji = get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
-
- ++pos;
-
- if (IS_JIT_INFO_TOMBSTONE (ji)) {
- mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
- continue;
- }
- if ((gint8*)addr >= (gint8*)ji->code_start
- && (gint8*)addr < (gint8*)ji->code_start + ji->code_size) {
- mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
- return ji;
- }
-
- /* If we find a non-tombstone element which is already
- beyond what we're looking for, we have to end the
- search. */
- if ((gint8*)addr < (gint8*)ji->code_start)
- goto not_found;
- }
-
- ++chunk_pos;
- pos = 0;
- } while (chunk_pos < table->num_chunks);
-
- not_found:
- if (hp)
- mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
- return NULL;
-}
-
-/*
- * mono_jit_info_table_find_internal:
- *
- * If TRY_AOT is FALSE, avoid loading information for missing methods from AOT images, which is currently not async safe.
- * In this case, only those AOT methods will be found whose jit info is already loaded.
- * ASYNC SAFETY: When called in an async context (mono_thread_info_is_async_context ()), this is async safe.
- * In this case, the returned MonoJitInfo might not have metadata information, in particular,
- * mono_jit_info_get_method () could fail.
- */
-MonoJitInfo*
-mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot)
-{
- MonoJitInfoTable *table;
- MonoJitInfo *ji, *module_ji;
- MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
-
- ++mono_stats.jit_info_table_lookup_count;
-
- /* First we have to get the domain's jit_info_table. This is
- complicated by the fact that a writer might substitute a
- new table and free the old one. What the writer guarantees
- us is that it looks at the hazard pointers after it has
- changed the jit_info_table pointer. So, if we guard the
- table by a hazard pointer and make sure that the pointer is
- still there after we've made it hazardous, we don't have to
- worry about the writer freeing the table. */
- table = get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX);
-
- ji = jit_info_table_find (table, hp, (gint8*)addr);
- if (hp)
- mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
- if (ji)
- return ji;
-
- /* Maybe its an AOT module */
- if (try_aot && mono_root_domain && mono_root_domain->aot_modules) {
- table = get_hazardous_pointer ((gpointer volatile*)&mono_root_domain->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX);
- module_ji = jit_info_table_find (table, hp, (gint8*)addr);
- if (module_ji)
- ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr);
- if (hp)
- mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
- }
-
- return ji;
-}
-
-MonoJitInfo*
-mono_jit_info_table_find (MonoDomain *domain, char *addr)
-{
- return mono_jit_info_table_find_internal (domain, addr, TRUE);
-}
-
-static G_GNUC_UNUSED void
-jit_info_table_check (MonoJitInfoTable *table)
-{
- int i;
-
- for (i = 0; i < table->num_chunks; ++i) {
- MonoJitInfoTableChunk *chunk = table->chunks [i];
- int j;
-
- g_assert (chunk->refcount > 0 /* && chunk->refcount <= 8 */);
- if (chunk->refcount > 10)
- printf("warning: chunk refcount is %d\n", chunk->refcount);
- g_assert (chunk->num_elements <= MONO_JIT_INFO_TABLE_CHUNK_SIZE);
-
- for (j = 0; j < chunk->num_elements; ++j) {
- MonoJitInfo *this = chunk->data [j];
- MonoJitInfo *next;
-
- g_assert ((gint8*)this->code_start + this->code_size <= chunk->last_code_end);
-
- if (j < chunk->num_elements - 1)
- next = chunk->data [j + 1];
- else if (i < table->num_chunks - 1) {
- int k;
-
- for (k = i + 1; k < table->num_chunks; ++k)
- if (table->chunks [k]->num_elements > 0)
- break;
-
- if (k >= table->num_chunks)
- return;
-
- g_assert (table->chunks [k]->num_elements > 0);
- next = table->chunks [k]->data [0];
- } else
- return;
-
- g_assert ((gint8*)this->code_start + this->code_size <= (gint8*)next->code_start + next->code_size);
- }
- }
-}
-
-static MonoJitInfoTable*
-jit_info_table_realloc (MonoJitInfoTable *old)
-{
- int i;
- int num_elements = jit_info_table_num_elements (old);
- int required_size;
- int num_chunks;
- int new_chunk, new_element;
- MonoJitInfoTable *new;
-
- /* number of needed places for elements needed */
- required_size = (int)((long)num_elements * JIT_INFO_TABLE_FILL_RATIO_DENOM / JIT_INFO_TABLE_FILL_RATIO_NOM);
- num_chunks = (required_size + MONO_JIT_INFO_TABLE_CHUNK_SIZE - 1) / MONO_JIT_INFO_TABLE_CHUNK_SIZE;
- if (num_chunks == 0) {
- g_assert (num_elements == 0);
- return jit_info_table_new (old->domain);
- }
- g_assert (num_chunks > 0);
-
- new = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * num_chunks);
- new->domain = old->domain;
- new->num_chunks = num_chunks;
-
- for (i = 0; i < num_chunks; ++i)
- new->chunks [i] = jit_info_table_new_chunk ();
-
- new_chunk = 0;
- new_element = 0;
- for (i = 0; i < old->num_chunks; ++i) {
- MonoJitInfoTableChunk *chunk = old->chunks [i];
- int chunk_num_elements = chunk->num_elements;
- int j;
-
- for (j = 0; j < chunk_num_elements; ++j) {
- if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j])) {
- g_assert (new_chunk < num_chunks);
- new->chunks [new_chunk]->data [new_element] = chunk->data [j];
- if (++new_element >= JIT_INFO_TABLE_FILLED_NUM_ELEMENTS) {
- new->chunks [new_chunk]->num_elements = new_element;
- ++new_chunk;
- new_element = 0;
- }
- }
- }
- }
-
- if (new_chunk < num_chunks) {
- g_assert (new_chunk == num_chunks - 1);
- new->chunks [new_chunk]->num_elements = new_element;
- g_assert (new->chunks [new_chunk]->num_elements > 0);
- }
-
- for (i = 0; i < num_chunks; ++i) {
- MonoJitInfoTableChunk *chunk = new->chunks [i];
- MonoJitInfo *ji = chunk->data [chunk->num_elements - 1];
-
- new->chunks [i]->last_code_end = (gint8*)ji->code_start + ji->code_size;
- }
-
- return new;
-}
-
-static void
-jit_info_table_split_chunk (MonoJitInfoTableChunk *chunk, MonoJitInfoTableChunk **new1p, MonoJitInfoTableChunk **new2p)
-{
- MonoJitInfoTableChunk *new1 = jit_info_table_new_chunk ();
- MonoJitInfoTableChunk *new2 = jit_info_table_new_chunk ();
-
- g_assert (chunk->num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE);
-
- new1->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE / 2;
- new2->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE - new1->num_elements;
-
- memcpy ((void*)new1->data, (void*)chunk->data, sizeof (MonoJitInfo*) * new1->num_elements);
- memcpy ((void*)new2->data, (void*)(chunk->data + new1->num_elements), sizeof (MonoJitInfo*) * new2->num_elements);
-
- new1->last_code_end = (gint8*)new1->data [new1->num_elements - 1]->code_start
- + new1->data [new1->num_elements - 1]->code_size;
- new2->last_code_end = (gint8*)new2->data [new2->num_elements - 1]->code_start
- + new2->data [new2->num_elements - 1]->code_size;
-
- *new1p = new1;
- *new2p = new2;
-}
-
-static MonoJitInfoTable*
-jit_info_table_copy_and_split_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
-{
- MonoJitInfoTable *new_table = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
- + sizeof (MonoJitInfoTableChunk*) * (table->num_chunks + 1));
- int i, j;
-
- new_table->domain = table->domain;
- new_table->num_chunks = table->num_chunks + 1;
-
- j = 0;
- for (i = 0; i < table->num_chunks; ++i) {
- if (table->chunks [i] == chunk) {
- jit_info_table_split_chunk (chunk, &new_table->chunks [j], &new_table->chunks [j + 1]);
- j += 2;
- } else {
- new_table->chunks [j] = table->chunks [i];
- ++new_table->chunks [j]->refcount;
- ++j;
- }
- }
-
- g_assert (j == new_table->num_chunks);
-
- return new_table;
-}
-
-static MonoJitInfoTableChunk*
-jit_info_table_purify_chunk (MonoJitInfoTableChunk *old)
-{
- MonoJitInfoTableChunk *new = jit_info_table_new_chunk ();
- int i, j;
-
- j = 0;
- for (i = 0; i < old->num_elements; ++i) {
- if (!IS_JIT_INFO_TOMBSTONE (old->data [i]))
- new->data [j++] = old->data [i];
- }
-
- new->num_elements = j;
- if (new->num_elements > 0)
- new->last_code_end = (gint8*)new->data [j - 1]->code_start + new->data [j - 1]->code_size;
- else
- new->last_code_end = old->last_code_end;
-
- return new;
-}
-
-static MonoJitInfoTable*
-jit_info_table_copy_and_purify_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
-{
- MonoJitInfoTable *new_table = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
- + sizeof (MonoJitInfoTableChunk*) * table->num_chunks);
- int i, j;
-
- new_table->domain = table->domain;
- new_table->num_chunks = table->num_chunks;
-
- j = 0;
- for (i = 0; i < table->num_chunks; ++i) {
- if (table->chunks [i] == chunk)
- new_table->chunks [j++] = jit_info_table_purify_chunk (table->chunks [i]);
- else {
- new_table->chunks [j] = table->chunks [i];
- ++new_table->chunks [j]->refcount;
- ++j;
- }
- }
-
- g_assert (j == new_table->num_chunks);
-
- return new_table;
-}
-
-/* As we add an element to the table the case can arise that the chunk
- * to which we need to add is already full. In that case we have to
- * allocate a new table and do something about that chunk. We have
- * several strategies:
- *
- * If the number of elements in the table is below the low watermark
- * or above the high watermark, we reallocate the whole table.
- * Otherwise we only concern ourselves with the overflowing chunk:
- *
- * If there are no tombstones in the chunk then we split the chunk in
- * two, each half full.
- *
- * If the chunk does contain tombstones, we just make a new copy of
- * the chunk without the tombstones, which will have room for at least
- * the one element we have to add.
- */
-static MonoJitInfoTable*
-jit_info_table_chunk_overflow (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
-{
- int num_elements = jit_info_table_num_elements (table);
- int i;
-
- if (num_elements < JIT_INFO_TABLE_LOW_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)
- || num_elements > JIT_INFO_TABLE_HIGH_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)) {
- //printf ("reallocing table\n");
- return jit_info_table_realloc (table);
- }
-
- /* count the number of non-tombstone elements in the chunk */
- num_elements = 0;
- for (i = 0; i < chunk->num_elements; ++i) {
- if (!IS_JIT_INFO_TOMBSTONE (chunk->data [i]))
- ++num_elements;
- }
-
- if (num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
- //printf ("splitting chunk\n");
- return jit_info_table_copy_and_split_chunk (table, chunk);
- }
-
- //printf ("purifying chunk\n");
- return jit_info_table_copy_and_purify_chunk (table, chunk);
-}
-
-/* We add elements to the table by first making space for them by
- * shifting the elements at the back to the right, one at a time.
- * This results in duplicate entries during the process, but during
- * all the time the table is in a sorted state. Also, when an element
- * is replaced by another one, the element that replaces it has an end
- * address that is equal to or lower than that of the replaced
- * element. That property is necessary to guarantee that when
- * searching for an element we end up at a position not higher than
- * the one we're looking for (i.e. we either find the element directly
- * or we end up to the left of it).
- */
-static void
-jit_info_table_add (MonoDomain *domain, MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji)
-{
- MonoJitInfoTable *table;
- MonoJitInfoTableChunk *chunk;
- int chunk_pos, pos;
- int num_elements;
- int i;
-
- table = *table_ptr;
-
- restart:
- chunk_pos = jit_info_table_index (table, (gint8*)ji->code_start + ji->code_size);
- g_assert (chunk_pos < table->num_chunks);
- chunk = table->chunks [chunk_pos];
-
- if (chunk->num_elements >= MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
- MonoJitInfoTable *new_table = jit_info_table_chunk_overflow (table, chunk);
-
- /* Debugging code, should be removed. */
- //jit_info_table_check (new_table);
-
- *table_ptr = new_table;
- mono_memory_barrier ();
- domain->num_jit_info_tables++;
- mono_thread_hazardous_free_or_queue (table, (MonoHazardousFreeFunc)jit_info_table_free, TRUE, FALSE);
- table = new_table;
-
- goto restart;
- }
-
- /* Debugging code, should be removed. */
- //jit_info_table_check (table);
-
- num_elements = chunk->num_elements;
-
- pos = jit_info_table_chunk_index (chunk, NULL, (gint8*)ji->code_start + ji->code_size);
-
- /* First we need to size up the chunk by one, by copying the
- last item, or inserting the first one, if the table is
- empty. */
- if (num_elements > 0)
- chunk->data [num_elements] = chunk->data [num_elements - 1];
- else
- chunk->data [0] = ji;
- mono_memory_write_barrier ();
- chunk->num_elements = ++num_elements;
-
- /* Shift the elements up one by one. */
- for (i = num_elements - 2; i >= pos; --i) {
- mono_memory_write_barrier ();
- chunk->data [i + 1] = chunk->data [i];
- }
-
- /* Now we have room and can insert the new item. */
- mono_memory_write_barrier ();
- chunk->data [pos] = ji;
-
- /* Set the high code end address chunk entry. */
- chunk->last_code_end = (gint8*)chunk->data [chunk->num_elements - 1]->code_start
- + chunk->data [chunk->num_elements - 1]->code_size;
-
- /* Debugging code, should be removed. */
- //jit_info_table_check (table);
-}
-
-void
-mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji)
-{
- g_assert (ji->d.method != NULL);
-
- mono_domain_lock (domain);
-
- ++mono_stats.jit_info_table_insert_count;
-
- jit_info_table_add (domain, &domain->jit_info_table, ji);
-
- mono_domain_unlock (domain);
-}
-
-static MonoJitInfo*
-mono_jit_info_make_tombstone (MonoJitInfo *ji)
-{
- MonoJitInfo *tombstone = g_new0 (MonoJitInfo, 1);
-
- tombstone->code_start = ji->code_start;
- tombstone->code_size = ji->code_size;
- tombstone->d.method = JIT_INFO_TOMBSTONE_MARKER;
-
- return tombstone;
-}
-
-/*
- * LOCKING: domain lock
- */
-static void
-mono_jit_info_free_or_queue (MonoDomain *domain, MonoJitInfo *ji)
-{
- if (domain->num_jit_info_tables <= 1) {
- /* Can it actually happen that we only have one table
- but ji is still hazardous? */
- mono_thread_hazardous_free_or_queue (ji, g_free, TRUE, FALSE);
- } else {
- domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji);
- }
-}
-
-static void
-jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji)
-{
- MonoJitInfoTableChunk *chunk;
- gpointer start = ji->code_start;
- int chunk_pos, pos;
-
- chunk_pos = jit_info_table_index (table, start);
- g_assert (chunk_pos < table->num_chunks);
-
- pos = jit_info_table_chunk_index (table->chunks [chunk_pos], NULL, start);
-
- do {
- chunk = table->chunks [chunk_pos];
-
- while (pos < chunk->num_elements) {
- if (chunk->data [pos] == ji)
- goto found;
-
- g_assert (IS_JIT_INFO_TOMBSTONE (chunk->data [pos]));
- g_assert ((guint8*)chunk->data [pos]->code_start + chunk->data [pos]->code_size
- <= (guint8*)ji->code_start + ji->code_size);
-
- ++pos;
- }
-
- ++chunk_pos;
- pos = 0;
- } while (chunk_pos < table->num_chunks);
-
- found:
- g_assert (chunk->data [pos] == ji);
-
- chunk->data [pos] = mono_jit_info_make_tombstone (ji);
-
- /* Debugging code, should be removed. */
- //jit_info_table_check (table);
-}
-
-void
-mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji)
-{
- MonoJitInfoTable *table;
-
- mono_domain_lock (domain);
- table = domain->jit_info_table;
-
- ++mono_stats.jit_info_table_remove_count;
-
- jit_info_table_remove (table, ji);
-
- mono_jit_info_free_or_queue (domain, ji);
-
- mono_domain_unlock (domain);
-}
-
-void
-mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end)
-{
- MonoJitInfo *ji;
-
- g_assert (mono_root_domain);
- mono_domain_lock (mono_root_domain);
-
- /*
- * We reuse MonoJitInfoTable to store AOT module info,
- * this gives us async-safe lookup.
- */
- if (!mono_root_domain->aot_modules) {
- mono_root_domain->num_jit_info_tables ++;
- mono_root_domain->aot_modules = jit_info_table_new (mono_root_domain);
- }
-
- ji = g_new0 (MonoJitInfo, 1);
- ji->d.image = image;
- ji->code_start = start;
- ji->code_size = (guint8*)end - (guint8*)start;
- jit_info_table_add (mono_root_domain, &mono_root_domain->aot_modules, ji);
-
- mono_domain_unlock (mono_root_domain);
-}
-
-void
-mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func)
-{
- jit_info_find_in_aot_func = func;
-}
-
-gpointer
-mono_jit_info_get_code_start (MonoJitInfo* ji)
-{
- return ji->code_start;
-}
-
-int
-mono_jit_info_get_code_size (MonoJitInfo* ji)
-{
- return ji->code_size;
-}
-
-MonoMethod*
-mono_jit_info_get_method (MonoJitInfo* ji)
-{
- g_assert (!ji->async);
- return ji->d.method;
-}
-
-static gpointer
-jit_info_key_extract (gpointer value)
-{
- MonoJitInfo *info = (MonoJitInfo*)value;
-
- return info->d.method;
-}
-
-static gpointer*
-jit_info_next_value (gpointer value)
-{
- MonoJitInfo *info = (MonoJitInfo*)value;
-
- return (gpointer*)&info->next_jit_code_hash;
-}
-
-void
-mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash)
-{
- mono_internal_hash_table_init (jit_code_hash,
- mono_aligned_addr_hash,
- jit_info_key_extract,
- jit_info_next_value);
-}
-
-MonoGenericJitInfo*
-mono_jit_info_get_generic_jit_info (MonoJitInfo *ji)
-{
- if (ji->has_generic_jit_info)
- return (MonoGenericJitInfo*)&ji->clauses [ji->num_clauses];
- else
- return NULL;
-}
-
-/*
- * mono_jit_info_get_generic_sharing_context:
- * @ji: a jit info
- *
- * Returns the jit info's generic sharing context, or NULL if it
- * doesn't have one.
- */
-MonoGenericSharingContext*
-mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji)
-{
- MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
-
- if (gi)
- return gi->generic_sharing_context;
- else
- return NULL;
-}
-
-/*
- * mono_jit_info_set_generic_sharing_context:
- * @ji: a jit info
- * @gsctx: a generic sharing context
- *
- * Sets the jit info's generic sharing context. The jit info must
- * have memory allocated for the context.
- */
-void
-mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx)
-{
- MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
-
- g_assert (gi);
-
- gi->generic_sharing_context = gsctx;
-}
-
-MonoTryBlockHoleTableJitInfo*
-mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji)
-{
- if (ji->has_try_block_holes) {
- char *ptr = (char*)&ji->clauses [ji->num_clauses];
- if (ji->has_generic_jit_info)
- ptr += sizeof (MonoGenericJitInfo);
- return (MonoTryBlockHoleTableJitInfo*)ptr;
- } else {
- return NULL;
- }
-}
-
-static int
-try_block_hole_table_size (MonoJitInfo *ji)
-{
- MonoTryBlockHoleTableJitInfo *table;
-
- table = mono_jit_info_get_try_block_hole_table_info (ji);
- g_assert (table);
- return sizeof (MonoTryBlockHoleTableJitInfo) + table->num_holes * sizeof (MonoTryBlockHoleJitInfo);
-}
-
-MonoArchEHJitInfo*
-mono_jit_info_get_arch_eh_info (MonoJitInfo *ji)
-{
- if (ji->has_arch_eh_info) {
- char *ptr = (char*)&ji->clauses [ji->num_clauses];
- if (ji->has_generic_jit_info)
- ptr += sizeof (MonoGenericJitInfo);
- if (ji->has_try_block_holes)
- ptr += try_block_hole_table_size (ji);
- return (MonoArchEHJitInfo*)ptr;
- } else {
- return NULL;
- }
-}
-
-MonoMethodCasInfo*
-mono_jit_info_get_cas_info (MonoJitInfo *ji)
-{
- if (ji->has_cas_info) {
- char *ptr = (char*)&ji->clauses [ji->num_clauses];
- if (ji->has_generic_jit_info)
- ptr += sizeof (MonoGenericJitInfo);
- if (ji->has_try_block_holes)
- ptr += try_block_hole_table_size (ji);
- if (ji->has_arch_eh_info)
- ptr += sizeof (MonoArchEHJitInfo);
- return (MonoMethodCasInfo*)ptr;
- } else {
- return NULL;
- }
-}
-
#define ALIGN_TO(val,align) ((((guint64)val) + ((align) - 1)) & ~((align) - 1))
#define ALIGN_PTR_TO(ptr,align) (gpointer)((((gssize)(ptr)) + (align - 1)) & (~(align - 1)))
mono_jit_code_hash_init (&domain->jit_code_hash);
domain->ldstr_table = mono_g_hash_table_new_type ((GHashFunc)mono_string_hash, (GCompareFunc)mono_string_equal, MONO_HASH_KEY_VALUE_GC);
domain->num_jit_info_tables = 1;
- domain->jit_info_table = jit_info_table_new (domain);
+ domain->jit_info_table = mono_jit_info_table_new (domain);
domain->jit_info_free_queue = NULL;
domain->finalizable_objects_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
domain->ftnptrs_hash = g_hash_table_new (mono_aligned_addr_hash, NULL);
*/
mono_thread_hazardous_try_free_all ();
if (domain->aot_modules)
- jit_info_table_free (domain->aot_modules);
+ mono_jit_info_table_free (domain->aot_modules);
g_assert (domain->num_jit_info_tables == 1);
- jit_info_table_free (domain->jit_info_table);
+ mono_jit_info_table_free (domain->jit_info_table);
domain->jit_info_table = NULL;
g_assert (!domain->jit_info_free_queue);
#include <glib.h>
#include <mono/metadata/object-internals.h>
#include <mono/metadata/threads-types.h>
-#include <mono/metadata/sgen-conf.h>
#include <mono/utils/gc_wrapper.h>
typedef struct {
int major_gc_count;
long long minor_gc_time;
long long major_gc_time;
-#ifdef HEAVY_STATISTICS
- unsigned long long gray_queue_section_alloc;
- unsigned long long gray_queue_section_free;
- unsigned long long gray_queue_enqueue_fast_path;
- unsigned long long gray_queue_dequeue_fast_path;
- unsigned long long gray_queue_enqueue_slow_path;
- unsigned long long gray_queue_dequeue_slow_path;
-#endif
+ long long major_gc_time_concurrent;
} GCStats;
#define mono_domain_finalizers_lock(domain) mono_mutex_lock (&(domain)->finalizable_objects_hash_lock);
*/
gboolean mono_gc_is_disabled (void) MONO_INTERNAL;
+void mono_gc_set_string_length (MonoString *str, gint32 new_length) MONO_INTERNAL;
+
#if defined(__MACH__)
void mono_gc_register_mach_exception_thread (pthread_t thread) MONO_INTERNAL;
pthread_t mono_gc_get_mach_exception_thread (void) MONO_INTERNAL;
mono_counters_register ("Major GC collections", MONO_COUNTER_GC | MONO_COUNTER_INT, &gc_stats.major_gc_count);
mono_counters_register ("Minor GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.minor_gc_time);
mono_counters_register ("Major GC time", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time);
-#ifdef HEAVY_STATISTICS
- mono_counters_register ("Gray Queue alloc section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &gc_stats.gray_queue_section_alloc);
- mono_counters_register ("Gray Queue free section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &gc_stats.gray_queue_section_free);
- mono_counters_register ("Gray Queue enqueue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &gc_stats.gray_queue_enqueue_fast_path);
- mono_counters_register ("Gray Queue dequeue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &gc_stats.gray_queue_dequeue_fast_path);
- mono_counters_register ("Gray Queue enqueue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &gc_stats.gray_queue_enqueue_slow_path);
- mono_counters_register ("Gray Queue dequeue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &gc_stats.gray_queue_dequeue_slow_path);
-#endif
+ mono_counters_register ("Major GC time concurrent", MONO_COUNTER_GC | MONO_COUNTER_LONG | MONO_COUNTER_TIME, &gc_stats.major_gc_time_concurrent);
mono_gc_base_init ();
ret = WaitForSingleObjectEx (gc_thread->handle, INFINITE, TRUE);
g_assert (ret == WAIT_OBJECT_0);
- mono_thread_join ((gpointer)gc_thread->tid);
+ mono_thread_join (MONO_UINT_TO_NATIVE_THREAD_ID (gc_thread->tid));
}
}
gc_thread = NULL;
ICALL(STRING_9, "InternalAllocateStr", ves_icall_System_String_InternalAllocateStr)
ICALL(STRING_10, "InternalIntern", ves_icall_System_String_InternalIntern)
ICALL(STRING_11, "InternalIsInterned", ves_icall_System_String_InternalIsInterned)
+ICALL(STRING_12, "InternalSetLength", ves_icall_System_String_InternalSetLength)
ICALL_TYPE(TENC, "System.Text.Encoding", TENC_1)
ICALL(TENC_1, "InternalCodePage", ves_icall_System_Text_Encoding_InternalCodePage)
--- /dev/null
+/*
+ * jit-info.c: MonoJitInfo functionality
+ *
+ * Author:
+ * Dietmar Maurer (dietmar@ximian.com)
+ * Patrik Torstensson
+ *
+ * Copyright 2001-2003 Ximian, Inc (http://www.ximian.com)
+ * Copyright 2004-2009 Novell, Inc (http://www.novell.com)
+ * Copyright 2011-2012 Xamarin, Inc (http://www.xamarin.com)
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include <mono/metadata/gc-internal.h>
+
+#include <mono/utils/atomic.h>
+#include <mono/utils/mono-compiler.h>
+#include <mono/utils/mono-logger-internal.h>
+#include <mono/utils/mono-membar.h>
+#include <mono/utils/mono-counters.h>
+#include <mono/utils/hazard-pointer.h>
+#include <mono/utils/mono-tls.h>
+#include <mono/utils/mono-mmap.h>
+#include <mono/utils/mono-threads.h>
+#include <mono/metadata/object.h>
+#include <mono/metadata/object-internals.h>
+#include <mono/metadata/domain-internals.h>
+#include <mono/metadata/class-internals.h>
+#include <mono/metadata/assembly.h>
+#include <mono/metadata/exception.h>
+#include <mono/metadata/metadata-internals.h>
+#include <mono/metadata/gc-internal.h>
+#include <mono/metadata/appdomain.h>
+#include <mono/metadata/mono-debug-debugger.h>
+#include <mono/metadata/mono-config.h>
+#include <mono/metadata/threads-types.h>
+#include <mono/metadata/runtime.h>
+#include <metadata/threads.h>
+#include <metadata/profiler-private.h>
+#include <mono/metadata/coree.h>
+
+static MonoJitInfoFindInAot jit_info_find_in_aot_func = NULL;
+
+#define JIT_INFO_TABLE_FILL_RATIO_NOM 3
+#define JIT_INFO_TABLE_FILL_RATIO_DENOM 4
+#define JIT_INFO_TABLE_FILLED_NUM_ELEMENTS (MONO_JIT_INFO_TABLE_CHUNK_SIZE * JIT_INFO_TABLE_FILL_RATIO_NOM / JIT_INFO_TABLE_FILL_RATIO_DENOM)
+
+#define JIT_INFO_TABLE_LOW_WATERMARK(n) ((n) / 2)
+#define JIT_INFO_TABLE_HIGH_WATERMARK(n) ((n) * 5 / 6)
+
+#define JIT_INFO_TOMBSTONE_MARKER ((MonoMethod*)NULL)
+#define IS_JIT_INFO_TOMBSTONE(ji) ((ji)->d.method == JIT_INFO_TOMBSTONE_MARKER)
+
+#define JIT_INFO_TABLE_HAZARD_INDEX 0
+#define JIT_INFO_HAZARD_INDEX 1
+
+static int
+jit_info_table_num_elements (MonoJitInfoTable *table)
+{
+ int i;
+ int num_elements = 0;
+
+ for (i = 0; i < table->num_chunks; ++i) {
+ MonoJitInfoTableChunk *chunk = table->chunks [i];
+ int chunk_num_elements = chunk->num_elements;
+ int j;
+
+ for (j = 0; j < chunk_num_elements; ++j) {
+ if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j]))
+ ++num_elements;
+ }
+ }
+
+ return num_elements;
+}
+
+static MonoJitInfoTableChunk*
+jit_info_table_new_chunk (void)
+{
+ MonoJitInfoTableChunk *chunk = g_new0 (MonoJitInfoTableChunk, 1);
+ chunk->refcount = 1;
+
+ return chunk;
+}
+
+MonoJitInfoTable *
+mono_jit_info_table_new (MonoDomain *domain)
+{
+ MonoJitInfoTable *table = g_malloc0 (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*));
+
+ table->domain = domain;
+ table->num_chunks = 1;
+ table->chunks [0] = jit_info_table_new_chunk ();
+
+ return table;
+}
+
+void
+mono_jit_info_table_free (MonoJitInfoTable *table)
+{
+ int i;
+ int num_chunks = table->num_chunks;
+ MonoDomain *domain = table->domain;
+
+ mono_domain_lock (domain);
+
+ table->domain->num_jit_info_tables--;
+ if (table->domain->num_jit_info_tables <= 1) {
+ GSList *list;
+
+ for (list = table->domain->jit_info_free_queue; list; list = list->next)
+ g_free (list->data);
+
+ g_slist_free (table->domain->jit_info_free_queue);
+ table->domain->jit_info_free_queue = NULL;
+ }
+
+ /* At this point we assume that there are no other threads
+ still accessing the table, so we don't have to worry about
+ hazardous pointers. */
+
+ for (i = 0; i < num_chunks; ++i) {
+ MonoJitInfoTableChunk *chunk = table->chunks [i];
+ int num_elements;
+ int j;
+
+ if (--chunk->refcount > 0)
+ continue;
+
+ num_elements = chunk->num_elements;
+ for (j = 0; j < num_elements; ++j) {
+ MonoJitInfo *ji = chunk->data [j];
+
+ if (IS_JIT_INFO_TOMBSTONE (ji))
+ g_free (ji);
+ }
+
+ g_free (chunk);
+ }
+
+ mono_domain_unlock (domain);
+
+ g_free (table);
+}
+
+/* The jit_info_table is sorted in ascending order by the end
+ * addresses of the compiled methods. The reason why we have to do
+ * this is that once we introduce tombstones, it becomes possible for
+ * code ranges to overlap, and if we sort by code start and insert at
+ * the back of the table, we cannot guarantee that we won't overlook
+ * an entry.
+ *
+ * There are actually two possible ways to do the sorting and
+ * inserting which work with our lock-free mechanism:
+ *
+ * 1. Sort by start address and insert at the front. When looking for
+ * an entry, find the last one with a start address lower than the one
+ * you're looking for, then work your way to the front of the table.
+ *
+ * 2. Sort by end address and insert at the back. When looking for an
+ * entry, find the first one with an end address higher than the one
+ * you're looking for, then work your way to the end of the table.
+ *
+ * We chose the latter out of convenience.
+ */
+static int
+jit_info_table_index (MonoJitInfoTable *table, gint8 *addr)
+{
+ int left = 0, right = table->num_chunks;
+
+ g_assert (left < right);
+
+ do {
+ int pos = (left + right) / 2;
+ MonoJitInfoTableChunk *chunk = table->chunks [pos];
+
+ if (addr < chunk->last_code_end)
+ right = pos;
+ else
+ left = pos + 1;
+ } while (left < right);
+ g_assert (left == right);
+
+ if (left >= table->num_chunks)
+ return table->num_chunks - 1;
+ return left;
+}
+
+static int
+jit_info_table_chunk_index (MonoJitInfoTableChunk *chunk, MonoThreadHazardPointers *hp, gint8 *addr)
+{
+ int left = 0, right = chunk->num_elements;
+
+ while (left < right) {
+ int pos = (left + right) / 2;
+ MonoJitInfo *ji = get_hazardous_pointer((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
+ gint8 *code_end = (gint8*)ji->code_start + ji->code_size;
+
+ if (addr < code_end)
+ right = pos;
+ else
+ left = pos + 1;
+ }
+ g_assert (left == right);
+
+ return left;
+}
+
+static MonoJitInfo*
+jit_info_table_find (MonoJitInfoTable *table, MonoThreadHazardPointers *hp, gint8 *addr)
+{
+ MonoJitInfo *ji;
+ int chunk_pos, pos;
+
+ chunk_pos = jit_info_table_index (table, (gint8*)addr);
+ g_assert (chunk_pos < table->num_chunks);
+
+ pos = jit_info_table_chunk_index (table->chunks [chunk_pos], hp, (gint8*)addr);
+
+ /* We now have a position that's very close to that of the
+ first element whose end address is higher than the one
+ we're looking for. If we don't have the exact position,
+ then we have a position below that one, so we'll just
+ search upward until we find our element. */
+ do {
+ MonoJitInfoTableChunk *chunk = table->chunks [chunk_pos];
+
+ while (pos < chunk->num_elements) {
+ ji = get_hazardous_pointer ((gpointer volatile*)&chunk->data [pos], hp, JIT_INFO_HAZARD_INDEX);
+
+ ++pos;
+
+ if (IS_JIT_INFO_TOMBSTONE (ji)) {
+ mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
+ continue;
+ }
+ if ((gint8*)addr >= (gint8*)ji->code_start
+ && (gint8*)addr < (gint8*)ji->code_start + ji->code_size) {
+ mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
+ return ji;
+ }
+
+ /* If we find a non-tombstone element which is already
+ beyond what we're looking for, we have to end the
+ search. */
+ if ((gint8*)addr < (gint8*)ji->code_start)
+ goto not_found;
+ }
+
+ ++chunk_pos;
+ pos = 0;
+ } while (chunk_pos < table->num_chunks);
+
+ not_found:
+ if (hp)
+ mono_hazard_pointer_clear (hp, JIT_INFO_HAZARD_INDEX);
+ return NULL;
+}
+
+/*
+ * mono_jit_info_table_find_internal:
+ *
+ * If TRY_AOT is FALSE, avoid loading information for missing methods from AOT images, which is currently not async safe.
+ * In this case, only those AOT methods will be found whose jit info is already loaded.
+ * ASYNC SAFETY: When called in an async context (mono_thread_info_is_async_context ()), this is async safe.
+ * In this case, the returned MonoJitInfo might not have metadata information, in particular,
+ * mono_jit_info_get_method () could fail.
+ */
+MonoJitInfo*
+mono_jit_info_table_find_internal (MonoDomain *domain, char *addr, gboolean try_aot)
+{
+ MonoJitInfoTable *table;
+ MonoJitInfo *ji, *module_ji;
+ MonoThreadHazardPointers *hp = mono_hazard_pointer_get ();
+
+ ++mono_stats.jit_info_table_lookup_count;
+
+ /* First we have to get the domain's jit_info_table. This is
+ complicated by the fact that a writer might substitute a
+ new table and free the old one. What the writer guarantees
+ us is that it looks at the hazard pointers after it has
+ changed the jit_info_table pointer. So, if we guard the
+ table by a hazard pointer and make sure that the pointer is
+ still there after we've made it hazardous, we don't have to
+ worry about the writer freeing the table. */
+ table = get_hazardous_pointer ((gpointer volatile*)&domain->jit_info_table, hp, JIT_INFO_TABLE_HAZARD_INDEX);
+
+ ji = jit_info_table_find (table, hp, (gint8*)addr);
+ if (hp)
+ mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
+ if (ji)
+ return ji;
+
+ /* Maybe its an AOT module */
+ if (try_aot && mono_get_root_domain () && mono_get_root_domain ()->aot_modules) {
+ table = get_hazardous_pointer ((gpointer volatile*)&mono_get_root_domain ()->aot_modules, hp, JIT_INFO_TABLE_HAZARD_INDEX);
+ module_ji = jit_info_table_find (table, hp, (gint8*)addr);
+ if (module_ji)
+ ji = jit_info_find_in_aot_func (domain, module_ji->d.image, addr);
+ if (hp)
+ mono_hazard_pointer_clear (hp, JIT_INFO_TABLE_HAZARD_INDEX);
+ }
+
+ return ji;
+}
+
+MonoJitInfo*
+mono_jit_info_table_find (MonoDomain *domain, char *addr)
+{
+ return mono_jit_info_table_find_internal (domain, addr, TRUE);
+}
+
+static G_GNUC_UNUSED void
+jit_info_table_check (MonoJitInfoTable *table)
+{
+ int i;
+
+ for (i = 0; i < table->num_chunks; ++i) {
+ MonoJitInfoTableChunk *chunk = table->chunks [i];
+ int j;
+
+ g_assert (chunk->refcount > 0 /* && chunk->refcount <= 8 */);
+ if (chunk->refcount > 10)
+ printf("warning: chunk refcount is %d\n", chunk->refcount);
+ g_assert (chunk->num_elements <= MONO_JIT_INFO_TABLE_CHUNK_SIZE);
+
+ for (j = 0; j < chunk->num_elements; ++j) {
+ MonoJitInfo *this = chunk->data [j];
+ MonoJitInfo *next;
+
+ g_assert ((gint8*)this->code_start + this->code_size <= chunk->last_code_end);
+
+ if (j < chunk->num_elements - 1)
+ next = chunk->data [j + 1];
+ else if (i < table->num_chunks - 1) {
+ int k;
+
+ for (k = i + 1; k < table->num_chunks; ++k)
+ if (table->chunks [k]->num_elements > 0)
+ break;
+
+ if (k >= table->num_chunks)
+ return;
+
+ g_assert (table->chunks [k]->num_elements > 0);
+ next = table->chunks [k]->data [0];
+ } else
+ return;
+
+ g_assert ((gint8*)this->code_start + this->code_size <= (gint8*)next->code_start + next->code_size);
+ }
+ }
+}
+
+static MonoJitInfoTable*
+jit_info_table_realloc (MonoJitInfoTable *old)
+{
+ int i;
+ int num_elements = jit_info_table_num_elements (old);
+ int required_size;
+ int num_chunks;
+ int new_chunk, new_element;
+ MonoJitInfoTable *new;
+
+ /* number of needed places for elements needed */
+ required_size = (int)((long)num_elements * JIT_INFO_TABLE_FILL_RATIO_DENOM / JIT_INFO_TABLE_FILL_RATIO_NOM);
+ num_chunks = (required_size + MONO_JIT_INFO_TABLE_CHUNK_SIZE - 1) / MONO_JIT_INFO_TABLE_CHUNK_SIZE;
+ if (num_chunks == 0) {
+ g_assert (num_elements == 0);
+ return mono_jit_info_table_new (old->domain);
+ }
+ g_assert (num_chunks > 0);
+
+ new = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE + sizeof (MonoJitInfoTableChunk*) * num_chunks);
+ new->domain = old->domain;
+ new->num_chunks = num_chunks;
+
+ for (i = 0; i < num_chunks; ++i)
+ new->chunks [i] = jit_info_table_new_chunk ();
+
+ new_chunk = 0;
+ new_element = 0;
+ for (i = 0; i < old->num_chunks; ++i) {
+ MonoJitInfoTableChunk *chunk = old->chunks [i];
+ int chunk_num_elements = chunk->num_elements;
+ int j;
+
+ for (j = 0; j < chunk_num_elements; ++j) {
+ if (!IS_JIT_INFO_TOMBSTONE (chunk->data [j])) {
+ g_assert (new_chunk < num_chunks);
+ new->chunks [new_chunk]->data [new_element] = chunk->data [j];
+ if (++new_element >= JIT_INFO_TABLE_FILLED_NUM_ELEMENTS) {
+ new->chunks [new_chunk]->num_elements = new_element;
+ ++new_chunk;
+ new_element = 0;
+ }
+ }
+ }
+ }
+
+ if (new_chunk < num_chunks) {
+ g_assert (new_chunk == num_chunks - 1);
+ new->chunks [new_chunk]->num_elements = new_element;
+ g_assert (new->chunks [new_chunk]->num_elements > 0);
+ }
+
+ for (i = 0; i < num_chunks; ++i) {
+ MonoJitInfoTableChunk *chunk = new->chunks [i];
+ MonoJitInfo *ji = chunk->data [chunk->num_elements - 1];
+
+ new->chunks [i]->last_code_end = (gint8*)ji->code_start + ji->code_size;
+ }
+
+ return new;
+}
+
+static void
+jit_info_table_split_chunk (MonoJitInfoTableChunk *chunk, MonoJitInfoTableChunk **new1p, MonoJitInfoTableChunk **new2p)
+{
+ MonoJitInfoTableChunk *new1 = jit_info_table_new_chunk ();
+ MonoJitInfoTableChunk *new2 = jit_info_table_new_chunk ();
+
+ g_assert (chunk->num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE);
+
+ new1->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE / 2;
+ new2->num_elements = MONO_JIT_INFO_TABLE_CHUNK_SIZE - new1->num_elements;
+
+ memcpy ((void*)new1->data, (void*)chunk->data, sizeof (MonoJitInfo*) * new1->num_elements);
+ memcpy ((void*)new2->data, (void*)(chunk->data + new1->num_elements), sizeof (MonoJitInfo*) * new2->num_elements);
+
+ new1->last_code_end = (gint8*)new1->data [new1->num_elements - 1]->code_start
+ + new1->data [new1->num_elements - 1]->code_size;
+ new2->last_code_end = (gint8*)new2->data [new2->num_elements - 1]->code_start
+ + new2->data [new2->num_elements - 1]->code_size;
+
+ *new1p = new1;
+ *new2p = new2;
+}
+
+static MonoJitInfoTable*
+jit_info_table_copy_and_split_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
+{
+ MonoJitInfoTable *new_table = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
+ + sizeof (MonoJitInfoTableChunk*) * (table->num_chunks + 1));
+ int i, j;
+
+ new_table->domain = table->domain;
+ new_table->num_chunks = table->num_chunks + 1;
+
+ j = 0;
+ for (i = 0; i < table->num_chunks; ++i) {
+ if (table->chunks [i] == chunk) {
+ jit_info_table_split_chunk (chunk, &new_table->chunks [j], &new_table->chunks [j + 1]);
+ j += 2;
+ } else {
+ new_table->chunks [j] = table->chunks [i];
+ ++new_table->chunks [j]->refcount;
+ ++j;
+ }
+ }
+
+ g_assert (j == new_table->num_chunks);
+
+ return new_table;
+}
+
+static MonoJitInfoTableChunk*
+jit_info_table_purify_chunk (MonoJitInfoTableChunk *old)
+{
+ MonoJitInfoTableChunk *new = jit_info_table_new_chunk ();
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < old->num_elements; ++i) {
+ if (!IS_JIT_INFO_TOMBSTONE (old->data [i]))
+ new->data [j++] = old->data [i];
+ }
+
+ new->num_elements = j;
+ if (new->num_elements > 0)
+ new->last_code_end = (gint8*)new->data [j - 1]->code_start + new->data [j - 1]->code_size;
+ else
+ new->last_code_end = old->last_code_end;
+
+ return new;
+}
+
+static MonoJitInfoTable*
+jit_info_table_copy_and_purify_chunk (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
+{
+ MonoJitInfoTable *new_table = g_malloc (MONO_SIZEOF_JIT_INFO_TABLE
+ + sizeof (MonoJitInfoTableChunk*) * table->num_chunks);
+ int i, j;
+
+ new_table->domain = table->domain;
+ new_table->num_chunks = table->num_chunks;
+
+ j = 0;
+ for (i = 0; i < table->num_chunks; ++i) {
+ if (table->chunks [i] == chunk)
+ new_table->chunks [j++] = jit_info_table_purify_chunk (table->chunks [i]);
+ else {
+ new_table->chunks [j] = table->chunks [i];
+ ++new_table->chunks [j]->refcount;
+ ++j;
+ }
+ }
+
+ g_assert (j == new_table->num_chunks);
+
+ return new_table;
+}
+
+/* As we add an element to the table the case can arise that the chunk
+ * to which we need to add is already full. In that case we have to
+ * allocate a new table and do something about that chunk. We have
+ * several strategies:
+ *
+ * If the number of elements in the table is below the low watermark
+ * or above the high watermark, we reallocate the whole table.
+ * Otherwise we only concern ourselves with the overflowing chunk:
+ *
+ * If there are no tombstones in the chunk then we split the chunk in
+ * two, each half full.
+ *
+ * If the chunk does contain tombstones, we just make a new copy of
+ * the chunk without the tombstones, which will have room for at least
+ * the one element we have to add.
+ */
+static MonoJitInfoTable*
+jit_info_table_chunk_overflow (MonoJitInfoTable *table, MonoJitInfoTableChunk *chunk)
+{
+ int num_elements = jit_info_table_num_elements (table);
+ int i;
+
+ if (num_elements < JIT_INFO_TABLE_LOW_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)
+ || num_elements > JIT_INFO_TABLE_HIGH_WATERMARK (table->num_chunks * MONO_JIT_INFO_TABLE_CHUNK_SIZE)) {
+ //printf ("reallocing table\n");
+ return jit_info_table_realloc (table);
+ }
+
+ /* count the number of non-tombstone elements in the chunk */
+ num_elements = 0;
+ for (i = 0; i < chunk->num_elements; ++i) {
+ if (!IS_JIT_INFO_TOMBSTONE (chunk->data [i]))
+ ++num_elements;
+ }
+
+ if (num_elements == MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
+ //printf ("splitting chunk\n");
+ return jit_info_table_copy_and_split_chunk (table, chunk);
+ }
+
+ //printf ("purifying chunk\n");
+ return jit_info_table_copy_and_purify_chunk (table, chunk);
+}
+
+/* We add elements to the table by first making space for them by
+ * shifting the elements at the back to the right, one at a time.
+ * This results in duplicate entries during the process, but during
+ * all the time the table is in a sorted state. Also, when an element
+ * is replaced by another one, the element that replaces it has an end
+ * address that is equal to or lower than that of the replaced
+ * element. That property is necessary to guarantee that when
+ * searching for an element we end up at a position not higher than
+ * the one we're looking for (i.e. we either find the element directly
+ * or we end up to the left of it).
+ */
+static void
+jit_info_table_add (MonoDomain *domain, MonoJitInfoTable *volatile *table_ptr, MonoJitInfo *ji)
+{
+ MonoJitInfoTable *table;
+ MonoJitInfoTableChunk *chunk;
+ int chunk_pos, pos;
+ int num_elements;
+ int i;
+
+ table = *table_ptr;
+
+ restart:
+ chunk_pos = jit_info_table_index (table, (gint8*)ji->code_start + ji->code_size);
+ g_assert (chunk_pos < table->num_chunks);
+ chunk = table->chunks [chunk_pos];
+
+ if (chunk->num_elements >= MONO_JIT_INFO_TABLE_CHUNK_SIZE) {
+ MonoJitInfoTable *new_table = jit_info_table_chunk_overflow (table, chunk);
+
+ /* Debugging code, should be removed. */
+ //jit_info_table_check (new_table);
+
+ *table_ptr = new_table;
+ mono_memory_barrier ();
+ domain->num_jit_info_tables++;
+ mono_thread_hazardous_free_or_queue (table, (MonoHazardousFreeFunc)mono_jit_info_table_free, TRUE, FALSE);
+ table = new_table;
+
+ goto restart;
+ }
+
+ /* Debugging code, should be removed. */
+ //jit_info_table_check (table);
+
+ num_elements = chunk->num_elements;
+
+ pos = jit_info_table_chunk_index (chunk, NULL, (gint8*)ji->code_start + ji->code_size);
+
+ /* First we need to size up the chunk by one, by copying the
+ last item, or inserting the first one, if the table is
+ empty. */
+ if (num_elements > 0)
+ chunk->data [num_elements] = chunk->data [num_elements - 1];
+ else
+ chunk->data [0] = ji;
+ mono_memory_write_barrier ();
+ chunk->num_elements = ++num_elements;
+
+ /* Shift the elements up one by one. */
+ for (i = num_elements - 2; i >= pos; --i) {
+ mono_memory_write_barrier ();
+ chunk->data [i + 1] = chunk->data [i];
+ }
+
+ /* Now we have room and can insert the new item. */
+ mono_memory_write_barrier ();
+ chunk->data [pos] = ji;
+
+ /* Set the high code end address chunk entry. */
+ chunk->last_code_end = (gint8*)chunk->data [chunk->num_elements - 1]->code_start
+ + chunk->data [chunk->num_elements - 1]->code_size;
+
+ /* Debugging code, should be removed. */
+ //jit_info_table_check (table);
+}
+
+void
+mono_jit_info_table_add (MonoDomain *domain, MonoJitInfo *ji)
+{
+ g_assert (ji->d.method != NULL);
+
+ mono_domain_lock (domain);
+
+ ++mono_stats.jit_info_table_insert_count;
+
+ jit_info_table_add (domain, &domain->jit_info_table, ji);
+
+ mono_domain_unlock (domain);
+}
+
+static MonoJitInfo*
+mono_jit_info_make_tombstone (MonoJitInfo *ji)
+{
+ MonoJitInfo *tombstone = g_new0 (MonoJitInfo, 1);
+
+ tombstone->code_start = ji->code_start;
+ tombstone->code_size = ji->code_size;
+ tombstone->d.method = JIT_INFO_TOMBSTONE_MARKER;
+
+ return tombstone;
+}
+
+/*
+ * LOCKING: domain lock
+ */
+static void
+mono_jit_info_free_or_queue (MonoDomain *domain, MonoJitInfo *ji)
+{
+ if (domain->num_jit_info_tables <= 1) {
+ /* Can it actually happen that we only have one table
+ but ji is still hazardous? */
+ mono_thread_hazardous_free_or_queue (ji, g_free, TRUE, FALSE);
+ } else {
+ domain->jit_info_free_queue = g_slist_prepend (domain->jit_info_free_queue, ji);
+ }
+}
+
+static void
+jit_info_table_remove (MonoJitInfoTable *table, MonoJitInfo *ji)
+{
+ MonoJitInfoTableChunk *chunk;
+ gpointer start = ji->code_start;
+ int chunk_pos, pos;
+
+ chunk_pos = jit_info_table_index (table, start);
+ g_assert (chunk_pos < table->num_chunks);
+
+ pos = jit_info_table_chunk_index (table->chunks [chunk_pos], NULL, start);
+
+ do {
+ chunk = table->chunks [chunk_pos];
+
+ while (pos < chunk->num_elements) {
+ if (chunk->data [pos] == ji)
+ goto found;
+
+ g_assert (IS_JIT_INFO_TOMBSTONE (chunk->data [pos]));
+ g_assert ((guint8*)chunk->data [pos]->code_start + chunk->data [pos]->code_size
+ <= (guint8*)ji->code_start + ji->code_size);
+
+ ++pos;
+ }
+
+ ++chunk_pos;
+ pos = 0;
+ } while (chunk_pos < table->num_chunks);
+
+ found:
+ g_assert (chunk->data [pos] == ji);
+
+ chunk->data [pos] = mono_jit_info_make_tombstone (ji);
+
+ /* Debugging code, should be removed. */
+ //jit_info_table_check (table);
+}
+
+void
+mono_jit_info_table_remove (MonoDomain *domain, MonoJitInfo *ji)
+{
+ MonoJitInfoTable *table;
+
+ mono_domain_lock (domain);
+ table = domain->jit_info_table;
+
+ ++mono_stats.jit_info_table_remove_count;
+
+ jit_info_table_remove (table, ji);
+
+ mono_jit_info_free_or_queue (domain, ji);
+
+ mono_domain_unlock (domain);
+}
+
+void
+mono_jit_info_add_aot_module (MonoImage *image, gpointer start, gpointer end)
+{
+ MonoJitInfo *ji;
+ MonoDomain *domain = mono_get_root_domain ();
+
+ g_assert (domain);
+ mono_domain_lock (domain);
+
+ /*
+ * We reuse MonoJitInfoTable to store AOT module info,
+ * this gives us async-safe lookup.
+ */
+ if (!domain->aot_modules) {
+ domain->num_jit_info_tables ++;
+ domain->aot_modules = mono_jit_info_table_new (domain);
+ }
+
+ ji = g_new0 (MonoJitInfo, 1);
+ ji->d.image = image;
+ ji->code_start = start;
+ ji->code_size = (guint8*)end - (guint8*)start;
+ jit_info_table_add (domain, &domain->aot_modules, ji);
+
+ mono_domain_unlock (domain);
+}
+
+void
+mono_install_jit_info_find_in_aot (MonoJitInfoFindInAot func)
+{
+ jit_info_find_in_aot_func = func;
+}
+
+int
+mono_jit_info_size (MonoJitInfoFlags flags, int num_clauses, int num_holes)
+{
+ int size = MONO_SIZEOF_JIT_INFO;
+
+ size += num_clauses * sizeof (MonoJitExceptionInfo);
+ if (flags & JIT_INFO_HAS_CAS_INFO)
+ size += sizeof (MonoMethodCasInfo);
+ if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO)
+ size += sizeof (MonoGenericJitInfo);
+ if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES)
+ size += sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo);
+ if (flags & JIT_INFO_HAS_ARCH_EH_INFO)
+ size += sizeof (MonoArchEHJitInfo);
+ return size;
+}
+
+void
+mono_jit_info_init (MonoJitInfo *ji, MonoMethod *method, guint8 *code, int code_size,
+ MonoJitInfoFlags flags, int num_clauses, int num_holes)
+{
+ ji->d.method = method;
+ ji->code_start = code;
+ ji->code_size = code_size;
+ ji->num_clauses = num_clauses;
+ if (flags & JIT_INFO_HAS_CAS_INFO)
+ ji->has_cas_info = 1;
+ if (flags & JIT_INFO_HAS_GENERIC_JIT_INFO)
+ ji->has_generic_jit_info = 1;
+ if (flags & JIT_INFO_HAS_TRY_BLOCK_HOLES)
+ ji->has_try_block_holes = 1;
+ if (flags & JIT_INFO_HAS_ARCH_EH_INFO)
+ ji->has_arch_eh_info = 1;
+}
+
+gpointer
+mono_jit_info_get_code_start (MonoJitInfo* ji)
+{
+ return ji->code_start;
+}
+
+int
+mono_jit_info_get_code_size (MonoJitInfo* ji)
+{
+ return ji->code_size;
+}
+
+MonoMethod*
+mono_jit_info_get_method (MonoJitInfo* ji)
+{
+ g_assert (!ji->async);
+ return ji->d.method;
+}
+
+static gpointer
+jit_info_key_extract (gpointer value)
+{
+ MonoJitInfo *info = (MonoJitInfo*)value;
+
+ return info->d.method;
+}
+
+static gpointer*
+jit_info_next_value (gpointer value)
+{
+ MonoJitInfo *info = (MonoJitInfo*)value;
+
+ return (gpointer*)&info->next_jit_code_hash;
+}
+
+void
+mono_jit_code_hash_init (MonoInternalHashTable *jit_code_hash)
+{
+ mono_internal_hash_table_init (jit_code_hash,
+ mono_aligned_addr_hash,
+ jit_info_key_extract,
+ jit_info_next_value);
+}
+
+MonoGenericJitInfo*
+mono_jit_info_get_generic_jit_info (MonoJitInfo *ji)
+{
+ if (ji->has_generic_jit_info)
+ return (MonoGenericJitInfo*)&ji->clauses [ji->num_clauses];
+ else
+ return NULL;
+}
+
+/*
+ * mono_jit_info_get_generic_sharing_context:
+ * @ji: a jit info
+ *
+ * Returns the jit info's generic sharing context, or NULL if it
+ * doesn't have one.
+ */
+MonoGenericSharingContext*
+mono_jit_info_get_generic_sharing_context (MonoJitInfo *ji)
+{
+ MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
+
+ if (gi)
+ return gi->generic_sharing_context;
+ else
+ return NULL;
+}
+
+/*
+ * mono_jit_info_set_generic_sharing_context:
+ * @ji: a jit info
+ * @gsctx: a generic sharing context
+ *
+ * Sets the jit info's generic sharing context. The jit info must
+ * have memory allocated for the context.
+ */
+void
+mono_jit_info_set_generic_sharing_context (MonoJitInfo *ji, MonoGenericSharingContext *gsctx)
+{
+ MonoGenericJitInfo *gi = mono_jit_info_get_generic_jit_info (ji);
+
+ g_assert (gi);
+
+ gi->generic_sharing_context = gsctx;
+}
+
+MonoTryBlockHoleTableJitInfo*
+mono_jit_info_get_try_block_hole_table_info (MonoJitInfo *ji)
+{
+ if (ji->has_try_block_holes) {
+ char *ptr = (char*)&ji->clauses [ji->num_clauses];
+ if (ji->has_generic_jit_info)
+ ptr += sizeof (MonoGenericJitInfo);
+ return (MonoTryBlockHoleTableJitInfo*)ptr;
+ } else {
+ return NULL;
+ }
+}
+
+static int
+try_block_hole_table_size (MonoJitInfo *ji)
+{
+ MonoTryBlockHoleTableJitInfo *table;
+
+ table = mono_jit_info_get_try_block_hole_table_info (ji);
+ g_assert (table);
+ return sizeof (MonoTryBlockHoleTableJitInfo) + table->num_holes * sizeof (MonoTryBlockHoleJitInfo);
+}
+
+MonoArchEHJitInfo*
+mono_jit_info_get_arch_eh_info (MonoJitInfo *ji)
+{
+ if (ji->has_arch_eh_info) {
+ char *ptr = (char*)&ji->clauses [ji->num_clauses];
+ if (ji->has_generic_jit_info)
+ ptr += sizeof (MonoGenericJitInfo);
+ if (ji->has_try_block_holes)
+ ptr += try_block_hole_table_size (ji);
+ return (MonoArchEHJitInfo*)ptr;
+ } else {
+ return NULL;
+ }
+}
+
+MonoMethodCasInfo*
+mono_jit_info_get_cas_info (MonoJitInfo *ji)
+{
+ if (ji->has_cas_info) {
+ char *ptr = (char*)&ji->clauses [ji->num_clauses];
+ if (ji->has_generic_jit_info)
+ ptr += sizeof (MonoGenericJitInfo);
+ if (ji->has_try_block_holes)
+ ptr += try_block_hole_table_size (ji);
+ if (ji->has_arch_eh_info)
+ ptr += sizeof (MonoArchEHJitInfo);
+ return (MonoMethodCasInfo*)ptr;
+ } else {
+ return NULL;
+ }
+}
*/
GSList *apps;
GSList *assemblies;
+ char *aot_options;
} MonoAotCacheConfig;
#define MONO_SIZEOF_METHOD_SIGNATURE (sizeof (struct _MonoMethodSignature) - MONO_ZERO_LEN_ARRAY * SIZEOF_VOID_P)
for (i = 0; attribute_names [i]; ++i) {
if (!strcmp (attribute_names [i], "app")) {
config->apps = g_slist_prepend (config->apps, g_strdup (attribute_values [i]));
- return;
}
}
config->assemblies = g_slist_prepend (config->assemblies, g_strdup (part));
}
g_strfreev (parts);
+ } else if (!strcmp (attribute_names [i], "options")) {
+ config->aot_options = g_strdup (attribute_values [i]);
}
}
}
volatile gint tail;
MonoArray *queue;
gint32 mask;
+ gint32 suspended;
MonoSemType lock;
};
wsq = g_new0 (MonoWSQ, 1);
wsq->mask = INITIAL_LENGTH - 1;
+ wsq->suspended = 0;
MONO_GC_REGISTER_ROOT_SINGLE (wsq->queue);
root = mono_get_root_domain ();
wsq->queue = mono_array_new_cached (root, mono_defaults.object_class, INITIAL_LENGTH);
return wsq;
}
+gboolean
+mono_wsq_suspend (MonoWSQ *wsq)
+{
+ return InterlockedCompareExchange (&wsq->suspended, 1, 0) == 0;
+}
+
void
mono_wsq_destroy (MonoWSQ *wsq)
{
return FALSE;
}
+ if (wsq->suspended) {
+ WSQ_DEBUG ("local_push: wsq suspended\n");
+ return FALSE;
+ }
+
tail = wsq->tail;
if (tail < wsq->head + wsq->mask) {
mono_array_setref (wsq->queue, tail & wsq->mask, (MonoObject *) obj);
gboolean mono_wsq_local_pop (void **ptr) MONO_INTERNAL;
void mono_wsq_try_steal (MonoWSQ *wsq, void **ptr, guint32 ms_timeout) MONO_INTERNAL;
gint mono_wsq_count (MonoWSQ *wsq) MONO_INTERNAL;
+gboolean mono_wsq_suspend (MonoWSQ *wsq) MONO_INTERNAL;
G_END_DECLS
size_t size;
/* check for overflow */
- if (len < 0 || len > ((SIZE_MAX - sizeof (MonoString) - 2) / 2))
+ if (len < 0 || len > ((SIZE_MAX - offsetof (MonoString, chars) - 2) / 2))
mono_gc_out_of_memory (-1);
- size = (sizeof (MonoString) + ((len + 1) * 2));
+ size = (offsetof (MonoString, chars) + ((len + 1) * 2));
g_assert (size > 0);
vtable = mono_class_vtable (domain, mono_defaults.string_class);
#include <mono/utils/strenc.h>
#include <mono/utils/mono-proclib.h>
#include <mono/io-layer/io-layer.h>
-#ifndef HAVE_GETPROCESSID
-#if defined(_MSC_VER) || defined(HAVE_WINTERNL_H)
-#include <winternl.h>
-#ifndef NT_SUCCESS
-#define NT_SUCCESS(status) ((NTSTATUS) (status) >= 0)
-#endif /* !NT_SUCCESS */
-#else /* ! (defined(_MSC_VER) || defined(HAVE_WINTERNL_H)) */
-#include <ddk/ntddk.h>
-#include <ddk/ntapi.h>
-#endif /* (defined(_MSC_VER) || defined(HAVE_WINTERNL_H)) */
-#endif /* !HAVE_GETPROCESSID */
-/* FIXME: fix this code to not depend so much on the inetrnals */
+/* FIXME: fix this code to not depend so much on the internals */
#include <mono/metadata/class-internals.h>
#define LOGDEBUG(...)
return(handle);
}
-guint32 ves_icall_System_Diagnostics_Process_GetPid_internal (void)
+guint32
+ves_icall_System_Diagnostics_Process_GetPid_internal (void)
{
MONO_ARCH_SAVE_REGS;
- return(GetCurrentProcessId ());
+ return mono_process_current_pid ();
}
void ves_icall_System_Diagnostics_Process_Process_free_internal (MonoObject *this,
return TRUE;
}
-#ifndef HAVE_GETPROCESSID
-/* Run-time GetProcessId detection for Windows */
-#ifdef TARGET_WIN32
-#define HAVE_GETPROCESSID
-
-typedef DWORD (WINAPI *GETPROCESSID_PROC) (HANDLE);
-typedef DWORD (WINAPI *NTQUERYINFORMATIONPROCESS_PROC) (HANDLE, PROCESSINFOCLASS, PVOID, ULONG, PULONG);
-typedef DWORD (WINAPI *RTLNTSTATUSTODOSERROR_PROC) (NTSTATUS);
-
-static DWORD WINAPI GetProcessId_detect (HANDLE process);
-
-static GETPROCESSID_PROC GetProcessId = &GetProcessId_detect;
-static NTQUERYINFORMATIONPROCESS_PROC NtQueryInformationProcess_proc = NULL;
-static RTLNTSTATUSTODOSERROR_PROC RtlNtStatusToDosError_proc = NULL;
-
-static DWORD WINAPI GetProcessId_ntdll (HANDLE process)
-{
- PROCESS_BASIC_INFORMATION pi;
- NTSTATUS status;
-
- status = NtQueryInformationProcess_proc (process, ProcessBasicInformation, &pi, sizeof (pi), NULL);
- if (NT_SUCCESS (status)) {
- return pi.UniqueProcessId;
- } else {
- SetLastError (RtlNtStatusToDosError_proc (status));
- return 0;
- }
-}
-
-static DWORD WINAPI GetProcessId_stub (HANDLE process)
-{
- SetLastError (ERROR_CALL_NOT_IMPLEMENTED);
- return 0;
-}
-
-static DWORD WINAPI GetProcessId_detect (HANDLE process)
-{
- HMODULE module_handle;
- GETPROCESSID_PROC GetProcessId_kernel;
-
- /* Windows XP SP1 and above have GetProcessId API */
- module_handle = GetModuleHandle (L"kernel32.dll");
- if (module_handle != NULL) {
- GetProcessId_kernel = (GETPROCESSID_PROC) GetProcAddress (module_handle, "GetProcessId");
- if (GetProcessId_kernel != NULL) {
- GetProcessId = GetProcessId_kernel;
- return GetProcessId (process);
- }
- }
-
- /* Windows 2000 and above have deprecated NtQueryInformationProcess API */
- module_handle = GetModuleHandle (L"ntdll.dll");
- if (module_handle != NULL) {
- NtQueryInformationProcess_proc = (NTQUERYINFORMATIONPROCESS_PROC) GetProcAddress (module_handle, "NtQueryInformationProcess");
- if (NtQueryInformationProcess_proc != NULL) {
- RtlNtStatusToDosError_proc = (RTLNTSTATUSTODOSERROR_PROC) GetProcAddress (module_handle, "RtlNtStatusToDosError");
- if (RtlNtStatusToDosError_proc != NULL) {
- GetProcessId = &GetProcessId_ntdll;
- return GetProcessId (process);
- }
- }
- }
-
- /* Fall back to ERROR_CALL_NOT_IMPLEMENTED */
- GetProcessId = &GetProcessId_stub;
- return GetProcessId (process);
-}
-#endif /* HOST_WIN32 */
-#endif /* !HAVE_GETPROCESSID */
-
MonoBoolean ves_icall_System_Diagnostics_Process_ShellExecuteEx_internal (MonoProcessStartInfo *proc_start_info, MonoProcInfo *process_info)
{
SHELLEXECUTEINFO shellex = {0};
/* Returns an array of pids */
MonoArray *ves_icall_System_Diagnostics_Process_GetProcesses_internal (void)
{
+#if !defined(HOST_WIN32)
+ MonoArray *procs;
+ gpointer *pidarray;
+ int i, count;
+
+ MONO_ARCH_SAVE_REGS;
+
+ pidarray = mono_process_list (&count);
+ if (!pidarray)
+ mono_raise_exception (mono_get_exception_not_supported ("This system does not support EnumProcesses"));
+ procs = mono_array_new (mono_domain_get (), mono_get_int32_class (), count);
+ if (sizeof (guint32) == sizeof (gpointer)) {
+ memcpy (mono_array_addr (procs, guint32, 0), pidarray, count);
+ } else {
+ for (i = 0; i < count; ++i)
+ *(mono_array_addr (procs, guint32, i)) = GPOINTER_TO_UINT (pidarray [i]);
+ }
+ g_free (pidarray);
+
+ return procs;
+#else
MonoArray *procs;
gboolean ret;
DWORD needed;
- guint32 count;
+ int count;
guint32 *pids;
- MONO_ARCH_SAVE_REGS;
-
count = 512;
do {
pids = g_new0 (guint32, count);
pids = NULL;
return procs;
+#endif
}
MonoBoolean ves_icall_System_Diagnostics_Process_GetWorkingSet_internal (HANDLE process, guint32 *min, guint32 *max)
gboolean success = FALSE;
PACL pDACL = NULL;
PSID pEveryoneSid = NULL;
+ PSECURITY_DESCRIPTOR pSecurityDescriptor = NULL;
DWORD dwRes = GetNamedSecurityInfoW (path, SE_FILE_OBJECT,
- DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, NULL);
+ DACL_SECURITY_INFORMATION, NULL, NULL, &pDACL, NULL, &pSecurityDescriptor);
if (dwRes != ERROR_SUCCESS)
return FALSE;
/* Note: we don't need to check our own access -
we'll know soon enough when reading the file */
- if (pDACL)
- LocalFree (pDACL);
+ if (pSecurityDescriptor)
+ LocalFree (pSecurityDescriptor);
return success;
}
void **p;
char *new_next;
TLAB_ACCESS_INIT;
+ size_t real_size = size;
+
+ CANARIFY_SIZE(size);
HEAVY_STAT (++stat_objects_alloced);
- if (size <= SGEN_MAX_SMALL_OBJ_SIZE)
+ if (real_size <= SGEN_MAX_SMALL_OBJ_SIZE)
HEAVY_STAT (stat_bytes_alloced += size);
else
HEAVY_STAT (stat_bytes_alloced_los += size);
if (collect_before_allocs) {
if (((current_alloc % collect_before_allocs) == 0) && nursery_section) {
sgen_perform_collection (0, GENERATION_NURSERY, "collect-before-alloc-triggered", TRUE);
- if (!degraded_mode && sgen_can_alloc_size (size) && size <= SGEN_MAX_SMALL_OBJ_SIZE) {
+ if (!degraded_mode && sgen_can_alloc_size (size) && real_size <= SGEN_MAX_SMALL_OBJ_SIZE) {
// FIXME:
g_assert_not_reached ();
}
* specially by the world-stopping code.
*/
- if (size > SGEN_MAX_SMALL_OBJ_SIZE) {
- p = sgen_los_alloc_large_inner (vtable, size);
+ if (real_size > SGEN_MAX_SMALL_OBJ_SIZE) {
+ p = sgen_los_alloc_large_inner (vtable, ALIGN_UP (real_size));
} else {
/* tlab_next and tlab_temp_end are TLS vars so accessing them might be expensive */
* visible before the vtable store.
*/
+ CANARIFY_ALLOC(p,real_size);
SGEN_LOG (6, "Allocated object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
binary_protocol_alloc (p , vtable, size);
if (G_UNLIKELY (MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ()))
do {
p = sgen_nursery_alloc (size);
if (!p) {
- sgen_ensure_free_space (size);
+ sgen_ensure_free_space (real_size);
if (degraded_mode)
return alloc_degraded (vtable, size, FALSE);
else
TLAB_TEMP_END = MIN (TLAB_REAL_END, TLAB_NEXT + SGEN_SCAN_START_SIZE);
SGEN_LOG (5, "Expanding local alloc: %p-%p", TLAB_NEXT, TLAB_TEMP_END);
}
+ CANARIFY_ALLOC(p,real_size);
}
if (G_LIKELY (p)) {
SGEN_LOG (6, "Allocated object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
binary_protocol_alloc (p, vtable, size);
if (G_UNLIKELY (MONO_GC_MAJOR_OBJ_ALLOC_LARGE_ENABLED ()|| MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ())) {
- if (size > SGEN_MAX_SMALL_OBJ_SIZE)
+ if (real_size > SGEN_MAX_SMALL_OBJ_SIZE)
MONO_GC_MAJOR_OBJ_ALLOC_LARGE ((mword)p, size, vtable->klass->name_space, vtable->klass->name);
else
MONO_GC_NURSERY_OBJ_ALLOC ((mword)p, size, vtable->klass->name_space, vtable->klass->name);
void **p;
char *new_next;
TLAB_ACCESS_INIT;
+ size_t real_size = size;
+
+ CANARIFY_SIZE(size);
size = ALIGN_UP (size);
- SGEN_ASSERT (9, size >= sizeof (MonoObject), "Object too small");
+ SGEN_ASSERT (9, real_size >= sizeof (MonoObject), "Object too small");
g_assert (vtable->gc_descr);
- if (size > SGEN_MAX_SMALL_OBJ_SIZE)
+ if (real_size > SGEN_MAX_SMALL_OBJ_SIZE)
return NULL;
if (G_UNLIKELY (size > tlab_size)) {
HEAVY_STAT (++stat_objects_alloced);
HEAVY_STAT (stat_bytes_alloced += size);
+ CANARIFY_ALLOC(p,real_size);
SGEN_LOG (6, "Allocated object %p, vtable: %p (%s), size: %zd", p, vtable, vtable->klass->name, size);
binary_protocol_alloc (p, vtable, size);
if (G_UNLIKELY (MONO_GC_NURSERY_OBJ_ALLOC_ENABLED ()))
/*
* a string allocator method takes the args: (vtable, len)
*
- * bytes = sizeof (MonoString) + ((len + 1) * 2)
+ * bytes = offsetof (MonoString, chars) + ((len + 1) * 2)
*
* condition:
*
*
* therefore:
*
- * sizeof (MonoString) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
- * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - sizeof (MonoString)) / 2 - 1
+ * offsetof (MonoString, chars) + ((len + 1) * 2) <= INT32_MAX - (SGEN_ALLOC_ALIGN - 1)
+ * len <= (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - offsetof (MonoString, chars)) / 2 - 1
*/
mono_mb_emit_ldarg (mb, 1);
- mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - sizeof (MonoString)) / 2 - 1);
+ mono_mb_emit_icon (mb, (INT32_MAX - (SGEN_ALLOC_ALIGN - 1) - MONO_STRUCT_OFFSET (MonoString, chars)) / 2 - 1);
pos = mono_mb_emit_short_branch (mb, MONO_CEE_BLE_UN_S);
mono_mb_emit_byte (mb, MONO_CUSTOM_PREFIX);
mono_mb_emit_icon (mb, 1);
mono_mb_emit_byte (mb, MONO_CEE_SHL);
//WE manually fold the above + 2 here
- mono_mb_emit_icon (mb, sizeof (MonoString) + 2);
+ mono_mb_emit_icon (mb, MONO_STRUCT_OFFSET (MonoString, chars) + 2);
mono_mb_emit_byte (mb, CEE_ADD);
mono_mb_emit_stloc (mb, size_var);
} else {
#ifndef __MONO_SGENARCHDEP_H__
#define __MONO_SGENARCHDEP_H__
-#include <mono/utils/mono-sigcontext.h>
+#include <mono/utils/mono-context.h>
+
+/*
+ * Define either USE_MONO_CTX, or
+ * ARCH_SIGCTX_SP/ARCH_SIGCTX_IP/ARCH_STORE_REGS/ARCH_COPY_SIGCTX_REGS.
+ * Define ARCH_NUM_REGS to be the number of general registers in MonoContext, or the
+ * number of registers stored by ARCH_STORE_REGS.
+ */
#if defined(MONO_CROSS_COMPILE)
#elif defined(TARGET_X86)
-#include <mono/utils/mono-context.h>
-
#define REDZONE_SIZE 0
#define ARCH_NUM_REGS 8
-#ifdef MONO_ARCH_HAS_MONO_CONTEXT
-#define USE_MONO_CTX
-#else
+#ifndef MONO_ARCH_HAS_MONO_CONTEXT
#error 0
#endif
-/*FIXME, move this to mono-sigcontext as this is generaly useful.*/
-#define ARCH_SIGCTX_SP(ctx) (UCONTEXT_REG_ESP ((ctx)))
-#define ARCH_SIGCTX_IP(ctx) (UCONTEXT_REG_EIP ((ctx)))
+#define USE_MONO_CTX
#elif defined(TARGET_AMD64)
-#include <mono/utils/mono-context.h>
-
#define REDZONE_SIZE 128
#define ARCH_NUM_REGS 16
#define USE_MONO_CTX
-/*FIXME, move this to mono-sigcontext as this is generaly useful.*/
-#define ARCH_SIGCTX_SP(ctx) (UCONTEXT_REG_RSP (ctx))
-#define ARCH_SIGCTX_IP(ctx) (UCONTEXT_REG_RIP (ctx))
-
#elif defined(TARGET_POWERPC)
#define REDZONE_SIZE 224
/* We dont store ip, sp */
#define ARCH_NUM_REGS 14
-#define ARCH_STORE_REGS(ptr) \
- __asm__ __volatile__( \
- "push {lr}\n" \
- "mov lr, %0\n" \
- "stmia lr!, {r0-r12}\n" \
- "pop {lr}\n" \
- : \
- : "r" (ptr) \
- )
-
-#define ARCH_SIGCTX_SP(ctx) (UCONTEXT_REG_SP((ctx)))
-#define ARCH_SIGCTX_IP(ctx) (UCONTEXT_REG_PC((ctx)))
-#define ARCH_COPY_SIGCTX_REGS(a,ctx) do { \
- ((a)[0]) = (gpointer) (UCONTEXT_REG_R0((ctx))); \
- ((a)[1]) = (gpointer) (UCONTEXT_REG_R1((ctx))); \
- ((a)[2]) = (gpointer) (UCONTEXT_REG_R2((ctx))); \
- ((a)[3]) = (gpointer) (UCONTEXT_REG_R3((ctx))); \
- ((a)[4]) = (gpointer) (UCONTEXT_REG_R4((ctx))); \
- ((a)[5]) = (gpointer) (UCONTEXT_REG_R5((ctx))); \
- ((a)[6]) = (gpointer) (UCONTEXT_REG_R6((ctx))); \
- ((a)[7]) = (gpointer) (UCONTEXT_REG_R7((ctx))); \
- ((a)[8]) = (gpointer) (UCONTEXT_REG_R8((ctx))); \
- ((a)[9]) = (gpointer) (UCONTEXT_REG_R9((ctx))); \
- ((a)[10]) = (gpointer) (UCONTEXT_REG_R10((ctx))); \
- ((a)[11]) = (gpointer) (UCONTEXT_REG_R11((ctx))); \
- ((a)[12]) = (gpointer) (UCONTEXT_REG_R12((ctx))); \
- ((a)[13]) = (gpointer) (UCONTEXT_REG_LR((ctx))); \
- } while (0)
#elif defined(TARGET_ARM64)
-#include <mono/utils/mono-sigcontext.h>
-
#ifdef __linux__
#define REDZONE_SIZE 0
#elif defined(__APPLE__)
#define USE_MONO_CTX
#define ARCH_NUM_REGS 31
-#define ARCH_STORE_REGS(ptr) do { g_assert_not_reached (); } while (0)
-
-#define ARCH_SIGCTX_SP(ctx) UCONTEXT_REG_SP (ctx)
-#define ARCH_SIGCTX_IP(ctx) UCONTEXT_REG_PC (ctx)
-#define ARCH_COPY_SIGCTX_REGS(a,ctx) do { g_assert_not_reached (); } while (0)
-
#elif defined(__mips__)
#define REDZONE_SIZE 0
#define USE_MONO_CTX
#define ARCH_NUM_REGS 32
-/*
- * These casts are necessary since glibc always makes the
- * gregs 64-bit values in userland.
- */
-#define ARCH_SIGCTX_SP(ctx) ((gsize) UCONTEXT_GREGS((ctx))[29])
-#define ARCH_SIGCTX_IP(ctx) ((gsize) UCONTEXT_REG_PC((ctx)))
-
#elif defined(__s390x__)
#define REDZONE_SIZE 0
-#include <mono/utils/mono-context.h>
-
#define USE_MONO_CTX
#define ARCH_NUM_REGS 16
-#define ARCH_SIGCTX_SP(ctx) ((UCONTEXT_GREGS((ctx))) [15])
-#define ARCH_SIGCTX_IP(ctx) ((ucontext_t *) (ctx))->uc_mcontext.psw.addr
#elif defined(__sparc__)
HEAVY_STAT (++bloby_objects);
if (cards) {
if (sgen_card_table_is_range_marked (cards, (mword)obj, block_obj_size))
- sgen_get_current_object_ops ()->scan_object (obj, queue);
+ sgen_get_current_object_ops ()->scan_object (obj, sgen_obj_get_descriptor (obj), queue);
} else if (sgen_card_table_region_begin_scanning ((mword)obj, block_obj_size)) {
- sgen_get_current_object_ops ()->scan_object (obj, queue);
+ sgen_get_current_object_ops ()->scan_object (obj, sgen_obj_get_descriptor (obj), queue);
}
binary_protocol_card_scan (obj, sgen_safe_object_get_size ((MonoObject*)obj));
*/
// #define HEAVY_STATISTICS
+#ifdef HEAVY_STATISTICS
+#define HEAVY_STAT(x) x
+#else
+#define HEAVY_STAT(x)
+#endif
+
/*
* Define this to allow the user to change the nursery size by
* specifying its value in the MONO_GC_PARAMS environmental
obj = destination;
if (queue) {
SGEN_LOG (9, "Enqueuing gray object %p (%s)", obj, sgen_safe_name (obj));
- GRAY_OBJECT_ENQUEUE (queue, obj);
+ GRAY_OBJECT_ENQUEUE (queue, obj, sgen_vtable_get_descriptor (vt));
}
}
static void
check_consistency_callback (char *start, size_t size, void *dummy)
{
- GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
+ MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (start);
+ mword desc = sgen_vtable_get_descriptor (vt);
SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
#include "sgen-scan-object.h"
check_mod_union_callback (char *start, size_t size, void *dummy)
{
gboolean in_los = (gboolean) (size_t) dummy;
- GCVTable *vt = (GCVTable*)LOAD_VTABLE (start);
+ MonoVTable *vt = (MonoVTable*)LOAD_VTABLE (start);
+ mword desc = sgen_vtable_get_descriptor (vt);
guint8 *cards;
SGEN_LOG (8, "Scanning object %p, vtable: %p (%s)", start, vt, vt->klass->name);
static void
check_major_refs_callback (char *start, size_t size, void *dummy)
{
+ mword desc = sgen_obj_get_descriptor (start);
+
#include "sgen-scan-object.h"
}
void
check_object (char *start)
{
+ mword desc;
+
if (!start)
return;
+ desc = sgen_obj_get_descriptor (start);
+
#include "sgen-scan-object.h"
}
verify_object_pointers_callback (char *start, size_t size, void *data)
{
gboolean allow_missing_pinned = (gboolean) (size_t) data;
+ mword desc = sgen_obj_get_descriptor (start);
#include "sgen-scan-object.h"
}
check_marked_callback (char *start, size_t size, void *dummy)
{
gboolean is_los = (gboolean) (size_t) dummy;
+ mword desc;
if (is_los) {
if (!sgen_los_object_is_pinned (start))
return;
}
+ desc = sgen_obj_get_descriptor (start);
+
#include "sgen-scan-object.h"
}
start = forwarded;
if (scan_object_for_specific_ref_precise) {
+ mword desc = sgen_obj_get_descriptor (start);
#include "sgen-scan-object.h"
} else {
mword *words = (mword*)start;
static void
scan_object_for_xdomain_refs (char *start, mword size, void *data)
{
- MonoDomain *domain = ((MonoObject*)start)->vtable->domain;
+ MonoVTable *vt = (MonoVTable*)SGEN_LOAD_VTABLE (start);
+ MonoDomain *domain = vt->domain;
+ mword desc = sgen_vtable_get_descriptor (vt);
#include "sgen-scan-object.h"
}
#define _XOPEN_SOURCE
#endif
+#include "utils/mono-counters.h"
#include "metadata/sgen-gc.h"
#define MAX_USER_DESCRIPTORS 16
static int user_descriptors_next = 0;
static void *all_ref_root_descrs [32];
+#ifdef HEAVY_STATISTICS
+static long long stat_scanned_count_per_descriptor [DESC_TYPE_MAX];
+#endif
static int
alloc_complex_descriptor (gsize *bitmap, int numbits)
return user_descriptors [desc >> ROOT_DESC_TYPE_SHIFT];
}
+#ifdef HEAVY_STATISTICS
+void
+sgen_descriptor_count_scanned_object (mword desc)
+{
+ int type = desc & 7;
+ SGEN_ASSERT (0, type, "Descriptor type can't be zero");
+ ++stat_scanned_count_per_descriptor [type - 1];
+}
+#endif
+
+void
+sgen_init_descriptors (void)
+{
+#ifdef HEAVY_STATISTICS
+ mono_counters_register ("# scanned RUN_LENGTH", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_RUN_LENGTH - 1]);
+ mono_counters_register ("# scanned SMALL_BITMAP", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_SMALL_BITMAP - 1]);
+ mono_counters_register ("# scanned COMPLEX", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_COMPLEX - 1]);
+ mono_counters_register ("# scanned VECTOR", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_VECTOR - 1]);
+ mono_counters_register ("# scanned LARGE_BITMAP", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_LARGE_BITMAP - 1]);
+ mono_counters_register ("# scanned COMPLEX_ARR", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_COMPLEX_ARR - 1]);
+ mono_counters_register ("# scanned COMPLEX_PTRFREE", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_scanned_count_per_descriptor [DESC_TYPE_COMPLEX_PTRFREE - 1]);
+#endif
+}
+
#endif
* copy_object_no_checks(), without having to fetch the
* object's class.
*/
- DESC_TYPE_RUN_LENGTH = 1, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
- DESC_TYPE_SMALL_BITMAP, /* 16 bits aligned byte size | 16-48 bit bitmap */
- DESC_TYPE_COMPLEX, /* index for bitmap into complex_descriptors */
- DESC_TYPE_VECTOR, /* 10 bits element size | 1 bit kind | 2 bits desc | element desc */
- DESC_TYPE_LARGE_BITMAP, /* | 29-61 bitmap bits */
- DESC_TYPE_COMPLEX_ARR, /* index for bitmap into complex_descriptors */
- DESC_TYPE_COMPLEX_PTRFREE, /*Nothing, used to encode large ptr objects. */
+ DESC_TYPE_RUN_LENGTH = 1, /* 16 bits aligned byte size | 1-3 (offset, numptr) bytes tuples */
+ DESC_TYPE_SMALL_BITMAP = 2, /* 16 bits aligned byte size | 16-48 bit bitmap */
+ DESC_TYPE_COMPLEX = 3, /* index for bitmap into complex_descriptors */
+ DESC_TYPE_VECTOR = 4, /* 10 bits element size | 1 bit kind | 2 bits desc | element desc */
+ DESC_TYPE_LARGE_BITMAP = 5, /* | 29-61 bitmap bits */
+ DESC_TYPE_COMPLEX_ARR = 6, /* index for bitmap into complex_descriptors */
+ DESC_TYPE_COMPLEX_PTRFREE = 7, /*Nothing, used to encode large ptr objects. */
+ DESC_TYPE_MAX = 7,
/* values for array kind */
DESC_TYPE_V_SZARRAY = 0, /*vector with no bounds data */
DESC_TYPE_V_ARRAY = 1, /* array with bounds data */
void* sgen_get_complex_descriptor_bitmap (mword desc) MONO_INTERNAL;
MonoGCRootMarkFunc sgen_get_user_descriptor_func (mword desc) MONO_INTERNAL;
+void sgen_init_descriptors (void) MONO_INTERNAL;
+
+#ifdef HEAVY_STATISTICS
+void sgen_descriptor_count_scanned_object (mword desc) MONO_INTERNAL;
+#endif
static inline gboolean
sgen_gc_descr_has_references (mword desc)
void **_objptr = (void**)(obj); \
_objptr += ((desc) >> 16) & 0xff; \
_objptr_end = _objptr + (((desc) >> 24) & 0xff); \
- HANDLE_PTR (_objptr, (obj)); \
- _objptr ++; \
while (_objptr < _objptr_end) { \
HANDLE_PTR (_objptr, (obj)); \
_objptr++; \
void **_objptr = (void**)(obj); \
gsize _bmap = (desc) >> 16; \
_objptr += OBJECT_HEADER_WORDS; \
- { \
- int _index = GNUC_BUILTIN_CTZ (_bmap); \
- _objptr += _index; \
- _bmap >>= (_index + 1); \
- HANDLE_PTR (_objptr, (obj)); \
- _objptr ++; \
- } \
- while (_bmap) { \
+ do { \
int _index = GNUC_BUILTIN_CTZ (_bmap); \
_objptr += _index; \
_bmap >>= (_index + 1); \
HANDLE_PTR (_objptr, (obj)); \
_objptr ++; \
- } \
+ } while (_bmap); \
} while (0)
#else
#define OBJ_BITMAP_FOREACH_PTR(desc,obj) do { \
} while (0)
/* this one is untested */
-#define OBJ_COMPLEX_ARR_FOREACH_PTR(vt,obj) do { \
+#define OBJ_COMPLEX_ARR_FOREACH_PTR(desc,obj) do { \
/* there are pointers */ \
- gsize *mbitmap_data = sgen_get_complex_descriptor ((vt)->desc); \
+ GCVTable *vt = (GCVTable*)SGEN_LOAD_VTABLE (obj); \
+ gsize *mbitmap_data = sgen_get_complex_descriptor ((desc)); \
gsize mbwords = (*mbitmap_data++) - 1; \
gsize el_size = mono_array_element_size (vt->klass); \
char *e_start = (char*)(obj) + G_STRUCT_OFFSET (MonoArray, vector); \
char *e_end = e_start + el_size * mono_array_length_fast ((MonoArray*)(obj)); \
if (0) \
- g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (vt)->desc, vt->klass->name_space, vt->klass->name); \
+ g_print ("found %d at %p (0x%zx): %s.%s\n", mbwords, (obj), (desc), (vt)->klass->name_space, (vt)->klass->name); \
while (e_start < e_end) { \
void **_objptr = (void**)e_start; \
gsize *bitmap_data = mbitmap_data; \
* GC.Collect().
*/
static gboolean allow_synchronous_major = TRUE;
-static gboolean nursery_collection_is_parallel = FALSE;
static gboolean disable_minor_collections = FALSE;
static gboolean disable_major_collections = FALSE;
gboolean do_pin_stats = FALSE;
static gboolean do_verify_nursery = FALSE;
static gboolean do_dump_nursery_content = FALSE;
+static gboolean enable_nursery_canaries = FALSE;
#ifdef HEAVY_STATISTICS
long long stat_objects_alloced_degraded = 0;
static long long time_major_sweep = 0;
static long long time_major_fragment_creation = 0;
+static SGEN_TV_DECLARE (time_major_conc_collection_start);
+static SGEN_TV_DECLARE (time_major_conc_collection_end);
+
+static SGEN_TV_DECLARE (last_minor_collection_start_tv);
+static SGEN_TV_DECLARE (last_minor_collection_end_tv);
+
int gc_debug_level = 0;
FILE* gc_debug_file;
return vt->klass->name;
}
+gboolean
+nursery_canaries_enabled (void)
+{
+ return enable_nursery_canaries;
+}
+
#define safe_object_get_size sgen_safe_object_get_size
const char*
}
if (wake) {
- g_assert (concurrent_collection_in_progress ||
- (current_collection_generation == GENERATION_OLD && major_collector.is_parallel));
+ g_assert (concurrent_collection_in_progress);
if (sgen_workers_have_started ()) {
sgen_workers_wake_up_all ();
} else {
obj = start;
}
- size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
-
- if ((MonoVTable*)SGEN_LOAD_VTABLE (obj) != array_fill_vtable)
+ if ((MonoVTable*)SGEN_LOAD_VTABLE (obj) != array_fill_vtable) {
+ CHECK_CANARY_FOR_OBJECT (obj);
+ size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
callback (obj, size, data);
+ CANARIFY_SIZE (size);
+ } else {
+ size = ALIGN_UP (safe_object_get_size ((MonoObject*)obj));
+ }
start += size;
}
static void
clear_domain_process_minor_object_callback (char *obj, size_t size, MonoDomain *domain)
{
- if (clear_domain_process_object (obj, domain))
+ if (clear_domain_process_object (obj, domain)) {
+ CANARIFY_SIZE (size);
memset (obj, 0, size);
+ }
}
static void
* Scan objects in the gray stack until the stack is empty. This should be called
* frequently after each object is copied, to achieve better locality and cache
* usage.
+ *
+ * max_objs is the maximum number of objects to scan, or -1 to scan until the stack is
+ * empty.
*/
gboolean
sgen_drain_gray_stack (int max_objs, ScanCopyContext ctx)
{
- char *obj;
ScanObjectFunc scan_func = ctx.scan_func;
GrayQueue *queue = ctx.queue;
- if (max_objs == -1) {
- for (;;) {
- GRAY_OBJECT_DEQUEUE (queue, &obj);
+ do {
+ int i;
+ for (i = 0; i != max_objs; ++i) {
+ char *obj;
+ mword desc;
+ GRAY_OBJECT_DEQUEUE (queue, &obj, &desc);
if (!obj)
return TRUE;
SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, safe_name (obj));
- scan_func (obj, queue);
+ scan_func (obj, desc, queue);
}
- } else {
- int i;
-
- do {
- for (i = 0; i != max_objs; ++i) {
- GRAY_OBJECT_DEQUEUE (queue, &obj);
- if (!obj)
- return TRUE;
- SGEN_LOG (9, "Precise gray object scan %p (%s)", obj, safe_name (obj));
- scan_func (obj, queue);
- }
- } while (max_objs < 0);
- return FALSE;
- }
+ } while (max_objs < 0);
+ return FALSE;
}
/*
while (start < end) {
void *obj_to_pin = NULL;
size_t obj_to_pin_size = 0;
+ mword desc;
addr = *start;
}
/* Skip to the next object */
+ if (((MonoObject*)search_start)->synchronisation != GINT_TO_POINTER (-1)) {
+ CHECK_CANARY_FOR_OBJECT (search_start);
+ CANARIFY_SIZE (obj_size);
+ CANARIFY_SIZE (obj_to_pin_size);
+ }
search_start = (void*)((char*)search_start + obj_size);
} while (search_start <= addr);
/*
* Finally - pin the object!
*/
+ desc = sgen_obj_get_descriptor_safe (obj_to_pin);
if (scan_func) {
- scan_func (obj_to_pin, queue);
+ scan_func (obj_to_pin, desc, queue);
} else {
SGEN_LOG (4, "Pinned object %p, vtable %p (%s), count %d\n",
obj_to_pin, *(void**)obj_to_pin, safe_name (obj_to_pin), count);
#endif
pin_object (obj_to_pin);
- GRAY_OBJECT_ENQUEUE (queue, obj_to_pin);
+ GRAY_OBJECT_ENQUEUE (queue, obj_to_pin, desc);
if (G_UNLIKELY (do_pin_stats))
sgen_pin_stats_register_object (obj_to_pin, obj_to_pin_size);
definitely_pinned [count] = obj_to_pin;
{
g_assert (!concurrent_collection_in_progress);
- if (sgen_collection_is_parallel ()) {
- LOCK_PIN_QUEUE;
- /*object arrives pinned*/
- sgen_pin_stage_ptr (object);
- ++objects_pinned ;
- UNLOCK_PIN_QUEUE;
- } else {
- SGEN_PIN_OBJECT (object);
- sgen_pin_stage_ptr (object);
- ++objects_pinned;
- if (G_UNLIKELY (do_pin_stats))
- sgen_pin_stats_register_object (object, safe_object_get_size (object));
- }
- GRAY_OBJECT_ENQUEUE (queue, object);
+ SGEN_PIN_OBJECT (object);
+ sgen_pin_stage_ptr (object);
+ ++objects_pinned;
+ if (G_UNLIKELY (do_pin_stats))
+ sgen_pin_stats_register_object (object, safe_object_get_size (object));
+
+ GRAY_OBJECT_ENQUEUE (queue, object, sgen_obj_get_descriptor (object));
binary_protocol_pin (object, (gpointer)LOAD_VTABLE (object), safe_object_get_size (object));
#ifdef ENABLE_DTRACE
gboolean major_pinned = FALSE;
if (sgen_ptr_in_nursery (obj)) {
- if (SGEN_CAS_PTR (obj, (void*)((mword)vt | SGEN_PINNED_BIT), vt) == vt) {
+ if (SGEN_CAS_PTR (obj, SGEN_POINTER_TAG_PINNED (vt), vt) == vt) {
sgen_pin_object (obj, queue);
break;
}
vtable_word = *(mword*)obj;
/*someone else forwarded it, update the pointer and bail out*/
- if (vtable_word & SGEN_FORWARDED_BIT) {
- *ptr = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
+ if (SGEN_POINTER_IS_TAGGED_FORWARDED (vtable_word)) {
+ *ptr = SGEN_POINTER_UNTAG_VTABLE (vtable_word);
break;
}
/*someone pinned it, nothing to do.*/
- if (vtable_word & SGEN_PINNED_BIT || major_pinned)
+ if (SGEN_POINTER_IS_TAGGED_PINNED (vtable_word) || major_pinned)
break;
}
}
{
for (;;) {
char *addr;
- GRAY_OBJECT_DEQUEUE (queue, &addr);
+ mword desc;
+ GRAY_OBJECT_DEQUEUE (queue, &addr, &desc);
if (!addr)
break;
g_assert (SGEN_OBJECT_IS_PINNED (addr));
}
g_assert (sgen_gray_object_queue_is_empty (queue));
+
+ sgen_gray_object_queue_trim_free_list (queue);
}
void
{
g_assert (mono_profiler_events & MONO_PROFILE_GC_MOVES);
- /* FIXME: handle this for parallel collector */
- g_assert (!sgen_collection_is_parallel ());
-
if (moved_objects_idx == MOVED_OBJECTS_NUM) {
mono_profiler_gc_moves (moved_objects, moved_objects_idx);
moved_objects_idx = 0;
bytes_pinned_from_failed_allocation += objsize;
}
-gboolean
-sgen_collection_is_parallel (void)
-{
- switch (current_collection_generation) {
- case GENERATION_NURSERY:
- return nursery_collection_is_parallel;
- case GENERATION_OLD:
- return major_collector.is_parallel;
- default:
- g_error ("Invalid current generation %d", current_collection_generation);
- }
-}
-
gboolean
sgen_collection_is_concurrent (void)
{
sgen_free_internal_dynamic (job_data, sizeof (ScanThreadDataJobData), INTERNAL_MEM_WORKER_JOB_DATA);
}
-typedef struct
-{
- FinalizeReadyEntry *list;
-} ScanFinalizerEntriesJobData;
-
static void
job_scan_finalizer_entries (WorkerData *worker_data, void *job_data_untyped)
{
- ScanFinalizerEntriesJobData *job_data = job_data_untyped;
+ FinalizeReadyEntry *list = job_data_untyped;
ScanCopyContext ctx = { NULL, current_object_ops.copy_or_mark_object, sgen_workers_get_job_gray_queue (worker_data) };
- scan_finalizer_entries (job_data->list, ctx);
- sgen_free_internal_dynamic (job_data, sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA);
+ scan_finalizer_entries (list, ctx);
}
static void
if (!do_verify_nursery)
return;
+
+ if (nursery_canaries_enabled ())
+ SGEN_LOG (1, "Checking nursery canaries...");
/*This cleans up unused fragments */
sgen_nursery_allocator_prepare_for_pinning ();
SGEN_LOG (1, "HOLE [%p %p %d]", hole_start, cur, (int)(cur - hole_start));
SGEN_LOG (1, "OBJ [%p %p %d %d %s %d]", cur, cur + size, (int)size, (int)ss, sgen_safe_name ((MonoObject*)cur), (gpointer)LOAD_VTABLE (cur) == sgen_get_array_fill_vtable ());
}
+ if (nursery_canaries_enabled () && (MonoVTable*)SGEN_LOAD_VTABLE (cur) != array_fill_vtable) {
+ CHECK_CANARY_FOR_OBJECT (cur);
+ CANARIFY_SIZE (size);
+ }
cur += size;
hole_start = cur;
}
static void
init_gray_queue (void)
{
- if (sgen_collection_is_parallel () || sgen_collection_is_concurrent ()) {
+ if (sgen_collection_is_concurrent ()) {
sgen_workers_init_distribute_gray_queue ();
sgen_gray_object_queue_init_with_alloc_prepare (&gray_queue, NULL,
gray_queue_redirect, sgen_workers_get_distribute_section_gray_queue ());
}
}
-static void
-pin_stage_object_callback (char *obj, size_t size, void *data)
-{
- sgen_pin_stage_ptr (obj);
- /* FIXME: do pin stats if enabled */
-}
-
/*
* Collect objects in the nursery. Returns whether to trigger a major
* collection.
char *nursery_next;
FinishRememberedSetScanJobData *frssjd;
ScanFromRegisteredRootsJobData *scrrjd_normal, *scrrjd_wbarrier;
- ScanFinalizerEntriesJobData *sfejd_fin_ready, *sfejd_critical_fin;
ScanThreadDataJobData *stdjd;
mword fragment_total;
ScanCopyContext ctx;
- TV_DECLARE (all_atv);
- TV_DECLARE (all_btv);
TV_DECLARE (atv);
TV_DECLARE (btv);
if (disable_minor_collections)
return TRUE;
+ TV_GETTIME (last_minor_collection_start_tv);
+ atv = last_minor_collection_start_tv;
+
MONO_GC_BEGIN (GENERATION_NURSERY);
binary_protocol_collection_begin (gc_stats.minor_gc_count, GENERATION_NURSERY);
#endif
current_collection_generation = GENERATION_NURSERY;
- if (sgen_collection_is_parallel ())
- current_object_ops = sgen_minor_collector.parallel_ops;
- else
- current_object_ops = sgen_minor_collector.serial_ops;
-
+ current_object_ops = sgen_minor_collector.serial_ops;
+
reset_pinned_from_failed_allocation ();
check_scan_starts ();
g_assert (nursery_section->size >= max_garbage_amount);
/* world must be stopped already */
- TV_GETTIME (all_atv);
- atv = all_atv;
-
TV_GETTIME (btv);
time_minor_pre_collection_fragment_clear += TV_ELAPSED (atv, btv);
mono_profiler_gc_event (MONO_GC_EVENT_MARK_START, 0);
pin_from_roots (sgen_get_nursery_start (), nursery_next, WORKERS_DISTRIBUTE_GRAY_QUEUE);
/* pin cemented objects */
- sgen_cement_iterate (pin_stage_object_callback, NULL);
+ sgen_pin_cemented_objects ();
/* identify pinned objects */
sgen_optimize_pin_queue ();
sgen_pinning_setup_section (nursery_section);
MONO_GC_CHECKPOINT_4 (GENERATION_NURSERY);
- if (!sgen_collection_is_parallel ()) {
- ctx.scan_func = current_object_ops.scan_object;
- ctx.copy_func = NULL;
- ctx.queue = &gray_queue;
- sgen_drain_gray_stack (-1, ctx);
- }
+ /* FIXME: why is this here? */
+ ctx.scan_func = current_object_ops.scan_object;
+ ctx.copy_func = NULL;
+ ctx.queue = &gray_queue;
+ sgen_drain_gray_stack (-1, ctx);
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_registered_roots ();
MONO_GC_CHECKPOINT_7 (GENERATION_NURSERY);
- g_assert (!sgen_collection_is_parallel () && !sgen_collection_is_concurrent ());
-
- if (sgen_collection_is_parallel () || sgen_collection_is_concurrent ())
- g_assert (sgen_gray_object_queue_is_empty (&gray_queue));
+ g_assert (!sgen_collection_is_concurrent ());
/* Scan the list of objects ready for finalization. If */
- sfejd_fin_ready = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
- sfejd_fin_ready->list = fin_ready_list;
- sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_fin_ready);
-
- sfejd_critical_fin = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
- sfejd_critical_fin->list = critical_fin_list;
- sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_critical_fin);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, fin_ready_list);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, critical_fin_list);
MONO_GC_CHECKPOINT_8 (GENERATION_NURSERY);
major_collector.finish_nursery_collection ();
- TV_GETTIME (all_btv);
- gc_stats.minor_gc_time += TV_ELAPSED (all_atv, all_btv);
+ TV_GETTIME (last_minor_collection_end_tv);
+ gc_stats.minor_gc_time += TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
if (heap_dump_file)
dump_heap ("minor", gc_stats.minor_gc_count - 1, NULL);
static void
scan_nursery_objects_callback (char *obj, size_t size, ScanCopyContext *ctx)
{
- ctx->scan_func (obj, ctx->queue);
+ /*
+ * This is called on all objects in the nursery, including pinned ones, so we need
+ * to use sgen_obj_get_descriptor_safe(), which masks out the vtable tag bits.
+ */
+ ctx->scan_func (obj, sgen_obj_get_descriptor_safe (obj), ctx->queue);
}
static void
GCRootReport root_report = { 0 };
ScanFromRegisteredRootsJobData *scrrjd_normal, *scrrjd_wbarrier;
ScanThreadDataJobData *stdjd;
- ScanFinalizerEntriesJobData *sfejd_fin_ready, *sfejd_critical_fin;
ScanCopyContext ctx;
if (concurrent_collection_in_progress) {
*
* FIXME: We could evict now!
*/
- sgen_cement_iterate (pin_stage_object_callback, NULL);
+ sgen_pin_cemented_objects ();
}
if (!concurrent_collection_in_progress)
}
sgen_los_pin_object (bigobj->data);
if (SGEN_OBJECT_HAS_REFERENCES (bigobj->data))
- GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data);
+ GRAY_OBJECT_ENQUEUE (WORKERS_DISTRIBUTE_GRAY_QUEUE, bigobj->data, sgen_obj_get_descriptor (bigobj->data));
if (G_UNLIKELY (do_pin_stats))
sgen_pin_stats_register_object ((char*) bigobj->data, safe_object_get_size ((MonoObject*) bigobj->data));
SGEN_LOG (6, "Marked large object %p (%s) size: %lu from roots", bigobj->data, safe_name (bigobj->data), (unsigned long)sgen_los_object_size (bigobj));
main_gc_thread = mono_native_thread_self ();
#endif
- if (!concurrent_collection_in_progress && major_collector.is_parallel) {
- sgen_workers_start_all_workers ();
- sgen_workers_start_marking ();
- }
-
if (mono_profiler_get_events () & MONO_PROFILE_GC_ROOTS)
report_registered_roots ();
TV_GETTIME (atv);
report_finalizer_roots ();
/* scan the list of objects ready for finalization */
- sfejd_fin_ready = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
- sfejd_fin_ready->list = fin_ready_list;
- sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_fin_ready);
-
- sfejd_critical_fin = sgen_alloc_internal_dynamic (sizeof (ScanFinalizerEntriesJobData), INTERNAL_MEM_WORKER_JOB_DATA, TRUE);
- sfejd_critical_fin->list = critical_fin_list;
- sgen_workers_enqueue_job (job_scan_finalizer_entries, sfejd_critical_fin);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, fin_ready_list);
+ sgen_workers_enqueue_job (job_scan_finalizer_entries, critical_fin_list);
if (scan_mod_union) {
g_assert (finish_up_concurrent_mark);
static void
join_workers (void)
{
- if (concurrent_collection_in_progress || major_collector.is_parallel) {
+ if (concurrent_collection_in_progress) {
gray_queue_redirect (&gray_queue);
sgen_workers_join ();
}
TV_GETTIME (btv);
- if (concurrent_collection_in_progress || major_collector.is_parallel)
+ if (concurrent_collection_in_progress)
join_workers ();
if (concurrent_collection_in_progress) {
static gboolean
major_do_collection (const char *reason)
{
- TV_DECLARE (all_atv);
- TV_DECLARE (all_btv);
+ TV_DECLARE (time_start);
+ TV_DECLARE (time_end);
size_t old_next_pin_slot;
if (disable_major_collections)
}
/* world must be stopped already */
- TV_GETTIME (all_atv);
+ TV_GETTIME (time_start);
major_start_collection (FALSE, &old_next_pin_slot);
major_finish_collection (reason, old_next_pin_slot, FALSE);
- TV_GETTIME (all_btv);
- gc_stats.major_gc_time += TV_ELAPSED (all_atv, all_btv);
+ TV_GETTIME (time_end);
+ gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
/* FIXME: also report this to the user, preferably in gc-end. */
if (major_collector.get_and_reset_num_major_objects_marked)
static void
major_start_concurrent_collection (const char *reason)
{
+ TV_DECLARE (time_start);
+ TV_DECLARE (time_end);
long long num_objects_marked;
if (disable_major_collections)
return;
+ TV_GETTIME (time_start);
+ SGEN_TV_GETTIME (time_major_conc_collection_start);
+
num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
g_assert (num_objects_marked == 0);
num_objects_marked = major_collector.get_and_reset_num_major_objects_marked ();
MONO_GC_CONCURRENT_START_END (GENERATION_OLD, num_objects_marked);
+ TV_GETTIME (time_end);
+ gc_stats.major_gc_time += TV_ELAPSED (time_start, time_end);
+
current_collection_generation = -1;
}
static gboolean
major_update_or_finish_concurrent_collection (gboolean force_finish)
{
+ TV_DECLARE (total_start);
+ TV_DECLARE (total_end);
SgenGrayQueue unpin_queue;
memset (&unpin_queue, 0, sizeof (unpin_queue));
+ TV_GETTIME (total_start);
+
MONO_GC_CONCURRENT_UPDATE_FINISH_BEGIN (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
binary_protocol_concurrent_update_finish ();
sgen_los_update_cardtable_mod_union ();
MONO_GC_CONCURRENT_UPDATE_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
+
+ TV_GETTIME (total_end);
+ gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end);
+
return FALSE;
}
*/
wait_for_workers_to_finish ();
+ SGEN_TV_GETTIME (time_major_conc_collection_end);
+ gc_stats.major_gc_time_concurrent += SGEN_TV_ELAPSED (time_major_conc_collection_start, time_major_conc_collection_end);
+
major_collector.update_cardtable_mod_union ();
sgen_los_update_cardtable_mod_union ();
MONO_GC_CONCURRENT_FINISH_END (GENERATION_OLD, major_collector.get_and_reset_num_major_objects_marked ());
+ TV_GETTIME (total_end);
+ gc_stats.major_gc_time += TV_ELAPSED (total_start, total_end) - TV_ELAPSED (last_minor_collection_start_tv, last_minor_collection_end_tv);
+
current_collection_generation = -1;
return TRUE;
static void
collect_references (HeapWalkInfo *hwi, char *start, size_t size)
{
+ mword desc = sgen_obj_get_descriptor (start);
+
#include "sgen-scan-object.h"
}
return MAX_SMALL_OBJ_SIZE;
}
+void
+mono_gc_set_string_length (MonoString *str, gint32 new_length)
+{
+ mono_unichar2 *new_end = str->chars + new_length;
+
+ /* zero the discarded string. This null-delimits the string and allows
+ * the space to be reclaimed by SGen. */
+
+ if (nursery_canaries_enabled () && sgen_ptr_in_nursery (str)) {
+ CHECK_CANARY_FOR_OBJECT (str);
+ memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2) + CANARY_SIZE);
+ memcpy (new_end + 1 , CANARY_STRING, CANARY_SIZE);
+ } else {
+ memset (new_end, 0, (str->length - new_length + 1) * sizeof (mono_unichar2));
+ }
+
+ str->length = new_length;
+}
+
gboolean
mono_gc_user_markers_supported (void)
{
char *minor_collector_opt = NULL;
size_t max_heap = 0;
size_t soft_limit = 0;
- int num_workers;
int result;
int dummy;
gboolean debug_print_allowance = FALSE;
sgen_init_fin_weak_hash ();
sgen_init_stw ();
sgen_init_hash_table ();
+ sgen_init_descriptors ();
+ sgen_init_gray_queues ();
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_SECTION, SGEN_SIZEOF_GC_MEM_SECTION);
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_FINALIZE_READY_ENTRY, sizeof (FinalizeReadyEntry));
if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep")) {
use_marksweep_major:
sgen_marksweep_init (&major_collector);
- } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed")) {
- sgen_marksweep_fixed_init (&major_collector);
- } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-par")) {
- sgen_marksweep_par_init (&major_collector);
- } else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-fixed-par")) {
- sgen_marksweep_fixed_par_init (&major_collector);
} else if (!major_collector_opt || !strcmp (major_collector_opt, "marksweep-conc")) {
sgen_marksweep_conc_init (&major_collector);
} else {
goto use_marksweep_major;
}
- if (have_split_nursery && major_collector.is_parallel) {
- sgen_env_var_error (MONO_GC_PARAMS_NAME, "Disabling split minor collector.", "`minor=split` is not supported with the parallel collector yet.");
- have_split_nursery = FALSE;
- }
-
- num_workers = mono_cpu_count ();
- g_assert (num_workers > 0);
- if (num_workers > 16)
- num_workers = 16;
-
///* Keep this the default for now */
/* Precise marking is broken on all supported targets. Disable until fixed. */
conservative_stack_mark = TRUE;
}
continue;
}
- if (g_str_has_prefix (opt, "workers=")) {
- long val;
- char *endptr;
- if (!major_collector.is_parallel) {
- sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "The `workers` option can only be used for parallel collectors.");
- continue;
- }
- opt = strchr (opt, '=') + 1;
- val = strtol (opt, &endptr, 10);
- if (!*opt || *endptr) {
- sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "Cannot parse the `workers` option value.");
- continue;
- }
- if (val <= 0 || val > 16) {
- sgen_env_var_error (MONO_GC_PARAMS_NAME, "Using default value.", "The number of `workers` must be in the range 1 to 16.");
- continue;
- }
- num_workers = (int)val;
- continue;
- }
if (g_str_has_prefix (opt, "stack-mark=")) {
opt = strchr (opt, '=') + 1;
if (!strcmp (opt, "precise")) {
}
if (!strcmp (opt, "cementing")) {
- if (major_collector.is_parallel) {
- sgen_env_var_error (MONO_GC_PARAMS_NAME, "Ignoring.", "`cementing` is not supported for the parallel major collector.");
- continue;
- }
cement_enabled = TRUE;
continue;
}
fprintf (stderr, " max-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
fprintf (stderr, " soft-heap-limit=n (where N is an integer, possibly with a k, m or a g suffix)\n");
fprintf (stderr, " nursery-size=N (where N is an integer, possibly with a k, m or a g suffix)\n");
- fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par', 'marksweep-fixed' or 'marksweep-fixed-par')\n");
+ fprintf (stderr, " major=COLLECTOR (where COLLECTOR is `marksweep', `marksweep-conc', `marksweep-par')\n");
fprintf (stderr, " minor=COLLECTOR (where COLLECTOR is `simple' or `split')\n");
fprintf (stderr, " wbarrier=WBARRIER (where WBARRIER is `remset' or `cardtable')\n");
fprintf (stderr, " stack-mark=MARK-METHOD (where MARK-METHOD is 'precise' or 'conservative')\n");
g_strfreev (opts);
}
- if (major_collector.is_parallel) {
- cement_enabled = FALSE;
- sgen_workers_init (num_workers);
- } else if (major_collector.is_concurrent) {
+ if (major_collector.is_concurrent)
sgen_workers_init (1);
- }
if (major_collector_opt)
g_free (major_collector_opt);
if (opt [0] == ':')
opt++;
if (opt [0]) {
-#ifdef HOST_WIN32
- char *rf = g_strdup_printf ("%s.%d", opt, GetCurrentProcessId ());
-#else
- char *rf = g_strdup_printf ("%s.%d", opt, getpid ());
-#endif
+ char *rf = g_strdup_printf ("%s.%d", opt, mono_process_current_pid ());
gc_debug_file = fopen (rf, "wb");
if (!gc_debug_file)
gc_debug_file = stderr;
*colon = '\0';
}
binary_protocol_init (filename, (long long)limit);
+ } else if (!strcmp (opt, "nursery-canaries")) {
+ do_verify_nursery = TRUE;
+ sgen_set_use_managed_allocator (FALSE);
+ enable_nursery_canaries = TRUE;
} else if (!sgen_bridge_handle_gc_debug (opt)) {
sgen_env_var_error (MONO_GC_DEBUG_NAME, "Ignoring.", "Unknown option `%s`.", opt);
fprintf (stderr, " print-pinning\n");
fprintf (stderr, " heap-dump=<filename>\n");
fprintf (stderr, " binary-protocol=<filename>[:<file-size-limit>]\n");
+ fprintf (stderr, " nursery-canaries\n");
sgen_bridge_print_gc_debug_usage ();
fprintf (stderr, "\n");
g_strfreev (opts);
}
- if (major_collector.is_parallel) {
- if (heap_dump_file) {
- sgen_env_var_error (MONO_GC_DEBUG_NAME, "Disabling.", "Cannot do `heap-dump` with the parallel collector.");
- fclose (heap_dump_file);
- heap_dump_file = NULL;
- }
- if (do_pin_stats) {
- sgen_env_var_error (MONO_GC_DEBUG_NAME, "Disabling.", "`print-pinning` is not supported with the parallel collector.");
- do_pin_stats = FALSE;
- }
- }
-
if (major_collector.post_param_init)
major_collector.post_param_init (&major_collector);
fin_callbacks = *callbacks;
}
+
+
+
+
#endif /* HAVE_SGEN_GC */
#endif
#ifdef HEAVY_STATISTICS
-#define HEAVY_STAT(x) x
-
extern long long stat_objects_alloced_degraded;
extern long long stat_bytes_alloced_degraded;
extern long long stat_copy_object_called_major;
extern long long stat_objects_copied_major;
-#else
-#define HEAVY_STAT(x)
#endif
#define SGEN_ASSERT(level, a, ...) do { \
* The values are already shifted.
* The forwarding address is stored in the sync block.
*/
-#define SGEN_FORWARDED_BIT 1
-#define SGEN_PINNED_BIT 2
+
#define SGEN_VTABLE_BITS_MASK 0x3
+#include "sgen-tagged-pointer.h"
+
+#define SGEN_POINTER_IS_TAGGED_FORWARDED(p) SGEN_POINTER_IS_TAGGED_1((p))
+#define SGEN_POINTER_TAG_FORWARDED(p) SGEN_POINTER_TAG_1((p))
+
+#define SGEN_POINTER_IS_TAGGED_PINNED(p) SGEN_POINTER_IS_TAGGED_2((p))
+#define SGEN_POINTER_TAG_PINNED(p) SGEN_POINTER_TAG_2((p))
+
+#define SGEN_POINTER_UNTAG_VTABLE(p) SGEN_POINTER_UNTAG_12((p))
+
/* returns NULL if not forwarded, or the forwarded address */
-#define SGEN_OBJECT_IS_FORWARDED(obj) (((mword*)(obj))[0] & SGEN_FORWARDED_BIT ? (void*)(((mword*)(obj))[0] & ~SGEN_VTABLE_BITS_MASK) : NULL)
-#define SGEN_OBJECT_IS_PINNED(obj) (((mword*)(obj))[0] & SGEN_PINNED_BIT)
+#define SGEN_VTABLE_IS_FORWARDED(vtable) (SGEN_POINTER_IS_TAGGED_FORWARDED ((vtable)) ? SGEN_POINTER_UNTAG_VTABLE ((vtable)) : NULL)
+#define SGEN_OBJECT_IS_FORWARDED(obj) (SGEN_VTABLE_IS_FORWARDED (((mword*)(obj))[0]))
+
+#define SGEN_VTABLE_IS_PINNED(vtable) SGEN_POINTER_IS_TAGGED_PINNED ((vtable))
+#define SGEN_OBJECT_IS_PINNED(obj) (SGEN_VTABLE_IS_PINNED (((mword*)(obj))[0]))
/* set the forwarded address fw_addr for object obj */
#define SGEN_FORWARD_OBJECT(obj,fw_addr) do { \
- ((mword*)(obj))[0] = (mword)(fw_addr) | SGEN_FORWARDED_BIT; \
+ *(void**)(obj) = SGEN_POINTER_TAG_FORWARDED ((fw_addr)); \
} while (0)
#define SGEN_PIN_OBJECT(obj) do { \
- ((mword*)(obj))[0] |= SGEN_PINNED_BIT; \
+ *(void**)(obj) = SGEN_POINTER_TAG_PINNED (*(void**)(obj)); \
} while (0)
#define SGEN_UNPIN_OBJECT(obj) do { \
- ((mword*)(obj))[0] &= ~SGEN_PINNED_BIT; \
+ *(void**)(obj) = SGEN_POINTER_UNTAG_2 (*(void**)(obj)); \
} while (0)
/*
* Since we set bits in the vtable, use the macro to load it from the pointer to
* an object that is potentially pinned.
*/
-#define SGEN_LOAD_VTABLE(addr) ((*(mword*)(addr)) & ~SGEN_VTABLE_BITS_MASK)
-
-static inline MONO_ALWAYS_INLINE void
-GRAY_OBJECT_ENQUEUE (SgenGrayQueue *queue, char* obj)
-{
-#if defined(SGEN_GRAY_OBJECT_ENQUEUE) || SGEN_MAX_DEBUG_LEVEL >= 9
- sgen_gray_object_enqueue (queue, obj);
-#else
- if (G_UNLIKELY (!queue->first || queue->cursor == GRAY_LAST_CURSOR_POSITION (queue->first))) {
- sgen_gray_object_enqueue (queue, obj);
- } else {
- HEAVY_STAT (gc_stats.gray_queue_enqueue_fast_path ++);
-
- *++queue->cursor = obj;
-#ifdef SGEN_HEAVY_BINARY_PROTOCOL
- binary_protocol_gray_enqueue (queue, queue->cursor, obj);
-#endif
- }
-
- PREFETCH (obj);
-#endif
-}
-
-static inline MONO_ALWAYS_INLINE void
-GRAY_OBJECT_DEQUEUE (SgenGrayQueue *queue, char** obj)
-{
-#if defined(SGEN_GRAY_OBJECT_ENQUEUE) || SGEN_MAX_DEBUG_LEVEL >= 9
- *obj = sgen_gray_object_enqueue (queue);
-#else
- if (!queue->first) {
- HEAVY_STAT (gc_stats.gray_queue_dequeue_fast_path ++);
-
- *obj = NULL;
-#ifdef SGEN_HEAVY_BINARY_PROTOCOL
- binary_protocol_gray_dequeue (queue, queue->cursor, *obj);
-#endif
- } else if (G_UNLIKELY (queue->cursor == GRAY_FIRST_CURSOR_POSITION (queue->first))) {
- *obj = sgen_gray_object_dequeue (queue);
- } else {
- HEAVY_STAT (gc_stats.gray_queue_dequeue_fast_path ++);
-
- *obj = *queue->cursor--;
-#ifdef SGEN_HEAVY_BINARY_PROTOCOL
- binary_protocol_gray_dequeue (queue, queue->cursor + 1, *obj);
-#endif
- }
-#endif
-}
+#define SGEN_LOAD_VTABLE(addr) SGEN_POINTER_UNTAG_12 (*(void**)(addr))
/*
List of what each bit on of the vtable gc bits means.
};
typedef void (*CopyOrMarkObjectFunc) (void**, SgenGrayQueue*);
-typedef void (*ScanObjectFunc) (char*, SgenGrayQueue*);
+typedef void (*ScanObjectFunc) (char *obj, mword desc, SgenGrayQueue*);
typedef void (*ScanVTypeFunc) (char*, mword desc, SgenGrayQueue* BINARY_PROTOCOL_ARG (size_t size));
typedef struct
void sgen_add_to_global_remset (gpointer ptr, gpointer obj) MONO_INTERNAL;
int sgen_get_current_collection_generation (void) MONO_INTERNAL;
-gboolean sgen_collection_is_parallel (void) MONO_INTERNAL;
gboolean sgen_collection_is_concurrent (void) MONO_INTERNAL;
gboolean sgen_concurrent_collection_in_progress (void) MONO_INTERNAL;
sgen_nursery_is_to_space (char *object)
{
size_t idx = (object - sgen_nursery_start) >> SGEN_TO_SPACE_GRANULE_BITS;
- size_t byte = idx / 8;
+ size_t byte = idx >> 3;
size_t bit = idx & 0x7;
SGEN_ASSERT (4, sgen_ptr_in_nursery (object), "object %p is not in nursery [%p - %p]", object, sgen_get_nursery_start (), sgen_get_nursery_end ());
gboolean is_split;
char* (*alloc_for_promotion) (MonoVTable *vtable, char *obj, size_t objsize, gboolean has_references);
- char* (*par_alloc_for_promotion) (MonoVTable *vtable, char *obj, size_t objsize, gboolean has_references);
SgenObjectOperations serial_ops;
- SgenObjectOperations parallel_ops;
void (*prepare_to_space) (char *to_space_bitmap, size_t space_bitmap_size);
void (*clear_fragments) (void);
typedef struct _SgenMajorCollector SgenMajorCollector;
struct _SgenMajorCollector {
size_t section_size;
- gboolean is_parallel;
gboolean is_concurrent;
gboolean supports_cardtable;
gboolean sweeps_lazily;
SgenObjectOperations major_concurrent_ops;
void* (*alloc_object) (MonoVTable *vtable, size_t size, gboolean has_references);
- void* (*par_alloc_object) (MonoVTable *vtable, size_t size, gboolean has_references);
void (*free_pinned_object) (char *obj, size_t size);
void (*iterate_objects) (IterateObjectsFlags flags, IterateObjectCallbackFunc callback, void *data);
void (*free_non_pinned_object) (char *obj, size_t size);
* mono_array_length_fast not using the object's vtable.
*/
if (klass == mono_defaults.string_class) {
- return sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
+ return offsetof (MonoString, chars) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
} else if (klass->rank) {
MonoArray *array = (MonoArray*)o;
size_t size = sizeof (MonoArray) + klass->sizes.element_size * mono_array_length_fast (array);
}
}
+static inline mword
+sgen_vtable_get_descriptor (MonoVTable *vtable)
+{
+ return (mword)vtable->gc_descr;
+}
+
+static inline mword
+sgen_obj_get_descriptor (char *obj)
+{
+ MonoVTable *vtable = ((MonoObject*)obj)->vtable;
+ SGEN_ASSERT (0, !SGEN_POINTER_IS_TAGGED_1_OR_2 (vtable), "Object can't be tagged");
+ return sgen_vtable_get_descriptor (vtable);
+}
+
+static inline mword
+sgen_obj_get_descriptor_safe (char *obj)
+{
+ MonoVTable *vtable = (MonoVTable*)SGEN_LOAD_VTABLE (obj);
+ return sgen_vtable_get_descriptor (vtable);
+}
+
/*
* This function can be called on an object whose first word, the
* vtable field, is not intact. This is necessary for the parallel
if (type == DESC_TYPE_RUN_LENGTH || type == DESC_TYPE_SMALL_BITMAP) {
mword size = descr & 0xfff8;
if (size == 0) /* This is used to encode a string */
- return sizeof (MonoString) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
+ return offsetof (MonoString, chars) + 2 * mono_string_length_fast ((MonoString*) o) + 2;
return size;
} else if (type == DESC_TYPE_VECTOR) {
int element_size = ((descr) >> VECTOR_ELSIZE_SHIFT) & MAX_ELEMENT_SIZE;
return sgen_par_object_get_size ((MonoVTable*)SGEN_LOAD_VTABLE (obj), obj);
}
+/*
+ * This variant guarantees to return the exact size of the object
+ * before alignment. Needed for canary support.
+ */
+static inline guint
+sgen_safe_object_get_size_unaligned (MonoObject *obj)
+{
+ char *forwarded;
+
+ if ((forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) {
+ obj = (MonoObject*)forwarded;
+ }
+
+ return slow_object_get_size ((MonoVTable*)SGEN_LOAD_VTABLE (obj), obj);
+}
+
const char* sgen_safe_name (void* obj) MONO_INTERNAL;
gboolean sgen_object_is_live (void *obj) MONO_INTERNAL;
void sgen_nursery_alloc_prepare_for_major (void) MONO_INTERNAL;
char* sgen_alloc_for_promotion (char *obj, size_t objsize, gboolean has_references) MONO_INTERNAL;
-char* sgen_par_alloc_for_promotion (char *obj, size_t objsize, gboolean has_references) MONO_INTERNAL;
/* TLS Data */
void sgen_qsort (void *base, size_t nel, size_t width, int (*compar) (const void*, const void*)) MONO_INTERNAL;
gint64 sgen_timestamp (void) MONO_INTERNAL;
+/*
+ * Canary (guard word) support
+ * Notes:
+ * - CANARY_SIZE must be multiple of word size in bytes
+ * - Canary space is not included on checks against SGEN_MAX_SMALL_OBJ_SIZE
+ */
+
+gboolean nursery_canaries_enabled (void) MONO_INTERNAL;
+
+#define CANARY_SIZE 8
+#define CANARY_STRING "koupepia"
+
+#define CANARIFY_SIZE(size) if (nursery_canaries_enabled ()) { \
+ size = size + CANARY_SIZE; \
+ }
+
+#define CANARIFY_ALLOC(addr,size) if (nursery_canaries_enabled ()) { \
+ memcpy ((char*) (addr) + (size), CANARY_STRING, CANARY_SIZE); \
+ }
+
+#define CANARY_VALID(addr) (strncmp ((char*) (addr), CANARY_STRING, CANARY_SIZE) == 0)
+
+#define CHECK_CANARY_FOR_OBJECT(addr) if (nursery_canaries_enabled ()) { \
+ char* canary_ptr = (char*) (addr) + sgen_safe_object_get_size_unaligned ((MonoObject *) (addr)); \
+ if (!CANARY_VALID(canary_ptr)) { \
+ char canary_copy[CANARY_SIZE +1]; \
+ strncpy (canary_copy, canary_ptr, 8); \
+ canary_copy[CANARY_SIZE] = 0; \
+ g_error ("CORRUPT CANARY:\naddr->%p\ntype->%s\nexcepted->'%s'\nfound->'%s'\n", (char*) addr, ((MonoObject*)addr)->vtable->klass->name, CANARY_STRING, canary_copy); \
+ } }
+
#endif /* HAVE_SGEN_GC */
#endif /* __MONO_SGENGC_H__ */
#include "utils/mono-counters.h"
#include "sgen-protocol.h"
+#ifdef HEAVY_STATISTICS
+unsigned long long stat_gray_queue_section_alloc;
+unsigned long long stat_gray_queue_section_free;
+unsigned long long stat_gray_queue_enqueue_fast_path;
+unsigned long long stat_gray_queue_dequeue_fast_path;
+unsigned long long stat_gray_queue_enqueue_slow_path;
+unsigned long long stat_gray_queue_dequeue_slow_path;
+#endif
+
#define GRAY_QUEUE_LENGTH_LIMIT 64
#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
{
GrayQueueSection *section;
- HEAVY_STAT (gc_stats.gray_queue_section_alloc ++);
+ HEAVY_STAT (stat_gray_queue_section_alloc ++);
if (queue->alloc_prepare_func)
queue->alloc_prepare_func (queue);
/* Link it with the others */
section->next = queue->first;
queue->first = section;
- queue->cursor = (char**)section->objects - 1;
+ queue->cursor = section->entries - 1;
}
void
sgen_gray_object_free_queue_section (GrayQueueSection *section)
{
- HEAVY_STAT (gc_stats.gray_queue_section_free ++);
+ HEAVY_STAT (stat_gray_queue_section_free ++);
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_FREED);
sgen_free_internal (section, INTERNAL_MEM_GRAY_QUEUE);
*/
void
-sgen_gray_object_enqueue (SgenGrayQueue *queue, char *obj)
+sgen_gray_object_enqueue (SgenGrayQueue *queue, char *obj, mword desc)
{
- HEAVY_STAT (gc_stats.gray_queue_enqueue_slow_path ++);
+ GrayQueueEntry entry = { obj, desc };
+
+ HEAVY_STAT (stat_gray_queue_enqueue_slow_path ++);
SGEN_ASSERT (9, obj, "enqueueing a null object");
//sgen_check_objref (obj);
}
STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
SGEN_ASSERT (9, queue->cursor <= GRAY_LAST_CURSOR_POSITION (queue->first), "gray queue %p overflow, first %p, cursor %p", queue, queue->first, queue->cursor);
- *++queue->cursor = obj;
+ *++queue->cursor = entry;
#ifdef SGEN_HEAVY_BINARY_PROTOCOL
binary_protocol_gray_enqueue (queue, queue->cursor, obj);
#endif
}
-char*
+GrayQueueEntry
sgen_gray_object_dequeue (SgenGrayQueue *queue)
{
- char *obj;
+ GrayQueueEntry entry;
- HEAVY_STAT (gc_stats.gray_queue_dequeue_slow_path ++);
+ HEAVY_STAT (stat_gray_queue_dequeue_slow_path ++);
- if (sgen_gray_object_queue_is_empty (queue))
- return NULL;
+ if (sgen_gray_object_queue_is_empty (queue)) {
+ entry.obj = NULL;
+ return entry;
+ }
STATE_ASSERT (queue->first, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
- SGEN_ASSERT (9, queue->cursor >= (char**)queue->first->objects, "gray queue %p underflow, first %p, cursor %d", queue, queue->first, queue->cursor);
+ SGEN_ASSERT (9, queue->cursor >= GRAY_FIRST_CURSOR_POSITION (queue->first), "gray queue %p underflow, first %p, cursor %d", queue, queue->first, queue->cursor);
- obj = *queue->cursor--;
+ entry = *queue->cursor--;
#ifdef SGEN_HEAVY_BINARY_PROTOCOL
binary_protocol_gray_dequeue (queue, queue->cursor + 1, obj);
#endif
- if (G_UNLIKELY (queue->cursor == (char**)queue->first->objects - 1)) {
+ if (G_UNLIKELY (queue->cursor < GRAY_FIRST_CURSOR_POSITION (queue->first))) {
GrayQueueSection *section = queue->first;
queue->first = section->next;
section->next = queue->free_list;
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
queue->free_list = section;
- queue->cursor = queue->first ? (char**)queue->first->objects + queue->first->size - 1 : NULL;
+ queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
}
- return obj;
+ return entry;
}
GrayQueueSection*
queue->first = section->next;
section->next = NULL;
- section->size = queue->cursor - (char**)section->objects + 1;
+ section->size = queue->cursor - section->entries + 1;
- queue->cursor = queue->first ? (char**)queue->first->objects + queue->first->size - 1 : NULL;
+ queue->cursor = queue->first ? queue->first->entries + queue->first->size - 1 : NULL;
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_ENQUEUED, GRAY_QUEUE_SECTION_STATE_FLOATING);
STATE_TRANSITION (section, GRAY_QUEUE_SECTION_STATE_FLOATING, GRAY_QUEUE_SECTION_STATE_ENQUEUED);
if (queue->first)
- queue->first->size = queue->cursor - (char**)queue->first->objects + 1;
+ queue->first->size = queue->cursor - queue->first->entries + 1;
section->next = queue->first;
queue->first = section;
- queue->cursor = (char**)queue->first->objects + queue->first->size - 1;
+ queue->cursor = queue->first->entries + queue->first->size - 1;
#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
if (queue->enqueue_check_func) {
int i;
for (i = 0; i < section->size; ++i)
- queue->enqueue_check_func (section->objects [i]);
+ queue->enqueue_check_func (section->entries [i].obj);
}
#endif
}
void
-sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func)
+sgen_gray_object_queue_trim_free_list (SgenGrayQueue *queue)
{
GrayQueueSection *section, *next;
- int i;
-
- g_assert (sgen_gray_object_queue_is_empty (queue));
-
- queue->alloc_prepare_func = NULL;
- queue->alloc_prepare_data = NULL;
-#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
- queue->enqueue_check_func = enqueue_check_func;
-#endif
-
- /* Free the extra sections allocated during the last collection */
- i = 0;
+ int i = 0;
for (section = queue->free_list; section && i < GRAY_QUEUE_LENGTH_LIMIT - 1; section = section->next) {
STATE_ASSERT (section, GRAY_QUEUE_SECTION_STATE_FREE_LIST);
i ++;
}
}
+void
+sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func)
+{
+ g_assert (sgen_gray_object_queue_is_empty (queue));
+
+ queue->alloc_prepare_func = NULL;
+ queue->alloc_prepare_data = NULL;
+#ifdef SGEN_CHECK_GRAY_OBJECT_ENQUEUE
+ queue->enqueue_check_func = enqueue_check_func;
+#endif
+
+ /* Free the extra sections allocated during the last collection */
+ sgen_gray_object_queue_trim_free_list (queue);
+}
+
static void
invalid_prepare_func (SgenGrayQueue *queue)
{
if (queue->enqueue_check_func) {
int i;
for (i = 0; i < section->size; ++i)
- queue->enqueue_check_func (section->objects [i]);
+ queue->enqueue_check_func (section->entries [i].obj);
}
#endif
unlock_section_queue (queue);
}
+void
+sgen_init_gray_queues (void)
+{
+#ifdef HEAVY_STATISTICS
+ mono_counters_register ("Gray Queue alloc section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_alloc);
+ mono_counters_register ("Gray Queue free section", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_section_free);
+ mono_counters_register ("Gray Queue enqueue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_fast_path);
+ mono_counters_register ("Gray Queue dequeue fast path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_fast_path);
+ mono_counters_register ("Gray Queue enqueue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_enqueue_slow_path);
+ mono_counters_register ("Gray Queue dequeue slow path", MONO_COUNTER_GC | MONO_COUNTER_ULONG, &stat_gray_queue_dequeue_slow_path);
+#endif
+}
#endif
* array and another 1 for the actual value in the array.
*/
-#define SGEN_GRAY_QUEUE_SECTION_SIZE (128 - 3)
+/* SGEN_GRAY_QUEUE_HEADER_SIZE is number of machine words */
+#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
+#define SGEN_GRAY_QUEUE_HEADER_SIZE 4
+#else
+#define SGEN_GRAY_QUEUE_HEADER_SIZE 2
+#endif
+
+#define SGEN_GRAY_QUEUE_SECTION_SIZE (128 - SGEN_GRAY_QUEUE_HEADER_SIZE)
#ifdef SGEN_CHECK_GRAY_OBJECT_SECTIONS
typedef enum {
} GrayQueueSectionState;
#endif
+typedef struct _GrayQueueEntry GrayQueueEntry;
+struct _GrayQueueEntry {
+ char *obj;
+ mword desc;
+};
+
/*
* This is a stack now instead of a queue, so the most recently added items are removed
* first, improving cache locality, and keeping the stack size manageable.
#endif
int size;
GrayQueueSection *next;
- char *objects [SGEN_GRAY_QUEUE_SECTION_SIZE];
+ GrayQueueEntry entries [SGEN_GRAY_QUEUE_SECTION_SIZE];
};
typedef struct _SgenGrayQueue SgenGrayQueue;
typedef void (*GrayQueueEnqueueCheckFunc) (char*);
struct _SgenGrayQueue {
- char **cursor;
+ GrayQueueEntry *cursor;
GrayQueueSection *first;
GrayQueueSection *free_list;
GrayQueueAllocPrepareFunc alloc_prepare_func;
#endif
};
-#define GRAY_LAST_CURSOR_POSITION(s) ((char**)(s)->objects + SGEN_GRAY_QUEUE_SECTION_SIZE - 1)
-#define GRAY_FIRST_CURSOR_POSITION(s) ((char**)(s)->objects)
+#define GRAY_LAST_CURSOR_POSITION(s) ((s)->entries + SGEN_GRAY_QUEUE_SECTION_SIZE - 1)
+#define GRAY_FIRST_CURSOR_POSITION(s) ((s)->entries)
+
+#ifdef HEAVY_STATISTICS
+extern unsigned long long stat_gray_queue_section_alloc;
+extern unsigned long long stat_gray_queue_section_free;
+extern unsigned long long stat_gray_queue_enqueue_fast_path;
+extern unsigned long long stat_gray_queue_dequeue_fast_path;
+extern unsigned long long stat_gray_queue_enqueue_slow_path;
+extern unsigned long long stat_gray_queue_dequeue_slow_path;
+#endif
+
+void sgen_init_gray_queues (void) MONO_INTERNAL;
-void sgen_gray_object_enqueue (SgenGrayQueue *queue, char *obj) MONO_INTERNAL;
-char* sgen_gray_object_dequeue (SgenGrayQueue *queue) MONO_INTERNAL;
+void sgen_gray_object_enqueue (SgenGrayQueue *queue, char *obj, mword desc) MONO_INTERNAL;
+GrayQueueEntry sgen_gray_object_dequeue (SgenGrayQueue *queue) MONO_INTERNAL;
GrayQueueSection* sgen_gray_object_dequeue_section (SgenGrayQueue *queue) MONO_INTERNAL;
void sgen_gray_object_enqueue_section (SgenGrayQueue *queue, GrayQueueSection *section) MONO_INTERNAL;
+void sgen_gray_object_queue_trim_free_list (SgenGrayQueue *queue) MONO_INTERNAL;
void sgen_gray_object_queue_init (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func) MONO_INTERNAL;
void sgen_gray_object_queue_init_invalid (SgenGrayQueue *queue) MONO_INTERNAL;
void sgen_gray_object_queue_init_with_alloc_prepare (SgenGrayQueue *queue, GrayQueueEnqueueCheckFunc enqueue_check_func,
return queue->first == NULL;
}
+static inline MONO_ALWAYS_INLINE void
+GRAY_OBJECT_ENQUEUE (SgenGrayQueue *queue, char* obj, mword desc)
+{
+#if SGEN_MAX_DEBUG_LEVEL >= 9
+ sgen_gray_object_enqueue (queue, obj, desc);
+#else
+ if (G_UNLIKELY (!queue->first || queue->cursor == GRAY_LAST_CURSOR_POSITION (queue->first))) {
+ sgen_gray_object_enqueue (queue, obj, desc);
+ } else {
+ GrayQueueEntry entry = { obj, desc };
+
+ HEAVY_STAT (stat_gray_queue_enqueue_fast_path ++);
+
+ *++queue->cursor = entry;
+#ifdef SGEN_HEAVY_BINARY_PROTOCOL
+ binary_protocol_gray_enqueue (queue, queue->cursor, obj);
+#endif
+ }
+#endif
+}
+
+static inline MONO_ALWAYS_INLINE void
+GRAY_OBJECT_DEQUEUE (SgenGrayQueue *queue, char** obj, mword *desc)
+{
+ GrayQueueEntry entry;
+#if SGEN_MAX_DEBUG_LEVEL >= 9
+ entry = sgen_gray_object_enqueue (queue);
+ *obj = entry.obj;
+ *desc = entry.desc;
+#else
+ if (!queue->first) {
+ HEAVY_STAT (stat_gray_queue_dequeue_fast_path ++);
+
+ *obj = NULL;
+#ifdef SGEN_HEAVY_BINARY_PROTOCOL
+ binary_protocol_gray_dequeue (queue, queue->cursor, *obj);
+#endif
+ } else if (G_UNLIKELY (queue->cursor == GRAY_FIRST_CURSOR_POSITION (queue->first))) {
+ entry = sgen_gray_object_dequeue (queue);
+ *obj = entry.obj;
+ *desc = entry.desc;
+ } else {
+ HEAVY_STAT (stat_gray_queue_dequeue_fast_path ++);
+
+ entry = *queue->cursor--;
+ *obj = entry.obj;
+ *desc = entry.desc;
+#ifdef SGEN_HEAVY_BINARY_PROTOCOL
+ binary_protocol_gray_dequeue (queue, queue->cursor + 1, *obj);
+#endif
+ }
+#endif
+}
+
#endif
#define CONCURRENT_NAME(x) x
#endif
+/*
+ * FIXME: We use the same scanning function in the concurrent collector whether we scan
+ * during the starting/finishing collection pause (with the world stopped) or from the
+ * concurrent worker thread.
+ *
+ * As long as the world is stopped, we should just follow pointers into the nursery and
+ * evict if possible. In that case we also don't need the ALWAYS_ADD_TO_GLOBAL_REMSET case,
+ * which only seems to make sense for when the world is stopped, in which case we only need
+ * it because we don't follow into the nursery.
+ */
+
#undef HANDLE_PTR
#define HANDLE_PTR(ptr,obj) do { \
void *__old = *(ptr); \
- void *__copy; \
SGEN_OBJECT_LAYOUT_STATISTICS_MARK_BITMAP ((obj), (ptr)); \
if (__old && FOLLOW_OBJECT (__old)) { \
+ void *__copy; \
PREFETCH_DYNAMIC_HEAP (__old); \
CONCURRENT_NAME (major_copy_or_mark_object) ((ptr), __old, queue); \
__copy = *(ptr); \
} while (0)
static void
-CONCURRENT_NAME (major_scan_object) (char *start, SgenGrayQueue *queue)
+CONCURRENT_NAME (major_scan_object) (char *start, mword desc, SgenGrayQueue *queue)
{
SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
+#ifdef HEAVY_STATISTICS
+ sgen_descriptor_count_scanned_object (desc);
+#endif
+
#define SCAN_OBJECT_PROTOCOL
#include "sgen-scan-object.h"
+++ /dev/null
-#include "config.h"
-
-#ifdef HAVE_SGEN_GC
-
-#ifndef DISABLE_SGEN_MAJOR_MARKSWEEP_FIXED_PAR
-
-#define SGEN_PARALLEL_MARK
-#define FIXED_HEAP
-
-#include "sgen-marksweep.c"
-
-#else
-
-#include "metadata/sgen-gc.h"
-
-void
-sgen_marksweep_fixed_par_init (SgenMajorCollector *collector)
-{
- fprintf (stderr, "Error: Mono was configured using --enable-minimal=sgen_marksweep_fixed_par.\n");
- exit (1);
-}
-
-#endif
-
-#endif
+++ /dev/null
-#include "config.h"
-
-#ifdef HAVE_SGEN_GC
-
-#ifndef DISABLE_SGEN_MAJOR_MARKSWEEP_FIXED
-
-#define FIXED_HEAP
-
-#include "sgen-marksweep.c"
-
-#else
-
-#include "metadata/sgen-gc.h"
-
-void
-sgen_marksweep_fixed_init (SgenMajorCollector *collector)
-{
- fprintf (stderr, "Error: Mono was configured using --enable-minimal=sgen_marksweep_fixed.\n");
- exit (1);
-}
-
-#endif
-
-#endif
+++ /dev/null
-#include "config.h"
-
-#ifdef HAVE_SGEN_GC
-
-#ifndef DISABLE_SGEN_MAJOR_MARKSWEEP_PAR
-
-#define SGEN_PARALLEL_MARK
-
-#include "sgen-marksweep.c"
-
-#else
-
-#include "metadata/sgen-gc.h"
-
-void
-sgen_marksweep_par_init (SgenMajorCollector *collector)
-{
- fprintf (stderr, "Error: Mono was configured using --enable-minimal=sgen_marksweep_par.\n");
- exit (1);
-}
-
-#endif /* DISABLE_SGEN_MAJOR_MARKSWEEP_PAR */
-
-#endif
#include "metadata/sgen-memory-governor.h"
#include "metadata/sgen-layout-stats.h"
#include "metadata/gc-internal.h"
+#include "metadata/sgen-pointer-queue.h"
-#if !defined(SGEN_PARALLEL_MARK) && !defined(FIXED_HEAP)
#define SGEN_HAVE_CONCURRENT_MARK
-#endif
#define MS_BLOCK_SIZE (16*1024)
#define MS_BLOCK_SIZE_SHIFT 14
#define MAJOR_SECTION_SIZE MS_BLOCK_SIZE
#define CARDS_PER_BLOCK (MS_BLOCK_SIZE / CARD_SIZE_IN_BYTES)
-#ifdef FIXED_HEAP
-#define MS_DEFAULT_HEAP_NUM_BLOCKS (32 * 1024) /* 512 MB */
-#endif
-
/*
* Don't allocate single blocks, but alloc a contingent of this many
* blocks in one swoop. This must be a power of two.
* of a block is the MSBlockHeader, then opional padding, then come
* the objects, so this must be >= sizeof (MSBlockHeader).
*/
-#ifdef FIXED_HEAP
-#define MS_BLOCK_SKIP 0
-#else
#define MS_BLOCK_SKIP 16
-#endif
#define MS_BLOCK_FREE (MS_BLOCK_SIZE - MS_BLOCK_SKIP)
unsigned int has_pinned : 1; /* means cannot evacuate */
unsigned int is_to_space : 1;
unsigned int swept : 1;
-#ifdef FIXED_HEAP
- unsigned int used : 1;
- unsigned int zeroed : 1;
-#endif
- MSBlockInfo *next;
char *block;
void **free_list;
MSBlockInfo *next_free;
mword mark_words [MS_NUM_MARK_WORDS];
};
-#ifdef FIXED_HEAP
-static mword ms_heap_num_blocks = MS_DEFAULT_HEAP_NUM_BLOCKS;
-
-static char *ms_heap_start;
-static char *ms_heap_end;
-
-#define MS_PTR_IN_SMALL_MAJOR_HEAP(p) ((char*)(p) >= ms_heap_start && (char*)(p) < ms_heap_end)
-
-/* array of all all block infos in the system */
-static MSBlockInfo *block_infos;
-#endif
+#define MS_BLOCK_FOR_BLOCK_INFO(b) ((b)->block)
-#define MS_BLOCK_OBJ(b,i) ((b)->block + MS_BLOCK_SKIP + (b)->obj_size * (i))
-#define MS_BLOCK_OBJ_FOR_SIZE(b,i,obj_size) ((b)->block + MS_BLOCK_SKIP + (obj_size) * (i))
+#define MS_BLOCK_OBJ(b,i) (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP + (b)->obj_size * (i))
+#define MS_BLOCK_OBJ_FOR_SIZE(b,i,obj_size) (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP + (obj_size) * (i))
#define MS_BLOCK_DATA_FOR_OBJ(o) ((char*)((mword)(o) & ~(mword)(MS_BLOCK_SIZE - 1)))
-#ifdef FIXED_HEAP
-#define MS_BLOCK_FOR_OBJ(o) (&block_infos [(mword)((char*)(o) - ms_heap_start) >> MS_BLOCK_SIZE_SHIFT])
-#else
typedef struct {
MSBlockInfo *info;
} MSBlockHeader;
#define MS_BLOCK_FOR_OBJ(o) (((MSBlockHeader*)MS_BLOCK_DATA_FOR_OBJ ((o)))->info)
-#endif
/* object index will always be small */
-#define MS_BLOCK_OBJ_INDEX(o,b) ((int)(((char*)(o) - ((b)->block + MS_BLOCK_SKIP)) / (b)->obj_size))
+#define MS_BLOCK_OBJ_INDEX(o,b) ((int)(((char*)(o) - (MS_BLOCK_FOR_BLOCK_INFO(b) + MS_BLOCK_SKIP)) / (b)->obj_size))
//casting to int is fine since blocks are 32k
#define MS_CALC_MARK_BIT(w,b,o) do { \
} \
} while (1)
-#define MS_OBJ_ALLOCED(o,b) (*(void**)(o) && (*(char**)(o) < (b)->block || *(char**)(o) >= (b)->block + MS_BLOCK_SIZE))
+#define MS_OBJ_ALLOCED(o,b) (*(void**)(o) && (*(char**)(o) < MS_BLOCK_FOR_BLOCK_INFO (b) || *(char**)(o) >= MS_BLOCK_FOR_BLOCK_INFO (b) + MS_BLOCK_SIZE))
#define MS_BLOCK_OBJ_SIZE_FACTOR (sqrt (2.0))
#define MS_BLOCK_TYPE_MAX 4
-#ifdef SGEN_PARALLEL_MARK
-static LOCK_DECLARE (ms_block_list_mutex);
-#define LOCK_MS_BLOCK_LIST mono_mutex_lock (&ms_block_list_mutex)
-#define UNLOCK_MS_BLOCK_LIST mono_mutex_unlock (&ms_block_list_mutex)
-#endif
-
static gboolean *evacuate_block_obj_sizes;
static float evacuation_threshold = 0.666f;
#ifdef SGEN_HAVE_CONCURRENT_MARK
static gboolean concurrent_mark;
#endif
+#define BLOCK_IS_TAGGED_HAS_REFERENCES(bl) SGEN_POINTER_IS_TAGGED_1 ((bl))
+#define BLOCK_TAG_HAS_REFERENCES(bl) SGEN_POINTER_TAG_1 ((bl))
+#define BLOCK_UNTAG_HAS_REFERENCES(bl) SGEN_POINTER_UNTAG_1 ((bl))
+
+#define BLOCK_TAG(bl) ((bl)->has_references ? BLOCK_TAG_HAS_REFERENCES ((bl)) : (bl))
+
/* all allocated blocks in the system */
-static MSBlockInfo *all_blocks;
+static SgenPointerQueue allocated_blocks;
-#ifdef FIXED_HEAP
-/* non-allocated block free-list */
-static MSBlockInfo *empty_blocks = NULL;
-#else
/* non-allocated block free-list */
static void *empty_blocks = NULL;
static size_t num_empty_blocks = 0;
-#endif
-#define FOREACH_BLOCK(bl) for ((bl) = all_blocks; (bl); (bl) = (bl)->next) {
-#define END_FOREACH_BLOCK }
+#define FOREACH_BLOCK(bl) { size_t __index; for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { (bl) = BLOCK_UNTAG_HAS_REFERENCES (allocated_blocks.data [__index]);
+#define FOREACH_BLOCK_HAS_REFERENCES(bl,hr) { size_t __index; for (__index = 0; __index < allocated_blocks.next_slot; ++__index) { (bl) = allocated_blocks.data [__index]; (hr) = BLOCK_IS_TAGGED_HAS_REFERENCES ((bl)); (bl) = BLOCK_UNTAG_HAS_REFERENCES ((bl));
+#define END_FOREACH_BLOCK } }
+#define DELETE_BLOCK_IN_FOREACH() (allocated_blocks.data [__index] = NULL)
static size_t num_major_sections = 0;
/* one free block list for each block object size */
static MSBlockInfo **free_block_lists [MS_BLOCK_TYPE_MAX];
-#ifdef SGEN_PARALLEL_MARK
-#ifdef HAVE_KW_THREAD
-static __thread MSBlockInfo ***workers_free_block_lists;
-#else
-static MonoNativeTlsKey workers_free_block_lists_key;
-#endif
-#endif
-
static long long stat_major_blocks_alloced = 0;
static long long stat_major_blocks_freed = 0;
static long long stat_major_blocks_lazy_swept = 0;
#define FREE_BLOCKS_FROM(lists,p,r) (lists [((p) ? MS_BLOCK_FLAG_PINNED : 0) | ((r) ? MS_BLOCK_FLAG_REFS : 0)])
#define FREE_BLOCKS(p,r) (FREE_BLOCKS_FROM (free_block_lists, (p), (r)))
-#ifdef SGEN_PARALLEL_MARK
-#ifdef HAVE_KW_THREAD
-#define FREE_BLOCKS_LOCAL(p,r) (FREE_BLOCKS_FROM (workers_free_block_lists, (p), (r)))
-#else
-#define FREE_BLOCKS_LOCAL(p,r) (FREE_BLOCKS_FROM (((MSBlockInfo***)(mono_native_tls_get_value (workers_free_block_lists_key))), (p), (r)))
-#endif
-#else
-//#define FREE_BLOCKS_LOCAL(p,r) (FREE_BLOCKS_FROM (free_block_lists, (p), (r)))
-#endif
#define MS_BLOCK_OBJ_SIZE_INDEX(s) \
(((s)+7)>>3 < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES ? \
fast_block_obj_size_indexes [((s)+7)>>3] : \
ms_find_block_obj_size_index ((s)))
-#ifdef FIXED_HEAP
-static void*
-major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
-{
- char *nursery_start;
- mword major_heap_size = ms_heap_num_blocks * MS_BLOCK_SIZE;
- mword alloc_size = nursery_size + major_heap_size;
- mword i;
-
- g_assert (ms_heap_num_blocks > 0);
- g_assert (nursery_size % MS_BLOCK_SIZE == 0);
- if (nursery_align)
- g_assert (nursery_align % MS_BLOCK_SIZE == 0);
-
- nursery_start = sgen_alloc_os_memory_aligned (alloc_size, nursery_align ? nursery_align : MS_BLOCK_SIZE, SGEN_ALLOC_HEAP | SGEN_ALLOC_ACTIVATE, "heap");
- ms_heap_start = nursery_start + nursery_size;
- ms_heap_end = ms_heap_start + major_heap_size;
-
- block_infos = sgen_alloc_internal_dynamic (sizeof (MSBlockInfo) * ms_heap_num_blocks, INTERNAL_MEM_MS_BLOCK_INFO, TRUE);
-
- for (i = 0; i < ms_heap_num_blocks; ++i) {
- block_infos [i].block = ms_heap_start + i * MS_BLOCK_SIZE;
- if (i < ms_heap_num_blocks - 1)
- block_infos [i].next_free = &block_infos [i + 1];
- else
- block_infos [i].next_free = NULL;
- block_infos [i].zeroed = TRUE;
- }
-
- empty_blocks = &block_infos [0];
-
- return nursery_start;
-}
-#else
static void*
major_alloc_heap (mword nursery_size, mword nursery_align, int the_nursery_bits)
{
return start;
}
-#endif
static void
update_heap_boundaries_for_block (MSBlockInfo *block)
{
- sgen_update_heap_boundaries ((mword)block->block, (mword)block->block + MS_BLOCK_SIZE);
+ sgen_update_heap_boundaries ((mword)MS_BLOCK_FOR_BLOCK_INFO (block), (mword)MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE);
}
-#ifdef FIXED_HEAP
-static MSBlockInfo*
-ms_get_empty_block (void)
-{
- MSBlockInfo *block;
-
- g_assert (empty_blocks);
-
- do {
- block = empty_blocks;
- } while (SGEN_CAS_PTR ((gpointer*)&empty_blocks, block->next_free, block) != block);
-
- block->used = TRUE;
-
- if (!block->zeroed)
- memset (block->block, 0, MS_BLOCK_SIZE);
-
- return block;
-}
-
-static void
-ms_free_block (MSBlockInfo *block)
-{
- block->next_free = empty_blocks;
- empty_blocks = block;
- block->used = FALSE;
- block->zeroed = FALSE;
- sgen_memgov_release_space (MS_BLOCK_SIZE, SPACE_MAJOR);
-}
-#else
static void*
ms_get_empty_block (void)
{
SGEN_ATOMIC_ADD_P (num_empty_blocks, 1);
}
-#endif
//#define MARKSWEEP_CONSISTENCY_CHECK
if (block->swept)
g_assert (block->free_list);
-#ifdef FIXED_HEAP
- /* the block must not be in the empty_blocks list */
- for (b = empty_blocks; b; b = b->next_free)
- g_assert (b != block);
-#endif
- /* the block must be in the all_blocks list */
- for (b = all_blocks; b; b = b->next) {
- if (b == block)
- break;
- }
- g_assert (b == block);
+ /* the block must be in the allocated_blocks array */
+ g_assert (sgen_pointer_queue_find (&allocated_blocks, BLOCK_TAG (block)) != (size_t)-1);
}
}
static void
check_empty_blocks (void)
{
-#ifndef FIXED_HEAP
void *p;
size_t i = 0;
for (p = empty_blocks; p; p = *(void**)p)
++i;
g_assert (i == num_empty_blocks);
-#endif
}
static void
int num_free = 0;
void **free;
-#ifndef FIXED_HEAP
/* check block header */
g_assert (((MSBlockHeader*)block->block)->info == block);
-#endif
/* count number of free slots */
for (i = 0; i < count; ++i) {
int size = block_obj_sizes [size_index];
int count = MS_BLOCK_FREE / size;
MSBlockInfo *info;
-#ifdef SGEN_PARALLEL_MARK
- MSBlockInfo *next;
-#endif
-#ifndef FIXED_HEAP
MSBlockHeader *header;
-#endif
MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
char *obj_start;
int i;
if (!sgen_memgov_try_alloc_space (MS_BLOCK_SIZE, SPACE_MAJOR))
return FALSE;
-#ifdef FIXED_HEAP
- info = ms_get_empty_block ();
-#else
info = sgen_alloc_internal (INTERNAL_MEM_MS_BLOCK_INFO);
-#endif
SGEN_ASSERT (9, count >= 2, "block with %d objects, it must hold at least 2", count);
*/
info->is_to_space = (sgen_get_current_collection_generation () == GENERATION_OLD);
info->swept = 1;
-#ifndef FIXED_HEAP
info->block = ms_get_empty_block ();
header = (MSBlockHeader*) info->block;
header->info = info;
-#endif
#ifdef SGEN_HAVE_CONCURRENT_MARK
info->cardtable_mod_union = NULL;
#endif
update_heap_boundaries_for_block (info);
/* build free list */
- obj_start = info->block + MS_BLOCK_SKIP;
+ obj_start = MS_BLOCK_FOR_BLOCK_INFO (info) + MS_BLOCK_SKIP;
info->free_list = (void**)obj_start;
/* we're skipping the last one - it must be nulled */
for (i = 0; i < count - 1; ++i) {
/* the last one */
*(void**)obj_start = NULL;
-#ifdef SGEN_PARALLEL_MARK
- do {
- next = info->next_free = free_blocks [size_index];
- } while (SGEN_CAS_PTR ((void**)&free_blocks [size_index], info, next) != next);
-
- do {
- next = info->next = all_blocks;
- } while (SGEN_CAS_PTR ((void**)&all_blocks, info, next) != next);
-#else
info->next_free = free_blocks [size_index];
free_blocks [size_index] = info;
- info->next = all_blocks;
- all_blocks = info;
-#endif
+ sgen_pointer_queue_add (&allocated_blocks, BLOCK_TAG (info));
++num_major_sections;
return TRUE;
MSBlockInfo *block;
FOREACH_BLOCK (block) {
- if (ptr >= block->block && ptr <= block->block + MS_BLOCK_SIZE)
+ if (ptr >= MS_BLOCK_FOR_BLOCK_INFO (block) && ptr <= MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE)
return block->pinned;
} END_FOREACH_BLOCK;
return FALSE;
return obj;
}
-#ifdef SGEN_PARALLEL_MARK
-static gboolean
-try_remove_block_from_free_list (MSBlockInfo *block, MSBlockInfo **free_blocks, int size_index)
-{
- /*
- * No more free slots in the block, so try to free the block.
- * Don't try again if we don't succeed - another thread will
- * already have done it.
- */
- MSBlockInfo *next_block = block->next_free;
- if (SGEN_CAS_PTR ((void**)&free_blocks [size_index], next_block, block) == block) {
- /*
- void *old = SGEN_CAS_PTR ((void**)&block->next_free, NULL, next_block);
- g_assert (old == next_block);
- */
- block->next_free = NULL;
- return TRUE;
- }
- return FALSE;
-}
-
-static void*
-alloc_obj_par (MonoVTable *vtable, int size, gboolean pinned, gboolean has_references)
-{
- int size_index = MS_BLOCK_OBJ_SIZE_INDEX (size);
- MSBlockInfo **free_blocks_local = FREE_BLOCKS_LOCAL (pinned, has_references);
- MSBlockInfo *block;
- void *obj;
-
-#ifdef SGEN_HAVE_CONCURRENT_MARK
- if (concurrent_mark)
- g_assert_not_reached ();
-#endif
-
- SGEN_ASSERT (9, current_collection_generation == GENERATION_OLD, "old gen parallel allocator called from a %d collection", current_collection_generation);
-
- if (free_blocks_local [size_index]) {
- get_slot:
- obj = unlink_slot_from_free_list_uncontested (free_blocks_local, size_index);
- } else {
- MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
-
- get_block:
- block = free_blocks [size_index];
- if (block) {
- if (!try_remove_block_from_free_list (block, free_blocks, size_index))
- goto get_block;
-
- g_assert (block->next_free == NULL);
- g_assert (block->free_list);
- block->next_free = free_blocks_local [size_index];
- free_blocks_local [size_index] = block;
-
- goto get_slot;
- } else {
- gboolean success;
-
- LOCK_MS_BLOCK_LIST;
- success = ms_alloc_block (size_index, pinned, has_references);
- UNLOCK_MS_BLOCK_LIST;
-
- if (G_UNLIKELY (!success))
- return NULL;
-
- goto get_block;
- }
- }
-
- *(MonoVTable**)obj = vtable;
-
- return obj;
-}
-
-static void*
-major_par_alloc_object (MonoVTable *vtable, size_t size, gboolean has_references)
-{
- return alloc_obj_par (vtable, size, FALSE, has_references);
-}
-#endif
-
static void*
alloc_obj (MonoVTable *vtable, size_t size, gboolean pinned, gboolean has_references)
{
MSBlockInfo **free_blocks = FREE_BLOCKS (pinned, has_references);
void *obj;
-#ifdef SGEN_PARALLEL_MARK
- SGEN_ASSERT (9, current_collection_generation == GENERATION_OLD, "old gen parallel allocator called from a %d collection", current_collection_generation);
-
-#endif
-
if (!free_blocks [size_index]) {
if (G_UNLIKELY (!ms_alloc_block (size_index, pinned, has_references)))
return NULL;
{
MSBlockInfo *block;
int word, bit;
-#ifndef FIXED_HEAP
mword objsize;
-#endif
if (sgen_ptr_in_nursery (obj))
return FALSE;
-#ifdef FIXED_HEAP
- /* LOS */
- if (!MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
- return FALSE;
-#else
objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
/* LOS */
if (objsize > SGEN_MAX_SMALL_OBJ_SIZE)
return FALSE;
-#endif
/* now we know it's in a major block */
block = MS_BLOCK_FOR_OBJ (obj);
MSBlockInfo *block;
FOREACH_BLOCK (block) {
- if (ptr >= block->block && ptr <= block->block + MS_BLOCK_SIZE) {
+ if (ptr >= MS_BLOCK_FOR_BLOCK_INFO (block) && ptr <= MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) {
int count = MS_BLOCK_FREE / block->obj_size;
int i;
int idx;
char *obj;
- if ((block->block > object) || ((block->block + MS_BLOCK_SIZE) <= object))
+ if ((MS_BLOCK_FOR_BLOCK_INFO (block) > object) || ((MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) <= object))
continue;
idx = MS_BLOCK_OBJ_INDEX (object, block);
int w, b;
gboolean marked;
- if ((block->block > ptr) || ((block->block + MS_BLOCK_SIZE) <= ptr))
+ if ((MS_BLOCK_FOR_BLOCK_INFO (block) > ptr) || ((MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE) <= ptr))
continue;
SGEN_LOG (0, "major-ptr (block %p sz %d pin %d ref %d)\n",
- block->block, block->obj_size, block->pinned, block->has_references);
+ MS_BLOCK_FOR_BLOCK_INFO (block), block->obj_size, block->pinned, block->has_references);
idx = MS_BLOCK_OBJ_INDEX (ptr, block);
obj = (char*)MS_BLOCK_OBJ (block, idx);
start = i;
} else {
if (start >= 0) {
- sgen_dump_occupied (MS_BLOCK_OBJ (block, start), MS_BLOCK_OBJ (block, i), block->block);
+ sgen_dump_occupied (MS_BLOCK_OBJ (block, start), MS_BLOCK_OBJ (block, i), MS_BLOCK_FOR_BLOCK_INFO (block));
start = -1;
}
}
#define LOAD_VTABLE SGEN_LOAD_VTABLE
-#define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,block,queue) do { \
+#define MS_MARK_OBJECT_AND_ENQUEUE_CHECKED(obj,desc,block,queue) do { \
int __word, __bit; \
MS_CALC_MARK_BIT (__word, __bit, (obj)); \
if (!MS_MARK_BIT ((block), __word, __bit) && MS_OBJ_ALLOCED ((obj), (block))) { \
MS_SET_MARK_BIT ((block), __word, __bit); \
if ((block)->has_references) \
- GRAY_OBJECT_ENQUEUE ((queue), (obj)); \
+ GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
INC_NUM_MAJOR_OBJECTS_MARKED (); \
} \
} while (0)
-#define MS_MARK_OBJECT_AND_ENQUEUE(obj,block,queue) do { \
+#define MS_MARK_OBJECT_AND_ENQUEUE(obj,desc,block,queue) do { \
int __word, __bit; \
MS_CALC_MARK_BIT (__word, __bit, (obj)); \
SGEN_ASSERT (9, MS_OBJ_ALLOCED ((obj), (block)), "object %p not allocated", obj); \
if (!MS_MARK_BIT ((block), __word, __bit)) { \
MS_SET_MARK_BIT ((block), __word, __bit); \
if ((block)->has_references) \
- GRAY_OBJECT_ENQUEUE ((queue), (obj)); \
+ GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
INC_NUM_MAJOR_OBJECTS_MARKED (); \
} \
} while (0)
-#define MS_PAR_MARK_OBJECT_AND_ENQUEUE(obj,block,queue) do { \
+#define MS_PAR_MARK_OBJECT_AND_ENQUEUE(obj,desc,block,queue) do { \
int __word, __bit; \
gboolean __was_marked; \
SGEN_ASSERT (9, MS_OBJ_ALLOCED ((obj), (block)), "object %p not allocated", obj); \
MS_PAR_SET_MARK_BIT (__was_marked, (block), __word, __bit); \
if (!__was_marked) { \
if ((block)->has_references) \
- GRAY_OBJECT_ENQUEUE ((queue), (obj)); \
+ GRAY_OBJECT_ENQUEUE ((queue), (obj), (desc)); \
binary_protocol_mark ((obj), (gpointer)LOAD_VTABLE ((obj)), sgen_safe_object_get_size ((MonoObject*)(obj))); \
INC_NUM_MAJOR_OBJECTS_MARKED (); \
} \
block = MS_BLOCK_FOR_OBJ (obj);
block->has_pinned = TRUE;
- MS_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
+ MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
}
#include "sgen-major-copy-object.h"
-#ifdef SGEN_PARALLEL_MARK
-static void
-major_copy_or_mark_object (void **ptr, void *obj, SgenGrayQueue *queue)
-{
- mword objsize;
- MSBlockInfo *block;
- MonoVTable *vt;
-
- HEAVY_STAT (++stat_copy_object_called_major);
-
- SGEN_ASSERT (9, obj, "null object from pointer %p", ptr);
- SGEN_ASSERT (9, current_collection_generation == GENERATION_OLD, "old gen parallel allocator called from a %d collection", current_collection_generation);
-
- if (sgen_ptr_in_nursery (obj)) {
- int word, bit;
- gboolean has_references;
- void *destination;
- mword vtable_word = *(mword*)obj;
- vt = (MonoVTable*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-
- if (vtable_word & SGEN_FORWARDED_BIT) {
- *ptr = (void*)vt;
- return;
- }
-
- if (vtable_word & SGEN_PINNED_BIT)
- return;
-
- /* An object in the nursery To Space has already been copied and grayed. Nothing to do. */
- if (sgen_nursery_is_to_space (obj))
- return;
-
- HEAVY_STAT (++stat_objects_copied_major);
-
- do_copy_object:
- objsize = SGEN_ALIGN_UP (sgen_par_object_get_size (vt, (MonoObject*)obj));
- has_references = SGEN_VTABLE_HAS_REFERENCES (vt);
-
- destination = sgen_minor_collector.par_alloc_for_promotion (vt, obj, objsize, has_references);
- if (G_UNLIKELY (!destination)) {
- if (!sgen_ptr_in_nursery (obj)) {
- int size_index;
- block = MS_BLOCK_FOR_OBJ (obj);
- size_index = block->obj_size_index;
- evacuate_block_obj_sizes [size_index] = FALSE;
- }
-
- sgen_parallel_pin_or_update (ptr, obj, vt, queue);
- sgen_set_pinned_from_failed_allocation (objsize);
- return;
- }
-
- if (SGEN_CAS_PTR (obj, (void*)((mword)destination | SGEN_FORWARDED_BIT), vt) == vt) {
- gboolean was_marked;
-
- par_copy_object_no_checks (destination, vt, obj, objsize, has_references ? queue : NULL);
- obj = destination;
- *ptr = obj;
-
- /*
- * FIXME: If we make major_alloc_object() give
- * us the block info, too, we won't have to
- * re-fetch it here.
- *
- * FIXME (2): We should rework this to avoid all those nursery checks.
- */
- /*
- * For the split nursery allocator the object
- * might still be in the nursery despite
- * having being promoted, in which case we
- * can't mark it.
- */
- if (!sgen_ptr_in_nursery (obj)) {
- block = MS_BLOCK_FOR_OBJ (obj);
- MS_CALC_MARK_BIT (word, bit, obj);
- SGEN_ASSERT (9, !MS_MARK_BIT (block, word, bit), "object %p already marked", obj);
- MS_PAR_SET_MARK_BIT (was_marked, block, word, bit);
- binary_protocol_mark (obj, vt, sgen_safe_object_get_size ((MonoObject*)obj));
- }
- } else {
- /*
- * FIXME: We have allocated destination, but
- * we cannot use it. Give it back to the
- * allocator.
- */
- *(void**)destination = NULL;
-
- vtable_word = *(mword*)obj;
- g_assert (vtable_word & SGEN_FORWARDED_BIT);
-
- obj = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-
- *ptr = obj;
-
- HEAVY_STAT (++stat_slots_allocated_in_vain);
- }
- } else {
-#ifdef FIXED_HEAP
- if (MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
-#else
- mword vtable_word = *(mword*)obj;
- vt = (MonoVTable*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-
- /* see comment in the non-parallel version below */
- if (vtable_word & SGEN_FORWARDED_BIT) {
- *ptr = (void*)vt;
- return;
- }
- objsize = SGEN_ALIGN_UP (sgen_par_object_get_size (vt, (MonoObject*)obj));
-
- if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE)
-#endif
- {
- int size_index;
-
- block = MS_BLOCK_FOR_OBJ (obj);
- size_index = block->obj_size_index;
-
- if (!block->has_pinned && evacuate_block_obj_sizes [size_index]) {
- if (block->is_to_space)
- return;
-
-#ifdef FIXED_HEAP
- {
- mword vtable_word = *(mword*)obj;
- vt = (MonoVTable*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-
- if (vtable_word & SGEN_FORWARDED_BIT) {
- *ptr = (void*)vt;
- return;
- }
- }
-#endif
-
- HEAVY_STAT (++stat_major_objects_evacuated);
- goto do_copy_object;
- }
-
- MS_PAR_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
- } else {
- LOSObject *bigobj = sgen_los_header_for_object (obj);
- mword size_word = bigobj->size;
-#ifdef FIXED_HEAP
- mword vtable_word = *(mword*)obj;
- vt = (MonoVTable*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-#endif
- if (size_word & 1)
- return;
- binary_protocol_pin (obj, vt, sgen_safe_object_get_size ((MonoObject*)obj));
- if (SGEN_CAS_PTR ((void*)&bigobj->size, (void*)(size_word | 1), (void*)size_word) == (void*)size_word) {
- if (SGEN_VTABLE_HAS_REFERENCES (vt))
- GRAY_OBJECT_ENQUEUE (queue, obj);
- } else {
- g_assert (sgen_los_object_is_pinned (obj));
- }
- }
- }
-}
-#else
#ifdef SGEN_HAVE_CONCURRENT_MARK
static void
major_copy_or_mark_object_concurrent (void **ptr, void *obj, SgenGrayQueue *queue)
g_assert (!SGEN_OBJECT_IS_FORWARDED (obj));
if (!sgen_ptr_in_nursery (obj)) {
-#ifdef FIXED_HEAP
- if (MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
-#else
mword objsize;
objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
- if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE)
-#endif
- {
+ if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE) {
MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
- MS_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
+ MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
} else {
if (sgen_los_object_is_pinned (obj))
return;
sgen_los_pin_object (obj);
if (SGEN_OBJECT_HAS_REFERENCES (obj))
- GRAY_OBJECT_ENQUEUE (queue, obj);
+ GRAY_OBJECT_ENQUEUE (queue, obj, sgen_obj_get_descriptor (obj));
INC_NUM_MAJOR_OBJECTS_MARKED ();
}
}
block = MS_BLOCK_FOR_OBJ (obj);
size_index = block->obj_size_index;
evacuate_block_obj_sizes [size_index] = FALSE;
- MS_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
+ MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
}
return;
}
}
} else {
char *forwarded;
-#ifdef FIXED_HEAP
- if (MS_PTR_IN_SMALL_MAJOR_HEAP (obj))
-#else
mword objsize;
/*
objsize = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)obj));
- if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE)
-#endif
- {
+ if (objsize <= SGEN_MAX_SMALL_OBJ_SIZE) {
int size_index;
gboolean evacuate;
size_index = block->obj_size_index;
evacuate = evacuate_block_obj_sizes [size_index];
-#ifdef FIXED_HEAP
- /*
- * We could also check for !block->has_pinned
- * here, but it would only make an uncommon case
- * faster, namely objects that are in blocks
- * whose slot sizes are evacuated but which have
- * pinned objects.
- */
- if (evacuate && (forwarded = SGEN_OBJECT_IS_FORWARDED (obj))) {
- *ptr = forwarded;
- return;
- }
-#endif
-
if (evacuate && !block->has_pinned) {
g_assert (!SGEN_OBJECT_IS_PINNED (obj));
if (block->is_to_space)
HEAVY_STAT (++stat_major_objects_evacuated);
goto do_copy_object;
} else {
- MS_MARK_OBJECT_AND_ENQUEUE (obj, block, queue);
+ MS_MARK_OBJECT_AND_ENQUEUE (obj, sgen_obj_get_descriptor (obj), block, queue);
}
} else {
if (sgen_los_object_is_pinned (obj))
sgen_los_pin_object (obj);
if (SGEN_OBJECT_HAS_REFERENCES (obj))
- GRAY_OBJECT_ENQUEUE (queue, obj);
+ GRAY_OBJECT_ENQUEUE (queue, obj, sgen_obj_get_descriptor (obj));
}
}
}
-#endif
static void
major_copy_or_mark_object_canonical (void **ptr, SgenGrayQueue *queue)
{
major_copy_or_mark_object_concurrent (ptr, *ptr, queue);
}
+#endif
static long long
major_get_and_reset_num_major_objects_marked (void)
return 0;
#endif
}
-#endif
#include "sgen-major-scan-object.h"
for (i = 0; i < block->pin_queue_num_entries; ++i) {
int index = MS_BLOCK_OBJ_INDEX (block->pin_queue_start [i], block);
+ char *obj;
SGEN_ASSERT (9, index >= 0 && index < MS_BLOCK_FREE / block->obj_size, "invalid object %p index %d max-index %d", block->pin_queue_start [i], index, MS_BLOCK_FREE / block->obj_size);
if (index == last_index)
continue;
- MS_MARK_OBJECT_AND_ENQUEUE_CHECKED (MS_BLOCK_OBJ (block, index), block, queue);
+ obj = MS_BLOCK_OBJ (block, index);
+ MS_MARK_OBJECT_AND_ENQUEUE_CHECKED (obj, sgen_obj_get_descriptor (obj), block, queue);
last_index = index;
}
}
ms_sweep (void)
{
int i;
- MSBlockInfo **iter;
+ MSBlockInfo *block;
/* statistics for evacuation */
int *slots_available = alloca (sizeof (int) * num_block_obj_sizes);
}
/* traverse all blocks, free and zero unmarked objects */
- iter = &all_blocks;
- while (*iter) {
- MSBlockInfo *block = *iter;
+ FOREACH_BLOCK (block) {
int count;
gboolean have_live = FALSE;
gboolean has_pinned;
slots_available [obj_size_index] += count;
}
- iter = &block->next;
-
/*
* If there are free slots in the block, add
* the block to the corresponding free list.
* Blocks without live objects are removed from the
* block list and freed.
*/
- *iter = block->next;
+ DELETE_BLOCK_IN_FOREACH ();
binary_protocol_empty (MS_BLOCK_OBJ (block, 0), (char*)MS_BLOCK_OBJ (block, count) - (char*)MS_BLOCK_OBJ (block, 0));
-#ifdef FIXED_HEAP
- ms_free_block (block);
-#else
ms_free_block (block->block);
-
sgen_free_internal (block, INTERNAL_MEM_MS_BLOCK_INFO);
-#endif
--num_major_sections;
}
- }
+ } END_FOREACH_BLOCK;
+ sgen_pointer_queue_remove_nulls (&allocated_blocks);
for (i = 0; i < num_block_obj_sizes; ++i) {
float usage = (float)slots_used [i] / (float)slots_available [i];
// Sweep all unswept blocks
if (lazy_sweep) {
- MSBlockInfo **iter;
+ MSBlockInfo *block;
MONO_GC_SWEEP_BEGIN (GENERATION_OLD, TRUE);
- iter = &all_blocks;
- while (*iter) {
- MSBlockInfo *block = *iter;
-
+ FOREACH_BLOCK (block) {
sweep_block (block, TRUE);
-
- iter = &block->next;
- }
+ } END_FOREACH_BLOCK;
MONO_GC_SWEEP_END (GENERATION_OLD, TRUE);
}
{
}
-#if !defined(FIXED_HEAP) && SIZEOF_VOID_P != 8
+#if SIZEOF_VOID_P != 8
static int
compare_pointers (const void *va, const void *vb) {
char *a = *(char**)va, *b = *(char**)vb;
static void
major_have_computer_minor_collection_allowance (void)
{
-#ifndef FIXED_HEAP
size_t section_reserve = sgen_get_minor_collection_allowance () / MS_BLOCK_SIZE;
g_assert (have_swept);
++stat_major_blocks_freed_individual;
#endif
}
-#endif
}
static void
MSBlockInfo *block;
FOREACH_BLOCK (block) {
- block->pin_queue_start = sgen_find_optimized_pin_queue_area (block->block + MS_BLOCK_SKIP, block->block + MS_BLOCK_SIZE,
+ block->pin_queue_start = sgen_find_optimized_pin_queue_area (MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SKIP, MS_BLOCK_FOR_BLOCK_INFO (block) + MS_BLOCK_SIZE,
&block->pin_queue_num_entries);
} END_FOREACH_BLOCK;
}
static gboolean
major_handle_gc_param (const char *opt)
{
-#ifdef FIXED_HEAP
- if (g_str_has_prefix (opt, "major-heap-size=")) {
- const char *arg = strchr (opt, '=') + 1;
- size_t size;
- if (!mono_gc_parse_environment_string_extract_number (arg, &size))
- return FALSE;
- ms_heap_num_blocks = (size + MS_BLOCK_SIZE - 1) / MS_BLOCK_SIZE;
- g_assert (ms_heap_num_blocks > 0);
- return TRUE;
- } else
-#endif
if (g_str_has_prefix (opt, "evacuation-threshold=")) {
const char *arg = strchr (opt, '=') + 1;
int percentage = atoi (arg);
{
fprintf (stderr,
""
-#ifdef FIXED_HEAP
- " major-heap-size=N (where N is an integer, possibly with a k, m or a g suffix)\n"
-#endif
" evacuation-threshold=P (where P is a percentage, an integer in 0-100)\n"
" (no-)lazy-sweep\n"
);
major_iterate_live_block_ranges (sgen_cardtable_block_callback callback)
{
MSBlockInfo *block;
+ gboolean has_references;
- FOREACH_BLOCK (block) {
- if (block->has_references)
- callback ((mword)block->block, MS_BLOCK_SIZE);
+ FOREACH_BLOCK_HAS_REFERENCES (block, has_references) {
+ if (has_references)
+ callback ((mword)MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE);
} END_FOREACH_BLOCK;
}
major_scan_card_table (gboolean mod_union, SgenGrayQueue *queue)
{
MSBlockInfo *block;
+ gboolean has_references;
ScanObjectFunc scan_func = sgen_get_current_object_ops ()->scan_object;
#ifdef SGEN_HAVE_CONCURRENT_MARK
g_assert (!mod_union);
#endif
- FOREACH_BLOCK (block) {
+ FOREACH_BLOCK_HAS_REFERENCES (block, has_references) {
int block_obj_size;
char *block_start;
- if (!block->has_references)
+ if (!has_references)
continue;
block_obj_size = block->obj_size;
- block_start = block->block;
+ block_start = MS_BLOCK_FOR_BLOCK_INFO (block);
if (block_obj_size >= CARD_SIZE_IN_BYTES) {
guint8 *cards;
}
HEAVY_STAT (++scanned_objects);
- scan_func (obj, queue);
+ scan_func (obj, sgen_obj_get_descriptor (obj), queue);
next_small:
obj += block_obj_size;
}
major_count_cards (long long *num_total_cards, long long *num_marked_cards)
{
MSBlockInfo *block;
+ gboolean has_references;
long long total_cards = 0;
long long marked_cards = 0;
- FOREACH_BLOCK (block) {
- guint8 *cards = sgen_card_table_get_card_scan_address ((mword) block->block);
+ FOREACH_BLOCK_HAS_REFERENCES (block, has_references) {
+ guint8 *cards = sgen_card_table_get_card_scan_address ((mword) MS_BLOCK_FOR_BLOCK_INFO (block));
int i;
- if (!block->has_references)
+ if (!has_references)
continue;
total_cards += CARDS_PER_BLOCK;
size_t num_cards;
block->cardtable_mod_union = sgen_card_table_update_mod_union (block->cardtable_mod_union,
- block->block, MS_BLOCK_SIZE, &num_cards);
+ MS_BLOCK_FOR_BLOCK_INFO (block), MS_BLOCK_SIZE, &num_cards);
SGEN_ASSERT (0, num_cards == CARDS_PER_BLOCK, "Number of cards calculation is wrong");
} END_FOREACH_BLOCK;
major_get_cardtable_mod_union_for_object (char *obj)
{
MSBlockInfo *block = MS_BLOCK_FOR_OBJ (obj);
- return &block->cardtable_mod_union [(obj - (char*)sgen_card_table_align_pointer (block->block)) >> CARD_BITS];
+ return &block->cardtable_mod_union [(obj - (char*)sgen_card_table_align_pointer (MS_BLOCK_FOR_BLOCK_INFO (block))) >> CARD_BITS];
}
#endif
lists [i] = sgen_alloc_internal_dynamic (sizeof (MSBlockInfo*) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
}
-#ifdef SGEN_PARALLEL_MARK
-static void*
-major_alloc_worker_data (void)
-{
- /* FIXME: free this when the workers come down */
- MSBlockInfo ***lists = malloc (sizeof (MSBlockInfo**) * MS_BLOCK_TYPE_MAX);
- alloc_free_block_lists (lists);
- return lists;
-}
-
-static void
-major_init_worker_thread (void *data)
-{
- MSBlockInfo ***lists = data;
- int i;
-
- g_assert (lists && lists != free_block_lists);
- for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i) {
- int j;
- for (j = 0; j < num_block_obj_sizes; ++j)
- g_assert (!lists [i][j]);
- }
-
-#ifdef HAVE_KW_THREAD
- workers_free_block_lists = data;
-#else
- mono_native_tls_set_value (workers_free_block_lists_key, data);
-#endif
-}
-
-static void
-major_reset_worker_data (void *data)
-{
- MSBlockInfo ***lists = data;
- int i;
- for (i = 0; i < MS_BLOCK_TYPE_MAX; ++i) {
- int j;
- for (j = 0; j < num_block_obj_sizes; ++j)
- lists [i][j] = NULL;
- }
-}
-#endif
-
#undef pthread_create
static void
static void
sgen_marksweep_init_internal (SgenMajorCollector *collector, gboolean is_concurrent)
#else // SGEN_HAVE_CONCURRENT_MARK
-#ifdef SGEN_PARALLEL_MARK
-#ifdef FIXED_HEAP
-void
-sgen_marksweep_fixed_par_init (SgenMajorCollector *collector)
-#else // FIXED_HEAP
-void
-sgen_marksweep_par_init (SgenMajorCollector *collector)
-#endif // FIXED_HEAP
-#else // SGEN_PARALLEL_MARK
-#ifdef FIXED_HEAP
-void
-sgen_marksweep_fixed_init (SgenMajorCollector *collector)
-#else // FIXED_HEAP
#error unknown configuration
-#endif // FIXED_HEAP
-#endif // SGEN_PARALLEL_MARK
#endif // SGEN_HAVE_CONCURRENT_MARK
{
int i;
-#ifndef FIXED_HEAP
sgen_register_fixed_internal_mem_type (INTERNAL_MEM_MS_BLOCK_INFO, sizeof (MSBlockInfo));
-#endif
num_block_obj_sizes = ms_calculate_block_obj_sizes (MS_BLOCK_OBJ_SIZE_FACTOR, NULL);
block_obj_sizes = sgen_alloc_internal_dynamic (sizeof (int) * num_block_obj_sizes, INTERNAL_MEM_MS_TABLES, TRUE);
for (i = 0; i < MS_NUM_FAST_BLOCK_OBJ_SIZE_INDEXES * 8; ++i)
g_assert (MS_BLOCK_OBJ_SIZE_INDEX (i) == ms_find_block_obj_size_index (i));
-#ifdef SGEN_PARALLEL_MARK
- LOCK_INIT (ms_block_list_mutex);
-#endif
-
mono_counters_register ("# major blocks allocated", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_alloced);
mono_counters_register ("# major blocks freed", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_freed);
mono_counters_register ("# major blocks lazy swept", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_lazy_swept);
mono_counters_register ("# major blocks allocated less ideally", MONO_COUNTER_GC | MONO_COUNTER_LONG, &stat_major_blocks_alloced_less_ideal);
#endif
-#ifdef SGEN_PARALLEL_MARK
-#ifndef HAVE_KW_THREAD
- mono_native_tls_alloc (&workers_free_block_lists_key, NULL);
-#endif
-#endif
-
collector->section_size = MAJOR_SECTION_SIZE;
-#ifdef SGEN_PARALLEL_MARK
- collector->is_parallel = TRUE;
- collector->alloc_worker_data = major_alloc_worker_data;
- collector->init_worker_thread = major_init_worker_thread;
- collector->reset_worker_data = major_reset_worker_data;
-#else
- collector->is_parallel = FALSE;
-#endif
+
#ifdef SGEN_HAVE_CONCURRENT_MARK
concurrent_mark = is_concurrent;
if (is_concurrent) {
collector->is_concurrent = TRUE;
collector->want_synchronous_collection = &want_evacuation;
- collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
} else
#endif
{
collector->is_concurrent = FALSE;
collector->want_synchronous_collection = NULL;
}
+ collector->get_and_reset_num_major_objects_marked = major_get_and_reset_num_major_objects_marked;
collector->supports_cardtable = TRUE;
collector->have_swept = &have_swept;
collector->alloc_degraded = major_alloc_degraded;
collector->alloc_object = major_alloc_object;
-#ifdef SGEN_PARALLEL_MARK
- collector->par_alloc_object = major_par_alloc_object;
-#endif
collector->free_pinned_object = free_pinned_object;
collector->iterate_objects = major_iterate_objects;
collector->free_non_pinned_object = major_free_non_pinned_object;
#define collector_pin_object(obj, queue) sgen_pin_object (obj, queue);
#define COLLECTOR_SERIAL_ALLOC_FOR_PROMOTION alloc_for_promotion
-#define COLLECTOR_PARALLEL_ALLOC_FOR_PROMOTION par_alloc_for_promotion
extern long long stat_nursery_copy_object_failed_to_space; /* from sgen-gc.c */
#endif
}
-static void
-PARALLEL_COPY_OBJECT (void **obj_slot, SgenGrayQueue *queue)
-{
- char *obj = *obj_slot;
- mword vtable_word, objsize;
- MonoVTable *vt;
- void *destination;
- gboolean has_references;
-
- SGEN_ASSERT (9, current_collection_generation == GENERATION_NURSERY, "calling minor-par-copy from a %d generation collection", current_collection_generation);
-
- HEAVY_STAT (++stat_copy_object_called_nursery);
-
- if (!sgen_ptr_in_nursery (obj)) {
- HEAVY_STAT (++stat_nursery_copy_object_failed_from_space);
- return;
- }
-
- vtable_word = *(mword*)obj;
- vt = (MonoVTable*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-
- /*
- * Before we can copy the object we must make sure that we are
- * allowed to, i.e. that the object not pinned, not already
- * forwarded and not in the nursery To Space.
- */
-
- if (vtable_word & SGEN_FORWARDED_BIT) {
- HEAVY_STAT (++stat_nursery_copy_object_failed_forwarded);
- *obj_slot = vt;
- return;
- }
- if (vtable_word & SGEN_PINNED_BIT) {
- HEAVY_STAT (++stat_nursery_copy_object_failed_pinned);
- return;
- }
-
- if (sgen_nursery_is_to_space (obj)) {
- HEAVY_STAT (++stat_nursery_copy_object_failed_to_space);
- return;
- }
-
- HEAVY_STAT (++stat_objects_copied_nursery);
-
- objsize = SGEN_ALIGN_UP (sgen_par_object_get_size (vt, (MonoObject*)obj));
- has_references = SGEN_VTABLE_HAS_REFERENCES (vt);
-
- destination = COLLECTOR_PARALLEL_ALLOC_FOR_PROMOTION (vt, obj, objsize, has_references);
-
- if (G_UNLIKELY (!destination)) {
- sgen_parallel_pin_or_update (obj_slot, obj, vt, queue);
- return;
- }
-
- *(MonoVTable**)destination = vt;
-
- if (SGEN_CAS_PTR ((void*)obj, (void*)((mword)destination | SGEN_FORWARDED_BIT), vt) == vt) {
- par_copy_object_no_checks (destination, vt, obj, objsize, has_references ? queue : NULL);
- obj = destination;
- *obj_slot = obj;
- } else {
- /* FIXME: unify with code in major_copy_or_mark_object() */
-
- /* FIXME: Give destination back to the allocator. */
- /*The major collector only needs the first word zeroed and nursery requires all bits to be. */
- if (!sgen_ptr_in_nursery (destination))
- *(void**)destination = NULL;
- else
- memset (destination, 0, objsize);
-
- vtable_word = *(mword*)obj;
- g_assert (vtable_word & SGEN_FORWARDED_BIT);
-
- obj = (void*)(vtable_word & ~SGEN_VTABLE_BITS_MASK);
-
- *obj_slot = obj;
-
- HEAVY_STAT (++stat_slots_allocated_in_vain);
- }
-}
-
#define FILL_MINOR_COLLECTOR_COPY_OBJECT(collector) do { \
(collector)->serial_ops.copy_or_mark_object = SERIAL_COPY_OBJECT; \
- (collector)->parallel_ops.copy_or_mark_object = PARALLEL_COPY_OBJECT; \
} while (0)
#if defined(SGEN_SIMPLE_NURSERY)
#define SERIAL_SCAN_OBJECT simple_nursery_serial_scan_object
#define SERIAL_SCAN_VTYPE simple_nursery_serial_scan_vtype
-#define PARALLEL_SCAN_OBJECT simple_nursery_parallel_scan_object
-#define PARALLEL_SCAN_VTYPE simple_nursery_parallel_scan_vtype
#elif defined (SGEN_SPLIT_NURSERY)
#define SERIAL_SCAN_OBJECT split_nursery_serial_scan_object
#define SERIAL_SCAN_VTYPE split_nursery_serial_scan_vtype
-#define PARALLEL_SCAN_OBJECT split_nursery_parallel_scan_object
-#define PARALLEL_SCAN_VTYPE split_nursery_parallel_scan_vtype
#else
#error "Please define GC_CONF_NAME"
#endif
-#undef HANDLE_PTR
-#define HANDLE_PTR(ptr,obj) do { \
- void *__old = *(ptr); \
- void *__copy; \
- SGEN_OBJECT_LAYOUT_STATISTICS_MARK_BITMAP ((obj), (ptr)); \
- if (__old) { \
- PARALLEL_COPY_OBJECT ((ptr), queue); \
- __copy = *(ptr); \
- SGEN_COND_LOG (9, __old != __copy, "Overwrote field at %p with %p (was: %p)", (ptr), *(ptr), __old); \
- if (G_UNLIKELY (sgen_ptr_in_nursery (__copy) && !sgen_ptr_in_nursery ((ptr)))) \
- sgen_add_to_global_remset ((ptr), __copy); \
- } \
- } while (0)
-
-/*
- * Scan the object pointed to by @start for references to
- * other objects between @from_start and @from_end and copy
- * them to the gray_objects area.
- */
-static void
-PARALLEL_SCAN_OBJECT (char *start, SgenGrayQueue *queue)
-{
- SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
-
-#define SCAN_OBJECT_PROTOCOL
-#include "sgen-scan-object.h"
-
- SGEN_OBJECT_LAYOUT_STATISTICS_COMMIT_BITMAP;
- HEAVY_STAT (++stat_scan_object_called_nursery);
-}
-
-/*
- * scan_vtype:
- *
- * Scan the valuetype pointed to by START, described by DESC for references to
- * other objects between @from_start and @from_end and copy them to the gray_objects area.
- * Returns a pointer to the end of the object.
- */
-static void
-PARALLEL_SCAN_VTYPE (char *start, mword desc, SgenGrayQueue *queue BINARY_PROTOCOL_ARG (size_t size))
-{
- SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
-
- /* The descriptors include info about the MonoObject header as well */
- start -= sizeof (MonoObject);
-
-#define SCAN_OBJECT_NOVTABLE
-#define SCAN_OBJECT_PROTOCOL
-#include "sgen-scan-object.h"
-}
-
#undef HANDLE_PTR
/* Global remsets are handled in SERIAL_COPY_OBJECT_FROM_OBJ */
#define HANDLE_PTR(ptr,obj) do { \
} while (0)
static void
-SERIAL_SCAN_OBJECT (char *start, SgenGrayQueue *queue)
+SERIAL_SCAN_OBJECT (char *start, mword desc, SgenGrayQueue *queue)
{
SGEN_OBJECT_LAYOUT_STATISTICS_DECLARE_BITMAP;
+#ifdef HEAVY_STATISTICS
+ sgen_descriptor_count_scanned_object (desc);
+#endif
+
#define SCAN_OBJECT_PROTOCOL
#include "sgen-scan-object.h"
}
#define FILL_MINOR_COLLECTOR_SCAN_OBJECT(collector) do { \
- (collector)->parallel_ops.scan_object = PARALLEL_SCAN_OBJECT; \
- (collector)->parallel_ops.scan_vtype = PARALLEL_SCAN_VTYPE; \
(collector)->serial_ops.scan_object = SERIAL_SCAN_OBJECT; \
(collector)->serial_ops.scan_vtype = SERIAL_SCAN_VTYPE; \
} while (0)
if (obj_entry) {
/* obj_entry needs to be expanded */
src = dyn_array_ptr_pop (&dfs_stack);
+
if (src)
g_assert (!src->v.dfs1.forwarded_to);
if (!obj_entry->v.dfs1.is_visited) {
int num_links = 0;
+ mword desc = sgen_obj_get_descriptor (start);
obj_entry->v.dfs1.is_visited = 1;
if (addr0 < addr1) {
if (unpin_queue)
- GRAY_OBJECT_ENQUEUE (unpin_queue, addr0);
+ GRAY_OBJECT_ENQUEUE (unpin_queue, addr0, sgen_obj_get_descriptor_safe (addr0));
else
SGEN_UNPIN_OBJECT (addr0);
+ size = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)addr0));
+ CANARIFY_SIZE (size);
sgen_set_nursery_scan_start (addr0);
frag_end = addr0;
- size = SGEN_ALIGN_UP (sgen_safe_object_get_size ((MonoObject*)addr0));
++i;
} else {
frag_end = addr1;
void*
sgen_nursery_alloc (size_t size)
{
- SGEN_ASSERT (1, size >= sizeof (MonoObject) && size <= SGEN_MAX_SMALL_OBJ_SIZE, "Invalid nursery object size");
+ SGEN_ASSERT (1, size >= sizeof (MonoObject) && size <= (SGEN_MAX_SMALL_OBJ_SIZE + CANARY_SIZE), "Invalid nursery object size");
SGEN_LOG (4, "Searching nursery for size: %zd", size);
size = SGEN_ALIGN_UP (size);
obj_entry = dyn_array_ptr_pop (&dfs_stack);
if (obj_entry) {
+ mword desc;
src = dyn_array_ptr_pop (&dfs_stack);
obj = obj_entry->obj;
start = (char*)obj;
+ desc = sgen_obj_get_descriptor (start);
if (src) {
//g_print ("link %s -> %s\n", sgen_safe_name (src->obj), sgen_safe_name (obj));
#ifndef USE_MONO_CTX
gpointer regs [ARCH_NUM_REGS];
#endif
+ MonoContext ctx;
gpointer stack_start;
info->stopped_domain = mono_domain_get ();
- info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
info->signal = 0;
stop_count = sgen_global_stop_count;
/* duplicate signal */
if (0 && info->stop_count == stop_count)
return;
+#ifdef USE_MONO_CTX
+ if (context) {
+ mono_sigctx_to_monoctx (context, &ctx);
+ info->stopped_ip = MONO_CONTEXT_GET_IP (&ctx);
+ stack_start = MONO_CONTEXT_GET_SP (&ctx) - REDZONE_SIZE;
+ } else {
+ info->stopped_ip = NULL;
+ stack_start = NULL;
+ }
+#else
+ info->stopped_ip = context ? (gpointer) ARCH_SIGCTX_IP (context) : NULL;
stack_start = context ? (char*) ARCH_SIGCTX_SP (context) - REDZONE_SIZE : NULL;
+#endif
+
/* If stack_start is not within the limits, then don't set it
in info and we will be restarted. */
if (stack_start >= info->stack_start_limit && info->stack_start <= info->stack_end) {
#ifdef USE_MONO_CTX
if (context) {
- mono_sigctx_to_monoctx (context, &info->ctx);
+ memcpy (&info->ctx, &ctx, sizeof (MonoContext));
} else {
memset (&info->ctx, 0, sizeof (MonoContext));
}
}
void
-sgen_cement_iterate (IterateObjectCallbackFunc callback, void *callback_data)
+sgen_pin_cemented_objects (void)
{
int i;
for (i = 0; i < SGEN_CEMENT_HASH_SIZE; ++i) {
SGEN_ASSERT (5, cement_hash [i].count >= SGEN_CEMENT_THRESHOLD, "Cementing hash inconsistent");
- callback (cement_hash [i].obj, 0, callback_data);
+ sgen_pin_stage_ptr (cement_hash [i].obj);
+ /* FIXME: do pin stats if enabled */
}
}
void sgen_cement_concurrent_finish (void) MONO_INTERNAL;
gboolean sgen_cement_lookup (char *obj) MONO_INTERNAL;
gboolean sgen_cement_lookup_or_register (char *obj) MONO_INTERNAL;
-void sgen_cement_iterate (IterateObjectCallbackFunc callback, void *callback_data) MONO_INTERNAL;
+void sgen_pin_cemented_objects (void) MONO_INTERNAL;
void sgen_cement_clear_below_threshold (void) MONO_INTERNAL;
#endif
return first;
}
+/*
+ * Removes all NULL pointers from the queue.
+ */
+void
+sgen_pointer_queue_remove_nulls (SgenPointerQueue *queue)
+{
+ void **start, **cur, **end;
+ start = cur = queue->data;
+ end = queue->data + queue->next_slot;
+ while (cur < end) {
+ if (*cur)
+ *start++ = *cur++;
+ else
+ ++cur;
+ }
+ queue->next_slot = start - queue->data;
+}
+
+/*
+ * Sorts the pointers in the queue, then removes duplicates.
+ */
void
sgen_pointer_queue_sort_uniq (SgenPointerQueue *queue)
{
SGEN_LOG (5, "Pointer queue reduced to size: %lu", queue->next_slot);
}
+/*
+ * Does a linear search through the pointer queue to find `ptr`. Returns the index if
+ * found, otherwise (size_t)-1.
+ */
+size_t
+sgen_pointer_queue_find (SgenPointerQueue *queue, void *ptr)
+{
+ size_t i;
+ for (i = 0; i < queue->next_slot; ++i)
+ if (queue->data [i] == ptr)
+ return i;
+ return (size_t)-1;
+}
+
#endif
void sgen_pointer_queue_add (SgenPointerQueue *queue, void *ptr) MONO_INTERNAL;
void sgen_pointer_queue_clear (SgenPointerQueue *queue) MONO_INTERNAL;
+void sgen_pointer_queue_remove_nulls (SgenPointerQueue *queue) MONO_INTERNAL;
void sgen_pointer_queue_sort_uniq (SgenPointerQueue *queue) MONO_INTERNAL;
size_t sgen_pointer_queue_search (SgenPointerQueue *queue, void *addr) MONO_INTERNAL;
+size_t sgen_pointer_queue_find (SgenPointerQueue *queue, void *ptr) MONO_INTERNAL;
#endif
* object must be given in the variable "char* start". Afterwards,
* "start" will point to the start of the next object, if the scanned
* object contained references. If not, the value of "start" should
- * be considered undefined after executing this code.
+ * be considered undefined after executing this code. The object's
+ * GC descriptor must be in the variable "mword desc".
+ *
+ * The macro `HANDLE_PTR` will be invoked for every reference encountered while scanning the
+ * object. It is called with two parameters: The pointer to the reference (not the
+ * reference itself!) as well as the pointer to the scanned object.
*
* Modifiers (automatically undefined):
*
{
#ifndef SCAN_OBJECT_NOVTABLE
- GCVTable *vt;
- mword desc;
-
- vt = (GCVTable*)SGEN_LOAD_VTABLE (start);
- //type = vt->desc & 0x7;
-
- /* gcc should be smart enough to remove the bounds check, but it isn't:( */
- desc = vt->desc;
-
#if defined(SGEN_HEAVY_BINARY_PROTOCOL) && defined(SCAN_OBJECT_PROTOCOL)
- binary_protocol_scan_begin (start, vt, sgen_safe_object_get_size ((MonoObject*)start));
+ binary_protocol_scan_begin (start, SGEN_LOAD_VTABLE (start), sgen_safe_object_get_size ((MonoObject*)start));
#endif
#else
#if defined(SGEN_HEAVY_BINARY_PROTOCOL) && defined(SCAN_OBJECT_PROTOCOL)
#ifndef SCAN_OBJECT_NOVTABLE
case DESC_TYPE_COMPLEX_ARR:
/* this is an array of complex structs */
-#define SCAN OBJ_COMPLEX_ARR_FOREACH_PTR (vt, start)
+#define SCAN OBJ_COMPLEX_ARR_FOREACH_PTR (desc, start)
#ifndef SCAN_OBJECT_NOSCAN
SCAN;
#endif
return major_collector.alloc_object (vtable, objsize, has_references);
}
-static inline char*
-par_alloc_for_promotion (MonoVTable *vtable, char *obj, size_t objsize, gboolean has_references)
-{
- return major_collector.par_alloc_object (vtable, objsize, has_references);
-}
-
static SgenFragment*
build_fragments_get_exclude_head (void)
{
#define SGEN_SIMPLE_NURSERY
#define SERIAL_COPY_OBJECT simple_nursery_serial_copy_object
-#define PARALLEL_COPY_OBJECT simple_nursery_parallel_copy_object
#define SERIAL_COPY_OBJECT_FROM_OBJ simple_nursery_serial_copy_object_from_obj
#include "sgen-minor-copy-object.h"
collector->is_split = FALSE;
collector->alloc_for_promotion = alloc_for_promotion;
- collector->par_alloc_for_promotion = par_alloc_for_promotion;
collector->prepare_to_space = prepare_to_space;
collector->clear_fragments = clear_fragments;
/* The collector allocs from here. */
static SgenFragmentAllocator collector_allocator;
-static LOCK_DECLARE (par_alloc_buffer_refill_mutex);
-
static inline int
get_object_age (char *object)
{
return p;
}
-static char*
-par_alloc_for_promotion_slow_path (int age, size_t objsize)
-{
- char *p;
- size_t allocated_size;
- size_t aligned_objsize = (size_t)align_up (objsize, SGEN_TO_SPACE_GRANULE_BITS);
-
- mono_mutex_lock (&par_alloc_buffer_refill_mutex);
-
-restart:
- p = age_alloc_buffers [age].next;
- if (G_LIKELY (p + objsize <= age_alloc_buffers [age].end)) {
- if (SGEN_CAS_PTR ((void*)&age_alloc_buffers [age].next, p + objsize, p) != p)
- goto restart;
- } else {
- /* Reclaim remaining space - if we OOMd the nursery nothing to see here. */
- char *end = age_alloc_buffers [age].end;
- if (end) {
- do {
- p = age_alloc_buffers [age].next;
- } while (SGEN_CAS_PTR ((void*)&age_alloc_buffers [age].next, end, p) != p);
- sgen_clear_range (p, end);
- }
-
- /* By setting end to NULL we make sure no other thread can advance while we're updating.*/
- age_alloc_buffers [age].end = NULL;
- STORE_STORE_FENCE;
-
- p = sgen_fragment_allocator_par_range_alloc (
- &collector_allocator,
- MAX (aligned_objsize, AGE_ALLOC_BUFFER_DESIRED_SIZE),
- MAX (aligned_objsize, AGE_ALLOC_BUFFER_MIN_SIZE),
- &allocated_size);
- if (p) {
- set_age_in_range (p, p + allocated_size, age);
- age_alloc_buffers [age].next = p + objsize;
- STORE_STORE_FENCE; /* Next must arrive before the new value for next. */
- age_alloc_buffers [age].end = p + allocated_size;
- }
- }
-
- mono_mutex_unlock (&par_alloc_buffer_refill_mutex);
- return p;
-}
-
-static inline char*
-par_alloc_for_promotion (MonoVTable *vtable, char *obj, size_t objsize, gboolean has_references)
-{
- char *p;
- int age;
-
- age = get_object_age (obj);
- if (age >= promote_age)
- return major_collector.par_alloc_object (vtable, objsize, has_references);
-
-restart:
- p = age_alloc_buffers [age].next;
-
- LOAD_LOAD_FENCE; /* The read of ->next must happen before ->end */
-
- if (G_LIKELY (p + objsize <= age_alloc_buffers [age].end)) {
- if (SGEN_CAS_PTR ((void*)&age_alloc_buffers [age].next, p + objsize, p) != p)
- goto restart;
- } else {
- p = par_alloc_for_promotion_slow_path (age, objsize);
-
- /* Have we failed to promote to the nursery, lets just evacuate it to old gen. */
- if (!p)
- return major_collector.par_alloc_object (vtable, objsize, has_references);
- }
-
- *(MonoVTable**)p = vtable;
-
- return p;
-}
-
static char*
minor_alloc_for_promotion (MonoVTable *vtable, char *obj, size_t objsize, gboolean has_references)
{
return alloc_for_promotion (vtable, obj, objsize, has_references);
}
-static char*
-minor_par_alloc_for_promotion (MonoVTable *vtable, char *obj, size_t objsize, gboolean has_references)
-{
- /*
- We only need to check for a non-nursery object if we're doing a major collection.
- */
- if (!sgen_ptr_in_nursery (obj))
- return major_collector.par_alloc_object (vtable, objsize, has_references);
-
- return par_alloc_for_promotion (vtable, obj, objsize, has_references);
-}
-
static SgenFragment*
build_fragments_get_exclude_head (void)
{
#define SGEN_SPLIT_NURSERY
#define SERIAL_COPY_OBJECT split_nursery_serial_copy_object
-#define PARALLEL_COPY_OBJECT split_nursery_parallel_copy_object
#define SERIAL_COPY_OBJECT_FROM_OBJ split_nursery_serial_copy_object_from_obj
#include "sgen-minor-copy-object.h"
collector->is_split = TRUE;
collector->alloc_for_promotion = minor_alloc_for_promotion;
- collector->par_alloc_for_promotion = minor_par_alloc_for_promotion;
collector->prepare_to_space = prepare_to_space;
collector->clear_fragments = clear_fragments;
FILL_MINOR_COLLECTOR_COPY_OBJECT (collector);
FILL_MINOR_COLLECTOR_SCAN_OBJECT (collector);
- LOCK_INIT (par_alloc_buffer_refill_mutex);
}
--- /dev/null
+/*
+ * sgen-tagged-pointer.h: Macros for tagging and untagging pointers.
+ *
+ * Copyright (C) 2014 Xamarin Inc
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License 2.0 as published by the Free Software Foundation;
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License 2.0 along with this library; if not, write to the Free
+ * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __MONO_SGEN_TAGGED_POINTER_H__
+#define __MONO_SGEN_TAGGED_POINTER_H__
+
+#define SGEN_POINTER_IS_TAGGED_1(p) ((mword)(p) & 1)
+#define SGEN_POINTER_TAG_1(p) ((void*)((mword)(p) | 1))
+#define SGEN_POINTER_UNTAG_1(p) ((void*)((mword)(p) & ~1))
+
+#define SGEN_POINTER_IS_TAGGED_2(p) ((mword)(p) & 2)
+#define SGEN_POINTER_TAG_2(p) ((void*)((mword)(p) | 2))
+#define SGEN_POINTER_UNTAG_2(p) ((void*)((mword)(p) & ~2))
+
+#define SGEN_POINTER_IS_TAGGED_1_OR_2(p) ((mword)(p) & 3)
+#define SGEN_POINTER_UNTAG_12(p) ((void*)((mword)(p) & ~3))
+
+#endif
{
MonoObject *obj = data->obj;
char *start = (char*)obj;
+ mword desc = sgen_obj_get_descriptor (start);
#if DUMP_GRAPH
printf ("**scanning %p %s\n", obj, safe_name_bridge (obj));
{
MonoObject *obj = data->obj;
char *start = (char*)obj;
+ mword desc = sgen_obj_get_descriptor (start);
#include "sgen-scan-object.h"
}
static gboolean
collection_needs_workers (void)
{
- return sgen_collection_is_parallel () || sgen_collection_is_concurrent ();
+ return sgen_collection_is_concurrent ();
}
void
n -= m;
sgen_gray_object_alloc_queue_section (queue);
- memcpy (queue->first->objects,
+ memcpy (queue->first->entries,
victim_data->stealable_stack + victim_data->stealable_stack_fill - num + n,
- sizeof (char*) * m);
+ sizeof (GrayQueueEntry) * m);
queue->first->size = m;
/*
* Doing so trigger "assert not reached" in sgen-scan-object.h : we use the queue->cursor
* to compute the size of the first section during section allocation (via alloc_prepare_func
* -> workers_gray_queue_share_redirect -> sgen_gray_object_dequeue_section) which will be then
- * set to 0, because queue->cursor is still pointing to queue->first->objects [-1], thus
+ * set to 0, because queue->cursor is still pointing to queue->first->entries [-1], thus
* losing objects in the gray queue.
*/
- queue->cursor = (char**)queue->first->objects + queue->first->size - 1;
+ queue->cursor = queue->first->entries + queue->first->size - 1;
}
victim_data->stealable_stack_fill -= num;
* distribute gray queue.
*/
major = sgen_get_major_collector ();
- if (major->is_concurrent || major->is_parallel) {
+ if (major->is_concurrent) {
GrayQueueSection *section = sgen_section_gray_queue_dequeue (&workers_distribute_gray_queue);
if (section) {
sgen_gray_object_enqueue_section (&data->private_gray_queue, section);
int num = MIN (section->size, STEALABLE_STACK_SIZE - data->stealable_stack_fill);
memcpy (data->stealable_stack + data->stealable_stack_fill,
- section->objects + section->size - num,
- sizeof (char*) * num);
+ section->entries + section->size - num,
+ sizeof (GrayQueueEntry) * num);
section->size -= num;
data->stealable_stack_fill += num;
if (!collection_needs_workers ())
return;
- init_distribute_gray_queue (sgen_get_major_collector ()->is_concurrent || sgen_get_major_collector ()->is_parallel);
+ init_distribute_gray_queue (sgen_get_major_collector ()->is_concurrent);
}
void
{
int i;
- if (!sgen_get_major_collector ()->is_parallel && !sgen_get_major_collector ()->is_concurrent)
+ if (!sgen_get_major_collector ()->is_concurrent)
return;
//g_print ("initing %d workers\n", num_workers);
MONO_SEM_INIT (&workers_waiting_sem, 0);
MONO_SEM_INIT (&workers_done_sem, 0);
- init_distribute_gray_queue (sgen_get_major_collector ()->is_concurrent || sgen_get_major_collector ()->is_parallel);
+ init_distribute_gray_queue (sgen_get_major_collector ()->is_concurrent);
if (sgen_get_major_collector ()->alloc_worker_data)
workers_gc_thread_major_collector_data = sgen_get_major_collector ()->alloc_worker_data ();
mono_mutex_t stealable_stack_mutex;
volatile int stealable_stack_fill;
- char *stealable_stack [STEALABLE_STACK_SIZE];
+ GrayQueueEntry stealable_stack [STEALABLE_STACK_SIZE];
};
typedef void (*JobFunc) (WorkerData *worker_data, void *job_data);
{
int limit = mono_gc_get_los_limit ();
- return (limit - 2 - sizeof (MonoString)) / 2;
+ return (limit - 2 - offsetof (MonoString, chars)) / 2;
}
+
+void
+ves_icall_System_String_InternalSetLength (MonoString *str, gint32 new_length)
+{
+ mono_gc_set_string_length (str, new_length);
+}
+
int
ves_icall_System_String_GetLOSLimit (void) MONO_INTERNAL;
+void
+ves_icall_System_String_InternalSetLength (MonoString *str, gint32 new_length) MONO_INTERNAL;
+
#endif /* _MONO_CLI_STRING_ICALLS_H_ */
#include <unistd.h>
#endif
#include <string.h>
+#include <math.h>
#ifdef HAVE_SYS_SOCKET_H
#include <sys/socket.h>
#endif
#define THREAD_WANTS_A_BREAK(t) ((t->state & (ThreadState_StopRequested | \
ThreadState_SuspendRequested)) != 0)
-#define SPIN_TRYLOCK(i) (InterlockedCompareExchange (&(i), 1, 0) == 0)
-#define SPIN_LOCK(i) do { \
- if (SPIN_TRYLOCK (i)) \
- break; \
- } while (1)
-
-#define SPIN_UNLOCK(i) i = 0
#define SMALL_STACK (128 * (sizeof (gpointer) / 4) * 1024)
/* DEBUG: prints tp data every 2s */
KQUEUE_BACKEND
};
+enum {
+ MONITOR_STATE_AWAKE,
+ MONITOR_STATE_FALLING_ASLEEP,
+ MONITOR_STATE_SLEEPING
+};
+
typedef struct {
mono_mutex_t io_lock; /* access to sock_to_state */
int inited; // 0 -> not initialized , 1->initializing, 2->initialized, 3->cleaned up
void *pc_nthreads; /* Performance counter for total number of active threads */
/**/
volatile gint destroy_thread;
- volatile gint ignore_times; /* Used when there's a thread being created or destroyed */
- volatile gint sp_lock; /* spin lock used to protect ignore_times */
- volatile gint64 last_check;
- volatile gint64 time_sum;
- volatile gint n_sum;
- gint64 averages [2];
+#if DEBUG
+ volatile gint32 njobs;
+#endif
+ volatile gint32 nexecuted;
gboolean is_io;
} ThreadPool;
static void threadpool_start_idle_threads (ThreadPool *tp);
static void threadpool_kill_idle_threads (ThreadPool *tp);
static gboolean threadpool_start_thread (ThreadPool *tp);
+static void threadpool_kill_thread (ThreadPool *tp);
static void monitor_thread (gpointer data);
static void socket_io_cleanup (SocketIOData *data);
static MonoObject *get_io_event (MonoMList **list, gint event);
mono_mutex_t wsqs_lock;
static gboolean suspended;
+static volatile gint32 monitor_njobs = 0;
+static volatile gint32 monitor_state;
+static MonoSemType monitor_sem;
+static MonoInternalThread *monitor_internal_thread;
+
/* Hooks */
static MonoThreadPoolFunc tp_start_func;
static MonoThreadPoolFunc tp_finish_func;
mono_thread_set_execution_context (ares->original_context);
ares->original_context = NULL;
}
+
+#if DEBUG
+ InterlockedDecrement (&tp->njobs);
+#endif
+ if (!tp->is_io)
+ InterlockedIncrement (&tp->nexecuted);
+
+ if (InterlockedDecrement (&monitor_njobs) == 0)
+ monitor_state = MONITOR_STATE_FALLING_ASLEEP;
+
return exc;
}
}
#endif
+#define SAMPLES_PERIOD 500
+#define HISTORY_SIZE 10
+/* number of iteration without any jobs
+ in the queue before going to sleep */
+#define NUM_WAITING_ITERATIONS 10
+
+typedef struct {
+ gint32 nexecuted;
+ gint32 nthreads;
+ gint8 nthreads_diff;
+} SamplesHistory;
+
+static gint8
+monitor_heuristic (gint16 *current, gint16 *history_size, SamplesHistory *history, ThreadPool *tp)
+{
+ int i;
+ gint8 decision;
+ gint16 cur, max = 0;
+
+ /*
+ * The following heuristic tries to approach the optimal number of threads to maximize jobs throughput. To
+ * achieve this, it simply stores the number of jobs executed (nexecuted), the number of Threads (nthreads)
+ * and the decision (nthreads_diff) for the past HISTORY_SIZE periods of time, each period being of
+ * duration SAMPLES_PERIOD ms. This history gives us an insight into what happened, and to see if we should
+ * increase or reduce the number of threads by comparing the last period (current) to the best one.
+ *
+ * The algorithm can be describe as following :
+ * - if we have a better throughput than the best period : we should either increase the number of threads
+ * in case we already have more threads, either reduce the number of threads if we have less threads; this
+ * is equivalent to move away from the number of threads of the best period, because we are currently better
+ * - if we have a worse throughput than the best period : we should either decrease the number of threads if
+ * we have more threads, either increase the number of threads if we have less threads; this is equivalent
+ * to get closer to the number of threads of the best period, because we are currently worse
+ */
+
+ *history_size = MIN (*history_size + 1, HISTORY_SIZE);
+ cur = *current = (*current + 1) % *history_size;
+
+ history [cur].nthreads = tp->nthreads;
+ history [cur].nexecuted = InterlockedExchange (&tp->nexecuted, 0);
+
+ if (tp->waiting) {
+ /* if we have waiting thread in the pool, then do not create a new one */
+ history [cur].nthreads_diff = tp->waiting > 1 ? -1 : 0;
+ decision = 0;
+ } else if (tp->nthreads < tp->min_threads) {
+ history [cur].nthreads_diff = 1;
+ decision = 1;
+ } else if (*history_size <= 1) {
+ /* first iteration, let's add a thread by default */
+ history [cur].nthreads_diff = 1;
+ decision = 2;
+ } else {
+ max = cur == 0 ? 1 : 0;
+ for (i = 0; i < *history_size; i++) {
+ if (i == cur)
+ continue;
+ if (history [i].nexecuted > history [max].nexecuted)
+ max = i;
+ }
+
+ if (history [cur].nexecuted >= history [max].nexecuted) {
+ /* we improved the situation, let's continue ! */
+ history [cur].nthreads_diff = history [cur].nthreads >= history [max].nthreads ? 1 : -1;
+ decision = 3;
+ } else {
+ /* we made it worse, let's return to previous situation */
+ history [cur].nthreads_diff = history [cur].nthreads >= history [max].nthreads ? -1 : 1;
+ decision = 4;
+ }
+ }
+
+#if DEBUG
+ printf ("monitor_thread: decision: %1d, history [current]: {nexecuted: %5d, nthreads: %3d, waiting: %2d, nthreads_diff: %2d}, history [max]: {nexecuted: %5d, nthreads: %3d}\n",
+ decision, history [cur].nexecuted, history [cur].nthreads, tp->waiting, history [cur].nthreads_diff, history [max].nexecuted, history [max].nthreads);
+#endif
+
+ return history [cur].nthreads_diff;
+}
+
static void
monitor_thread (gpointer unused)
{
ThreadPool *pools [2];
MonoInternalThread *thread;
- guint32 ms;
- gboolean need_one;
int i;
+ guint32 ms;
+ gint8 num_waiting_iterations = 0;
+
+ gint16 history_size = 0, current = -1;
+ SamplesHistory *history = malloc (sizeof (SamplesHistory) * HISTORY_SIZE);
+
pools [0] = &async_tp;
pools [1] = &async_io_tp;
thread = mono_thread_internal_current ();
ves_icall_System_Threading_Thread_SetName_internal (thread, mono_string_new (mono_domain_get (), "Threadpool monitor"));
while (1) {
- ms = 500;
+ ms = SAMPLES_PERIOD;
i = 10; //number of spurious awakes we tolerate before doing a round of rebalancing.
do {
guint32 ts;
if (suspended)
continue;
+ switch (monitor_state) {
+ case MONITOR_STATE_AWAKE:
+ num_waiting_iterations = 0;
+ break;
+ case MONITOR_STATE_FALLING_ASLEEP:
+ if (++num_waiting_iterations == NUM_WAITING_ITERATIONS) {
+ if (monitor_state == MONITOR_STATE_FALLING_ASLEEP && InterlockedCompareExchange (&monitor_state, MONITOR_STATE_SLEEPING, MONITOR_STATE_FALLING_ASLEEP) == MONITOR_STATE_FALLING_ASLEEP) {
+ MONO_SEM_WAIT (&monitor_sem);
+
+ num_waiting_iterations = 0;
+ current = -1;
+ history_size = 0;
+ }
+ }
+ break;
+ case MONITOR_STATE_SLEEPING:
+ g_assert_not_reached ();
+ }
+
for (i = 0; i < 2; i++) {
ThreadPool *tp;
tp = pools [i];
- if (tp->waiting > 0)
- continue;
- need_one = (mono_cq_count (tp->queue) > 0);
- if (!need_one && !tp->is_io) {
- mono_mutex_lock (&wsqs_lock);
- for (i = 0; wsqs != NULL && i < wsqs->len; i++) {
- MonoWSQ *wsq;
- wsq = g_ptr_array_index (wsqs, i);
- if (mono_wsq_count (wsq) != 0) {
- need_one = TRUE;
- break;
- }
- }
- mono_mutex_unlock (&wsqs_lock);
+
+ if (tp->is_io) {
+ if (!tp->waiting && mono_cq_count (tp->queue) > 0)
+ threadpool_start_thread (tp);
+ } else {
+ gint8 nthreads_diff = monitor_heuristic (¤t, &history_size, history, tp);
+
+ if (nthreads_diff > 0)
+ threadpool_start_thread (tp);
+ else if (nthreads_diff < 0)
+ threadpool_kill_thread (tp);
}
- if (need_one)
- threadpool_start_thread (tp);
}
}
}
signal (SIGALRM, signal_handler);
alarm (2);
#endif
+
+ MONO_SEM_INIT (&monitor_sem, 0);
+ monitor_state = MONITOR_STATE_AWAKE;
+ monitor_njobs = 0;
}
static MonoAsyncResult *
mono_mutex_unlock (&wsqs_lock);
MONO_SEM_DESTROY (&async_tp.new_job);
}
+
+ MONO_SEM_DESTROY (&monitor_sem);
}
static gboolean
MONO_SEM_POST (&tp->new_job);
}
+static void
+threadpool_kill_thread (ThreadPool *tp)
+{
+ if (tp->destroy_thread == 0 && InterlockedCompareExchange (&tp->destroy_thread, 1, 0) == 0)
+ pulse_on_new_job (tp);
+}
+
void
icall_append_job (MonoObject *ar)
{
static void
threadpool_append_jobs (ThreadPool *tp, MonoObject **jobs, gint njobs)
{
- static int job_counter;
MonoObject *ar;
gint i;
if (tp->pool_status == 0 && InterlockedCompareExchange (&tp->pool_status, 1, 0) == 0) {
if (!tp->is_io) {
- mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, TRUE, SMALL_STACK);
+ monitor_internal_thread = mono_thread_create_internal (mono_get_root_domain (), monitor_thread, NULL, TRUE, SMALL_STACK);
+ monitor_internal_thread->flags |= MONO_THREAD_FLAG_DONT_MANAGE;
threadpool_start_thread (tp);
}
/* Create on demand up to min_threads to avoid startup penalty for apps that don't use
}
}
+ InterlockedAdd (&monitor_njobs, njobs);
+
+ if (monitor_state == MONITOR_STATE_SLEEPING && InterlockedCompareExchange (&monitor_state, MONITOR_STATE_AWAKE, MONITOR_STATE_SLEEPING) == MONITOR_STATE_SLEEPING)
+ MONO_SEM_POST (&monitor_sem);
+
+ if (monitor_state == MONITOR_STATE_FALLING_ASLEEP)
+ InterlockedCompareExchange (&monitor_state, MONITOR_STATE_AWAKE, MONITOR_STATE_FALLING_ASLEEP);
+
for (i = 0; i < njobs; i++) {
ar = jobs [i];
if (ar == NULL || mono_domain_is_unloading (ar->vtable->domain))
continue; /* Might happen when cleaning domain jobs */
- if (!tp->is_io && (InterlockedIncrement (&job_counter) % 10) == 0) {
- MonoAsyncResult *o = (MonoAsyncResult *) ar;
- o->add_time = mono_100ns_ticks ();
- }
threadpool_jobs_inc (ar);
#ifndef DISABLE_PERFCOUNTERS
mono_perfcounter_update_value (tp->pc_nitems, TRUE, 1);
mono_cq_enqueue (tp->queue, ar);
}
+#if DEBUG
+ InterlockedAdd (&tp->njobs, njobs);
+#endif
+
for (i = 0; tp->waiting > 0 && i < MIN(njobs, tp->max_threads); i++)
pulse_on_new_job (tp);
}
return (*data != NULL);
}
-static void
-process_idle_times (ThreadPool *tp, gint64 t)
-{
- gint64 ticks;
- gint64 avg;
- gboolean compute_avg;
- gint new_threads;
- gint64 per1;
-
- if (tp->ignore_times || t <= 0)
- return;
-
- compute_avg = FALSE;
- ticks = mono_100ns_ticks ();
- t = ticks - t;
- SPIN_LOCK (tp->sp_lock);
- if (tp->ignore_times) {
- SPIN_UNLOCK (tp->sp_lock);
- return;
- }
- tp->time_sum += t;
- tp->n_sum++;
- if (tp->last_check == 0)
- tp->last_check = ticks;
- else if (tp->last_check > 0 && (ticks - tp->last_check) > 5000000) {
- tp->ignore_times = 1;
- compute_avg = TRUE;
- }
- SPIN_UNLOCK (tp->sp_lock);
-
- if (!compute_avg)
- return;
-
- //printf ("Items: %d Time elapsed: %.3fs\n", tp->n_sum, (ticks - tp->last_check) / 10000.0);
- tp->last_check = ticks;
- new_threads = 0;
- avg = tp->time_sum / tp->n_sum;
- if (tp->averages [1] == 0) {
- tp->averages [1] = avg;
- } else {
- per1 = ((100 * (ABS (avg - tp->averages [1]))) / tp->averages [1]);
- if (per1 > 5) {
- if (avg > tp->averages [1]) {
- if (tp->averages [1] < tp->averages [0]) {
- new_threads = -1;
- } else {
- new_threads = 1;
- }
- } else if (avg < tp->averages [1] && tp->averages [1] < tp->averages [0]) {
- new_threads = 1;
- }
- } else {
- int min, n;
- min = tp->min_threads;
- n = tp->nthreads;
- if ((n - min) < min && tp->busy_threads == n)
- new_threads = 1;
- }
- /*
- if (new_threads != 0) {
- printf ("n: %d per1: %lld avg=%lld avg1=%lld avg0=%lld\n", new_threads, per1, avg, tp->averages [1], tp->averages [0]);
- }
- */
- }
-
- tp->time_sum = 0;
- tp->n_sum = 0;
-
- tp->averages [0] = tp->averages [1];
- tp->averages [1] = avg;
- tp->ignore_times = 0;
-
- if (new_threads == -1) {
- if (tp->destroy_thread == 0 && InterlockedCompareExchange (&tp->destroy_thread, 1, 0) == 0)
- pulse_on_new_job (tp);
- }
-}
-
static gboolean
should_i_die (ThreadPool *tp)
{
if (tp_item_begin_func)
tp_item_begin_func (tp_item_user_data);
- if (!is_io_task && ar->add_time > 0)
- process_idle_times (tp, ar->add_time);
exc = mono_async_invoke (tp, ar);
if (tp_item_end_func)
tp_item_end_func (tp_item_user_data);
ar = NULL;
data = NULL;
must_die = should_i_die (tp);
- if (!must_die && (tp->is_io || !mono_wsq_local_pop (&data)))
- dequeue_or_steal (tp, &data, wsq);
+ if (must_die) {
+ mono_wsq_suspend (wsq);
+ } else {
+ if (tp->is_io || !mono_wsq_local_pop (&data))
+ dequeue_or_steal (tp, &data, wsq);
+ }
n_naps = 0;
while (!must_die && !data && n_naps < 4) {
ThreadApartmentState_Unknown = 0x00000002
} MonoThreadApartmentState;
-typedef void (*MonoThreadNotifyPendingExcFunc) (void);
-
#define SPECIAL_STATIC_NONE 0
#define SPECIAL_STATIC_THREAD 1
#define SPECIAL_STATIC_CONTEXT 2
typedef struct _MonoInternalThread MonoInternalThread;
typedef void (*MonoThreadCleanupFunc) (MonoInternalThread* thread);
+/* INFO has type MonoThreadInfo* */
+typedef void (*MonoThreadNotifyPendingExcFunc) (gpointer info);
MonoInternalThread* mono_thread_create_internal (MonoDomain *domain, gpointer func, gpointer arg, gboolean threadpool_thread, guint32 stack_size) MONO_INTERNAL;
if (mono_thread_notify_pending_exc_fn && !running_managed)
/* The JIT will notify the thread about the interruption */
/* This shouldn't take any locks */
- mono_thread_notify_pending_exc_fn ();
+ mono_thread_notify_pending_exc_fn (NULL);
/* this will awake the thread if it is in WaitForSingleObject
or similar */
*/
gpointer interrupt_handle;
+ if (mono_thread_notify_pending_exc_fn)
+ /* The JIT will notify the thread about the interruption */
+ mono_thread_notify_pending_exc_fn (info);
+
interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
mono_thread_info_finish_suspend_and_resume (info);
mono_thread_info_finish_interrupt (interrupt_handle);
InterlockedIncrement (&thread_interruption_requested);
if (interrupt)
interrupt_handle = mono_thread_info_prepare_interrupt (thread->handle);
+ if (mono_thread_notify_pending_exc_fn && !running_managed)
+ /* The JIT will notify the thread about the interruption */
+ mono_thread_notify_pending_exc_fn (info);
mono_thread_info_finish_suspend_and_resume (info);
if (interrupt)
mono_thread_info_finish_interrupt (interrupt_handle);
acfg->llvm_label_prefix = "";
acfg->user_symbol_prefix = "";
+#if defined(TARGET_X86)
+ g_string_append (acfg->llc_args, " -march=x86 -mattr=sse4.1");
+#endif
+
#if defined(TARGET_AMD64)
g_string_append (acfg->llc_args, " -march=x86-64 -mattr=sse4.1");
#endif
case MONO_PATCH_INFO_MSCORLIB_GOT_ADDR:
case MONO_PATCH_INFO_JIT_TLS_ID:
case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR:
+ break;
case MONO_PATCH_INFO_CASTCLASS_CACHE:
+ encode_value (patch_info->data.index, p, &p);
break;
case MONO_PATCH_INFO_METHOD_REL:
encode_value ((gint)patch_info->data.offset, p, &p);
aot_printerrf (acfg, "The 'soft-debug' option is not supported when compiling with LLVM.\n");
return 1;
}
+
+ mini_llvm_init ();
}
if (acfg->aot_opts.full_aot)
res = emit_llvm_file (acfg);
if (!res)
- return FALSE;
+ return 1;
}
#endif
mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: loading from cache: '%s'.", fname);
module = mono_dl_open (fname, MONO_DL_LAZY, NULL);
- if (module)
+ if (module) {
+ mono_trace (G_LOG_LEVEL_INFO, MONO_TRACE_AOT, "AOT: found in cache: '%s'.", fname);
return module;
+ }
if (!strcmp (assembly->aname.name, "mscorlib") && !mscorlib_aot_loaded)
/*
fclose (failure_file);
}
- mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compiling assembly '%s'... ", assembly->image->name);
+ mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compiling assembly '%s', logfile: '%s.log'... ", assembly->image->name, fname);
/*
* We need to invoke the AOT compiler here. There are multiple approaches:
* - fork a new process and do the work there.
*/
if (in_process) {
- aot_options = g_strdup_printf ("outfile=%s,internal-logfile=%s.log", fname, fname);
+ aot_options = g_strdup_printf ("outfile=%s,internal-logfile=%s.log%s%s", fname, fname, config->aot_options ? "," : "", config->aot_options ? config->aot_options : "");
/* Maybe due this in another thread ? */
res = mono_compile_assembly (assembly, mono_parse_default_optimizations (NULL), aot_options);
- if (!res) {
+ if (res) {
+ mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compilation failed.");
failure_fname = g_strdup_printf ("%s.failure", fname);
failure_file = fopen (failure_fname, "a+");
fclose (failure_file);
g_free (failure_fname);
+ } else {
+ mono_trace (G_LOG_LEVEL_MESSAGE, MONO_TRACE_AOT, "AOT: compilation succeeded.");
}
} else {
/*
decode_llvm_mono_eh_frame (MonoAotModule *amodule, MonoDomain *domain,
MonoMethod *method, guint8 *code,
MonoJitExceptionInfo *clauses, int num_clauses,
- int extra_size, GSList **nesting,
+ MonoJitInfoFlags flags,
+ GSList **nesting,
int *this_reg, int *this_offset)
{
guint8 *p;
* allocate a new JI.
*/
jinfo =
- mono_domain_alloc0_lock_free (domain, MONO_SIZEOF_JIT_INFO + (sizeof (MonoJitExceptionInfo) * (ei_len + nested_len)) + extra_size);
+ mono_domain_alloc0_lock_free (domain, mono_jit_info_size (flags, ei_len + nested_len, 0));
+ mono_jit_info_init (jinfo, method, code, code_len, flags, ei_len + nested_len, 0);
- jinfo->code_size = code_len;
jinfo->unwind_info = mono_cache_unwind_info (info.unw_info, info.unw_info_len);
- jinfo->d.method = method;
- jinfo->code_start = code;
- jinfo->domain_neutral = 0;
/* This signals that unwind_info points to a normal cached unwind info */
jinfo->from_aot = 0;
- jinfo->num_clauses = ei_len + nested_len;
+ jinfo->from_llvm = 1;
for (i = 0; i < ei_len; ++i) {
/*
- * orig_jinfo contains the original IL exception info saved by the AOT
+ * clauses contains the original IL exception info saved by the AOT
* compiler, we have to combine that with the information produced by LLVM
*/
/* The type_info entries contain IL clause indexes */
{
int i, buf_len, num_clauses, len;
MonoJitInfo *jinfo;
- guint unwind_info, flags;
+ MonoJitInfoFlags flags = JIT_INFO_NONE;
+ guint unwind_info, eflags;
gboolean has_generic_jit_info, has_dwarf_unwind_info, has_clauses, has_seq_points, has_try_block_holes, has_arch_eh_jit_info;
gboolean from_llvm, has_gc_map;
guint8 *p;
async = mono_thread_info_is_async_context ();
p = ex_info;
- flags = decode_value (p, &p);
- has_generic_jit_info = (flags & 1) != 0;
- has_dwarf_unwind_info = (flags & 2) != 0;
- has_clauses = (flags & 4) != 0;
- has_seq_points = (flags & 8) != 0;
- from_llvm = (flags & 16) != 0;
- has_try_block_holes = (flags & 32) != 0;
- has_gc_map = (flags & 64) != 0;
- has_arch_eh_jit_info = (flags & 128) != 0;
+ eflags = decode_value (p, &p);
+ has_generic_jit_info = (eflags & 1) != 0;
+ has_dwarf_unwind_info = (eflags & 2) != 0;
+ has_clauses = (eflags & 4) != 0;
+ has_seq_points = (eflags & 8) != 0;
+ from_llvm = (eflags & 16) != 0;
+ has_try_block_holes = (eflags & 32) != 0;
+ has_gc_map = (eflags & 64) != 0;
+ has_arch_eh_jit_info = (eflags & 128) != 0;
if (has_dwarf_unwind_info) {
unwind_info = decode_value (p, &p);
} else {
unwind_info = decode_value (p, &p);
}
- if (has_generic_jit_info)
+ if (has_generic_jit_info) {
+ flags |= JIT_INFO_HAS_GENERIC_JIT_INFO;
generic_info_size = sizeof (MonoGenericJitInfo);
- else
+ } else {
generic_info_size = 0;
+ }
if (has_try_block_holes) {
num_holes = decode_value (p, &p);
+ flags |= JIT_INFO_HAS_TRY_BLOCK_HOLES;
try_holes_info_size = sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo);
} else {
num_holes = try_holes_info_size = 0;
num_clauses = decode_value (p, &p);
else
num_clauses = 0;
- if (has_arch_eh_jit_info)
+ if (has_arch_eh_jit_info) {
+ flags |= JIT_INFO_HAS_ARCH_EH_INFO;
arch_eh_jit_info_size = sizeof (MonoArchEHJitInfo);
- else
+ } else {
arch_eh_jit_info_size = 0;
+ }
if (from_llvm) {
MonoJitExceptionInfo *clauses;
}
}
- jinfo = decode_llvm_mono_eh_frame (amodule, domain, method, code, clauses, num_clauses, generic_info_size + try_holes_info_size + arch_eh_jit_info_size, nesting, &this_reg, &this_offset);
- jinfo->from_llvm = 1;
+ jinfo = decode_llvm_mono_eh_frame (amodule, domain, method, code, clauses, num_clauses, flags, nesting, &this_reg, &this_offset);
g_free (clauses);
for (i = 0; i < num_clauses; ++i)
g_slist_free (nesting [i]);
g_free (nesting);
} else {
- len = MONO_SIZEOF_JIT_INFO + (sizeof (MonoJitExceptionInfo) * num_clauses) + generic_info_size + try_holes_info_size + arch_eh_jit_info_size;
+ len = mono_jit_info_size (flags, num_clauses, num_holes);
jinfo = alloc0_jit_info_data (domain, len, async);
- jinfo->num_clauses = num_clauses;
+ mono_jit_info_init (jinfo, method, code, code_len, flags, num_clauses, num_holes);
for (i = 0; i < jinfo->num_clauses; ++i) {
MonoJitExceptionInfo *ei = &jinfo->clauses [i];
ei->handler_start = code + decode_value (p, &p);
}
- jinfo->code_size = code_len;
jinfo->unwind_info = unwind_info;
- jinfo->d.method = method;
- jinfo->code_start = code;
jinfo->domain_neutral = 0;
jinfo->from_aot = 1;
}
- /*
- * Set all the 'has' flags, the mono_jit_info_get () functions depends on this to
- * compute the addresses of data blocks.
- */
- if (has_generic_jit_info)
- jinfo->has_generic_jit_info = 1;
- if (has_arch_eh_jit_info)
- jinfo->has_arch_eh_info = 1;
- if (has_try_block_holes)
- jinfo->has_try_block_holes = 1;
-
if (has_try_block_holes) {
MonoTryBlockHoleTableJitInfo *table;
case MONO_PATCH_INFO_MONITOR_ENTER:
case MONO_PATCH_INFO_MONITOR_EXIT:
case MONO_PATCH_INFO_GC_CARD_TABLE_ADDR:
- case MONO_PATCH_INFO_CASTCLASS_CACHE:
case MONO_PATCH_INFO_JIT_TLS_ID:
break;
+ case MONO_PATCH_INFO_CASTCLASS_CACHE:
+ ji->data.index = decode_value (p, &p);
+ break;
case MONO_PATCH_INFO_RGCTX_FETCH: {
gboolean res;
MonoJumpInfoRgctxEntry *entry;
br_reg: src1:i len:8
bigmul: len:8 dest:l src1:i src2:i
bigmul_un: len:8 dest:l src1:i src2:i
-tls_get: len:8 dest:i clob:c
+tls_get: len:24 dest:i clob:c
# 32 bit opcodes
int_add: dest:i src1:i src2:i len:4
#define HEADER_LENGTH 11
#define MAJOR_VERSION 2
-#define MINOR_VERSION 36
+#define MINOR_VERSION 37
typedef enum {
CMD_SET_VM = 1,
INVOKE_FLAG_DISABLE_BREAKPOINTS = 1,
INVOKE_FLAG_SINGLE_THREADED = 2,
INVOKE_FLAG_RETURN_OUT_THIS = 4,
- INVOKE_FLAG_RETURN_OUT_ARGS = 8
+ INVOKE_FLAG_RETURN_OUT_ARGS = 8,
+ INVOKE_FLAG_VIRTUAL = 16
} InvokeFlags;
typedef enum {
return ERR_INVALID_ARGUMENT;
}
m = mono_object_get_virtual_method (this, m);
+ } else if (invoke->flags & INVOKE_FLAG_VIRTUAL) {
+ if (!this) {
+ DEBUG (1, fprintf (log_file, "[%p] Error: invoke with INVOKE_FLAG_VIRTUAL flag set without this argument.\n", (gpointer)GetCurrentThreadId ()));
+ return ERR_INVALID_ARGUMENT;
+ }
+ m = mono_object_get_virtual_method (this, m);
}
DEBUG (1, fprintf (log_file, "[%p] Invoking method '%s' on receiver '%s'.\n", (gpointer)GetCurrentThreadId (), mono_method_full_name (m, TRUE), this ? this->vtable->klass->name : "<null>"));
guint8 *cfa;
guint32 unwind_info_len;
guint8 *unwind_info;
- guint8 *epilog;
+ guint8 *epilog = NULL;
frame->type = FRAME_TYPE_MANAGED;
printf ("%s %p %p\n", ji->d.method->name, ji->code_start, ip);
mono_print_unwind_info (unwind_info, unwind_info_len);
*/
- epilog = (guint8*)ji->code_start + ji->code_size - mono_jinfo_get_epilog_size (ji);
+ /* LLVM compiled code doesn't have this info */
+ if (ji->has_arch_eh_info)
+ epilog = (guint8*)ji->code_start + ji->code_size - mono_jinfo_get_epilog_size (ji);
regs [AMD64_RAX] = new_ctx->rax;
regs [AMD64_RBX] = new_ctx->rbx;
mono_unwind_frame (unwind_info, unwind_info_len, ji->code_start,
(guint8*)ji->code_start + ji->code_size,
- ip, &epilog, regs, MONO_MAX_IREGS + 1,
+ ip, epilog ? &epilog : NULL, regs, MONO_MAX_IREGS + 1,
save_locations, MONO_MAX_IREGS, &cfa);
new_ctx->rax = regs [AMD64_RAX];
/* Adjust IP */
new_ctx->rip --;
-#ifndef MONO_AMD64_NO_PUSHES
- /* Pop arguments off the stack */
- if (ji->has_arch_eh_info)
- new_ctx->rsp += mono_jit_info_get_arch_eh_info (ji)->stack_size;
-#endif
-
return TRUE;
} else if (*lmf) {
guint64 rip;
* exception.
*/
void
-mono_arch_notify_pending_exc (void)
+mono_arch_notify_pending_exc (MonoThreadInfo *info)
{
MonoLMF *lmf = mono_get_lmf ();
+ if (!info) {
+ lmf = mono_get_lmf ();
+ } else {
+ g_assert (info->suspend_state.valid);
+ lmf = info->suspend_state.unwind_data [MONO_UNWIND_DATA_LMF];
+ }
+
if (!lmf)
/* Not yet started */
return;
/* Adjust IP */
new_ctx->eip --;
-
-#ifndef MONO_X86_NO_PUSHES
- /* Pop arguments off the stack */
- if (ji->has_arch_eh_info) {
- int stack_size;
-
- stack_size = mono_jit_info_get_arch_eh_info (ji)->stack_size;
-
- if (stack_size) {
-#ifdef ENABLE_LLVM
- MonoJitInfo *caller_ji;
-
- caller_ji = mini_jit_info_table_find (domain, (char*)new_ctx->eip, NULL);
- /* LLVM doesn't push the arguments */
- if (caller_ji && !caller_ji->from_llvm)
- new_ctx->esp += stack_size;
-#else
- new_ctx->esp += stack_size;
-#endif
- }
- }
-#endif
-
return TRUE;
} else if (*lmf) {
}
}
+ [Category ("GSHAREDVT")]
static int test_0_synchronized_gshared () {
var c = new SyncClass<string> ();
if (c.getInstance () != typeof (string))
var s = f2 (f);
return s == "A" ? 0 : 1;
}
+
+ public interface ICovariant<out R>
+ {
+ }
+
+ // Deleting the `out` modifier from this line stop the problem
+ public interface IExtCovariant<out R> : ICovariant<R>
+ {
+ }
+
+ public class Sample<R> : ICovariant<R>
+ {
+ }
+
+ public interface IMyInterface
+ {
+ }
+
+ public static int test_0_variant_cast_cache () {
+ object covariant = new Sample<IMyInterface>();
+
+ var foo = (ICovariant<IMyInterface>)(covariant);
+
+ try {
+ var extCovariant = (IExtCovariant<IMyInterface>)covariant;
+ return 1;
+ } catch {
+ return 0;
+ }
+ }
}
#if !MOBILE
#define BRANCH_COST 10
#define INLINE_LENGTH_LIMIT 20
+
+/* These have 'cfg' as an implicit argument */
#define INLINE_FAILURE(msg) do { \
- if ((cfg->method != method) && (method->wrapper_type == MONO_WRAPPER_NONE)) { \
- if (cfg->verbose_level >= 2) \
- printf ("inline failed: %s\n", msg); \
- goto inline_failure; \
+ if ((cfg->method != cfg->current_method) && (cfg->current_method->wrapper_type == MONO_WRAPPER_NONE)) { \
+ inline_failure (cfg, msg); \
+ goto exception_exit; \
} \
} while (0)
#define CHECK_CFG_EXCEPTION do {\
- if (cfg->exception_type != MONO_EXCEPTION_NONE)\
- goto exception_exit;\
+ if (cfg->exception_type != MONO_EXCEPTION_NONE) \
+ goto exception_exit; \
} while (0)
-#define METHOD_ACCESS_FAILURE do { \
- char *method_fname = mono_method_full_name (method, TRUE); \
- char *cil_method_fname = mono_method_full_name (cil_method, TRUE); \
- mono_cfg_set_exception (cfg, MONO_EXCEPTION_METHOD_ACCESS); \
- cfg->exception_message = g_strdup_printf ("Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname); \
- g_free (method_fname); \
- g_free (cil_method_fname); \
- goto exception_exit; \
+#define METHOD_ACCESS_FAILURE(method, cmethod) do { \
+ method_access_failure ((cfg), (method), (cmethod)); \
+ goto exception_exit; \
} while (0)
-#define FIELD_ACCESS_FAILURE do { \
- char *method_fname = mono_method_full_name (method, TRUE); \
- char *field_fname = mono_field_full_name (field); \
- mono_cfg_set_exception (cfg, MONO_EXCEPTION_FIELD_ACCESS); \
- cfg->exception_message = g_strdup_printf ("Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname); \
- g_free (method_fname); \
- g_free (field_fname); \
+#define FIELD_ACCESS_FAILURE(method, field) do { \
+ field_access_failure ((cfg), (method), (field)); \
goto exception_exit; \
} while (0)
#define GENERIC_SHARING_FAILURE(opcode) do { \
- if (cfg->generic_sharing_context) { \
- if (cfg->verbose_level > 2) \
- printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __LINE__); \
- mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
+ if (cfg->gshared) { \
+ gshared_failure (cfg, opcode, __FILE__, __LINE__); \
goto exception_exit; \
} \
} while (0)
#define GSHAREDVT_FAILURE(opcode) do { \
if (cfg->gsharedvt) { \
- cfg->exception_message = g_strdup_printf ("gsharedvt failed for method %s.%s.%s/%d opcode %s %s:%d", method->klass->name_space, method->klass->name, method->name, method->signature->param_count, mono_opcode_name ((opcode)), __FILE__, __LINE__); \
- if (cfg->verbose_level >= 2) \
- printf ("%s\n", cfg->exception_message); \
- mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
+ gsharedvt_failure (cfg, opcode, __FILE__, __LINE__); \
goto exception_exit; \
} \
} while (0)
printf ("AOT disabled: %s:%d\n", __FILE__, __LINE__); \
(cfg)->disable_aot = TRUE; \
} while (0)
+#define LOAD_ERROR do { \
+ break_on_unverified (); \
+ mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD); \
+ goto exception_exit; \
+ } while (0)
+
+#define TYPE_LOAD_ERROR(klass) do { \
+ cfg->exception_ptr = klass; \
+ LOAD_ERROR; \
+ } while (0)
/* Determine whenever 'ins' represents a load of the 'this' argument */
#define MONO_CHECK_THIS(ins) (mono_method_signature (cfg->method)->hasthis && ((ins)->opcode == OP_MOVE) && ((ins)->sreg1 == cfg->args [0]->dreg))
helper_sig_monitor_enter_exit_trampoline_llvm = mono_create_icall_signature ("void object");
}
+static MONO_NEVER_INLINE void
+break_on_unverified (void)
+{
+ if (mini_get_debug_options ()->break_on_unverified)
+ G_BREAKPOINT ();
+}
+
+static MONO_NEVER_INLINE void
+method_access_failure (MonoCompile *cfg, MonoMethod *method, MonoMethod *cil_method)
+{
+ char *method_fname = mono_method_full_name (method, TRUE);
+ char *cil_method_fname = mono_method_full_name (cil_method, TRUE);
+ mono_cfg_set_exception (cfg, MONO_EXCEPTION_METHOD_ACCESS);
+ cfg->exception_message = g_strdup_printf ("Method `%s' is inaccessible from method `%s'\n", cil_method_fname, method_fname);
+ g_free (method_fname);
+ g_free (cil_method_fname);
+}
+
+static MONO_NEVER_INLINE void
+field_access_failure (MonoCompile *cfg, MonoMethod *method, MonoClassField *field)
+{
+ char *method_fname = mono_method_full_name (method, TRUE);
+ char *field_fname = mono_field_full_name (field);
+ mono_cfg_set_exception (cfg, MONO_EXCEPTION_FIELD_ACCESS);
+ cfg->exception_message = g_strdup_printf ("Field `%s' is inaccessible from method `%s'\n", field_fname, method_fname);
+ g_free (method_fname);
+ g_free (field_fname);
+}
+
+static MONO_NEVER_INLINE void
+inline_failure (MonoCompile *cfg, const char *msg)
+{
+ if (cfg->verbose_level >= 2)
+ printf ("inline failed: %s\n", msg);
+ mono_cfg_set_exception (cfg, MONO_EXCEPTION_INLINE_FAILED);
+}
+
+static MONO_NEVER_INLINE void
+gshared_failure (MonoCompile *cfg, int opcode, const char *file, int line)
+{
+ if (cfg->verbose_level > 2) \
+ printf ("sharing failed for method %s.%s.%s/%d opcode %s line %d\n", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), __LINE__);
+ mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
+}
+
+static MONO_NEVER_INLINE void
+gsharedvt_failure (MonoCompile *cfg, int opcode, const char *file, int line)
+{
+ cfg->exception_message = g_strdup_printf ("gsharedvt failed for method %s.%s.%s/%d opcode %s %s:%d", cfg->current_method->klass->name_space, cfg->current_method->klass->name, cfg->current_method->name, cfg->current_method->signature->param_count, mono_opcode_name ((opcode)), file, line);
+ if (cfg->verbose_level >= 2)
+ printf ("%s\n", cfg->exception_message);
+ mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED);
+}
+
/*
* When using gsharedvt, some instatiations might be verifiable, and some might be not. i.e.
* foo<T> (int i) { ldarg.0; box T; }
mono_cfg_set_exception (cfg, MONO_EXCEPTION_GENERIC_SHARING_FAILED); \
goto exception_exit; \
} \
- if (mini_get_debug_options ()->break_on_unverified) \
- G_BREAKPOINT (); \
- else \
- goto unverified; \
+ break_on_unverified (); \
+ goto unverified; \
} while (0)
-#define LOAD_ERROR do { if (mini_get_debug_options ()->break_on_unverified) G_BREAKPOINT (); else goto load_error; } while (0)
-
-#define TYPE_LOAD_ERROR(klass) do { if (mini_get_debug_options ()->break_on_unverified) G_BREAKPOINT (); else { cfg->exception_ptr = klass; goto load_error; } } while (0)
-
#define GET_BBLOCK(cfg,tblock,ip) do { \
(tblock) = cfg->cil_offset_to_bb [(ip) - cfg->cil_start]; \
if (!(tblock)) { \
return res;
}
+static MonoInst*
+emit_castclass_with_cache_nonshared (MonoCompile *cfg, MonoInst *obj, MonoClass *klass, MonoBasicBlock **out_bblock)
+{
+ MonoInst *args [3];
+ int idx;
+
+ /* obj */
+ args [0] = obj;
+
+ /* klass */
+ EMIT_NEW_CLASSCONST (cfg, args [1], klass);
+
+ /* inline cache*/
+ if (cfg->compile_aot) {
+ /* Each CASTCLASS_CACHE patch needs a unique index which identifies the call site */
+ cfg->castclass_cache_index ++;
+ idx = (cfg->method_index << 16) | cfg->castclass_cache_index;
+ EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, GINT_TO_POINTER (idx));
+ } else {
+ EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
+ }
+
+ /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
+
+ return emit_castclass_with_cache (cfg, klass, args, out_bblock);
+}
+
/*
* Returns NULL and set the cfg exception on error.
*/
int i;
#endif
+ if (cfg->disable_inline)
+ return FALSE;
if (cfg->generic_sharing_context)
return FALSE;
}
#endif
+ if (g_list_find (cfg->dont_inline, method))
+ return FALSE;
+
return TRUE;
}
}
}
+/*
+ * inline_method:
+ *
+ * Return the cost of inlining CMETHOD.
+ */
static int
inline_method (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, MonoInst **sp,
- guchar *ip, guint real_offset, GList *dont_inline, gboolean inline_always)
+ guchar *ip, guint real_offset, gboolean inline_always, MonoBasicBlock **out_cbb)
{
MonoInst *ins, *rvar = NULL;
MonoMethodHeader *cheader;
guint32 prev_cil_offset_to_bb_len;
MonoMethod *prev_current_method;
MonoGenericContext *prev_generic_context;
- gboolean ret_var_set, prev_ret_var_set, virtual = FALSE;
+ gboolean ret_var_set, prev_ret_var_set, prev_disable_inline, virtual = FALSE;
g_assert (cfg->exception_type == MONO_EXCEPTION_NONE);
prev_current_method = cfg->current_method;
prev_generic_context = cfg->generic_context;
prev_ret_var_set = cfg->ret_var_set;
+ prev_disable_inline = cfg->disable_inline;
if (*ip == CEE_CALLVIRT && !(cmethod->flags & METHOD_ATTRIBUTE_STATIC))
virtual = TRUE;
- costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, dont_inline, sp, real_offset, virtual);
+ costs = mono_method_to_ir (cfg, cmethod, sbblock, ebblock, rvar, sp, real_offset, virtual);
ret_var_set = cfg->ret_var_set;
cfg->current_method = prev_current_method;
cfg->generic_context = prev_generic_context;
cfg->ret_var_set = prev_ret_var_set;
+ cfg->disable_inline = prev_disable_inline;
cfg->inline_depth --;
if ((costs >= 0 && costs < 60) || inline_always) {
cfg->cbb = ebblock;
}
+ *out_cbb = cfg->cbb;
+
if (rvar) {
/*
* If the inlined method contains only a throw, then the ret var is not
#define CHECK_LOCAL(num) if ((unsigned)(num) >= (unsigned)header->num_locals) UNVERIFIED
#define CHECK_OPSIZE(size) if (ip + size > end) UNVERIFIED
#define CHECK_UNVERIFIABLE(cfg) if (cfg->unverifiable) UNVERIFIED
-#define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) {cfg->exception_ptr = klass; LOAD_ERROR;}
+#define CHECK_TYPELOAD(klass) if (!(klass) || (klass)->exception_type) TYPE_LOAD_ERROR ((klass))
/* offset from br.s -> br like opcodes */
#define BIG_BRANCH_OFFSET 13
emit_init_local (cfg, local, type, TRUE);
return ip + 6;
}
-load_error:
+ exception_exit:
return NULL;
}
return addr;
}
+/*
+ * handle_ctor_call:
+ *
+ * Handle calls made to ctors from NEWOBJ opcodes.
+ *
+ * REF_BBLOCK will point to the current bblock after the call.
+ */
+static void
+handle_ctor_call (MonoCompile *cfg, MonoMethod *cmethod, MonoMethodSignature *fsig, int context_used,
+ MonoInst **sp, guint8 *ip, MonoBasicBlock **ref_bblock, int *inline_costs)
+{
+ MonoInst *vtable_arg = NULL, *callvirt_this_arg = NULL, *ins;
+ MonoBasicBlock *bblock = *ref_bblock;
+
+ if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
+ mono_method_is_generic_sharable (cmethod, TRUE)) {
+ if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
+ mono_class_vtable (cfg->domain, cmethod->klass);
+ CHECK_TYPELOAD (cmethod->klass);
+
+ vtable_arg = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
+ } else {
+ if (context_used) {
+ vtable_arg = emit_get_rgctx_klass (cfg, context_used,
+ cmethod->klass, MONO_RGCTX_INFO_VTABLE);
+ } else {
+ MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
+
+ CHECK_TYPELOAD (cmethod->klass);
+ EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
+ }
+ }
+ }
+
+ /* Avoid virtual calls to ctors if possible */
+ if (mono_class_is_marshalbyref (cmethod->klass))
+ callvirt_this_arg = sp [0];
+
+ if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
+ g_assert (MONO_TYPE_IS_VOID (fsig->ret));
+ CHECK_CFG_EXCEPTION;
+ } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
+ mono_method_check_inlining (cfg, cmethod) &&
+ !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE)) {
+ int costs;
+
+ if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, FALSE, &bblock))) {
+ cfg->real_offset += 5;
+
+ *inline_costs += costs - 5;
+ *ref_bblock = bblock;
+ } else {
+ INLINE_FAILURE ("inline failure");
+ // FIXME-VT: Clean this up
+ if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
+ GSHAREDVT_FAILURE(*ip);
+ mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
+ }
+ } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
+ MonoInst *addr;
+
+ addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
+ mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
+ } else if (context_used &&
+ ((!mono_method_is_generic_sharable_full (cmethod, TRUE, FALSE, FALSE) ||
+ !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
+ MonoInst *cmethod_addr;
+
+ /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
+
+ cmethod_addr = emit_get_rgctx_method (cfg, context_used,
+ cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
+
+ mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
+ } else {
+ INLINE_FAILURE ("ctor call");
+ ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
+ callvirt_this_arg, NULL, vtable_arg);
+ }
+ exception_exit:
+ return;
+}
+
/*
* mono_method_to_ir:
*
*/
int
mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
- MonoInst *return_var, GList *dont_inline, MonoInst **inline_args,
+ MonoInst *return_var, MonoInst **inline_args,
guint inline_offset, gboolean is_virtual_call)
{
MonoError error;
gboolean dont_verify, dont_verify_stloc, readonly = FALSE;
int context_used;
gboolean init_locals, seq_points, skip_dead_blocks;
- gboolean disable_inline, sym_seq_points = FALSE;
+ gboolean sym_seq_points = FALSE;
MonoInst *cached_tls_addr = NULL;
MonoDebugMethodInfo *minfo;
MonoBitSet *seq_point_locs = NULL;
MonoBitSet *seq_point_set_locs = NULL;
- disable_inline = is_jit_optimizer_disabled (method);
+ cfg->disable_inline = is_jit_optimizer_disabled (method);
/* serialization and xdomain stuff may need access to private fields and methods */
dont_verify = method->klass->image->assembly->corlib_internal? TRUE: FALSE;
param_types [n + sig->hasthis] = sig->params [n];
cfg->arg_types = param_types;
- dont_inline = g_list_prepend (dont_inline, method);
+ cfg->dont_inline = g_list_prepend (cfg->dont_inline, method);
if (cfg->method == method) {
if (cfg->prof_options & MONO_PROFILE_INS_COVERAGE)
}
if (!mono_method_can_access_method (method_definition, target_method) &&
!mono_method_can_access_method (method, cil_method))
- METHOD_ACCESS_FAILURE;
+ METHOD_ACCESS_FAILURE (method, cil_method);
}
if (mono_security_core_clr_enabled ())
/* Inlining */
if (cmethod && (cfg->opt & MONO_OPT_INLINE) &&
(!virtual || !(cmethod->flags & METHOD_ATTRIBUTE_VIRTUAL) || MONO_METHOD_IS_FINAL (cmethod)) &&
- !disable_inline && mono_method_check_inlining (cfg, cmethod) &&
- !g_list_find (dont_inline, cmethod)) {
+ mono_method_check_inlining (cfg, cmethod)) {
int costs;
gboolean always = FALSE;
always = TRUE;
}
- costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, dont_inline, always);
+ costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, always, &bblock);
if (costs) {
cfg->real_offset += 5;
- bblock = cfg->cbb;
if (!MONO_TYPE_IS_VOID (fsig->ret)) {
/* *sp is already set by inline_method */
}
*/
- if (cmethod->klass->valuetype && mono_class_generic_sharing_enabled (cmethod->klass) &&
- mono_method_is_generic_sharable (cmethod, TRUE)) {
- if (cmethod->is_inflated && mono_method_get_context (cmethod)->method_inst) {
- mono_class_vtable (cfg->domain, cmethod->klass);
- CHECK_TYPELOAD (cmethod->klass);
-
- vtable_arg = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_METHOD_RGCTX);
- } else {
- if (context_used) {
- vtable_arg = emit_get_rgctx_klass (cfg, context_used,
- cmethod->klass, MONO_RGCTX_INFO_VTABLE);
- } else {
- MonoVTable *vtable = mono_class_vtable (cfg->domain, cmethod->klass);
-
- CHECK_TYPELOAD (cmethod->klass);
- EMIT_NEW_VTABLECONST (cfg, vtable_arg, vtable);
- }
- }
- }
-
n = fsig->param_count;
CHECK_STACK (n);
((n < 2) || (!fsig->params [1]->byref && fsig->params [1]->type == MONO_TYPE_STRING))) {
MonoInst *iargs [3];
- g_assert (!vtable_arg);
-
sp -= n;
EMIT_NEW_ICONST (cfg, iargs [0], cmethod->klass->type_token);
iargs [0] = NULL;
if (mini_class_is_system_array (cmethod->klass)) {
- g_assert (!vtable_arg);
-
*sp = emit_get_rgctx_method (cfg, context_used,
cmethod, MONO_RGCTX_INFO_METHOD);
/* now call the string ctor */
alloc = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, NULL, NULL, NULL);
} else {
- MonoInst* callvirt_this_arg = NULL;
-
if (cmethod->klass->valuetype) {
iargs [0] = mono_compile_create_var (cfg, &cmethod->klass->byval_arg, OP_LOCAL);
emit_init_rvar (cfg, iargs [0]->dreg, &cmethod->klass->byval_arg);
MONO_EMIT_NEW_UNALU (cfg, OP_NOT_NULL, -1, alloc->dreg);
/* Now call the actual ctor */
- /* Avoid virtual calls to ctors if possible */
- if (mono_class_is_marshalbyref (cmethod->klass))
- callvirt_this_arg = sp [0];
-
-
- if (cmethod && (cfg->opt & MONO_OPT_INTRINS) && (ins = mini_emit_inst_for_ctor (cfg, cmethod, fsig, sp))) {
- if (!MONO_TYPE_IS_VOID (fsig->ret)) {
- type_to_eval_stack_type ((cfg), fsig->ret, ins);
- *sp = ins;
- sp++;
- }
-
- CHECK_CFG_EXCEPTION;
- } else if ((cfg->opt & MONO_OPT_INLINE) && cmethod && !context_used && !vtable_arg &&
- !disable_inline && mono_method_check_inlining (cfg, cmethod) &&
- !mono_class_is_subclass_of (cmethod->klass, mono_defaults.exception_class, FALSE) &&
- !g_list_find (dont_inline, cmethod)) {
- int costs;
-
- if ((costs = inline_method (cfg, cmethod, fsig, sp, ip, cfg->real_offset, dont_inline, FALSE))) {
- cfg->real_offset += 5;
- bblock = cfg->cbb;
-
- inline_costs += costs - 5;
- } else {
- INLINE_FAILURE ("inline failure");
- // FIXME-VT: Clean this up
- if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig))
- GSHAREDVT_FAILURE(*ip);
- mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp, callvirt_this_arg, NULL, NULL);
- }
- } else if (cfg->gsharedvt && mini_is_gsharedvt_signature (cfg, fsig)) {
- MonoInst *addr;
-
- addr = emit_get_rgctx_gsharedvt_call (cfg, context_used, fsig, cmethod, MONO_RGCTX_INFO_METHOD_GSHAREDVT_OUT_TRAMPOLINE);
- mono_emit_calli (cfg, fsig, sp, addr, NULL, vtable_arg);
- } else if (context_used &&
- ((!mono_method_is_generic_sharable (cmethod, TRUE) ||
- !mono_class_generic_sharing_enabled (cmethod->klass)) || cfg->gsharedvt)) {
- MonoInst *cmethod_addr;
-
- /* Generic calls made out of gsharedvt methods cannot be patched, so use an indirect call */
-
- cmethod_addr = emit_get_rgctx_method (cfg, context_used,
- cmethod, MONO_RGCTX_INFO_GENERIC_METHOD_CODE);
-
- mono_emit_calli (cfg, fsig, sp, cmethod_addr, NULL, vtable_arg);
- } else {
- INLINE_FAILURE ("ctor call");
- ins = mono_emit_method_call_full (cfg, cmethod, fsig, FALSE, sp,
- callvirt_this_arg, NULL, vtable_arg);
- }
+ handle_ctor_call (cfg, cmethod, fsig, context_used, sp, ip, &bblock, &inline_costs);
+ CHECK_CFG_EXCEPTION;
}
if (alloc == NULL) {
EMIT_NEW_TEMPLOAD (cfg, ins, iargs [0]->inst_c0);
type_to_eval_stack_type (cfg, &ins->klass->byval_arg, ins);
*sp++= ins;
- }
- else
+ } else {
*sp++ = alloc;
+ }
ip += 5;
inline_costs += 5;
context_used = mini_class_check_context_used (cfg, klass);
if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
- MonoInst *args [3];
-
- /* obj */
- args [0] = *sp;
-
- /* klass */
- EMIT_NEW_CLASSCONST (cfg, args [1], klass);
-
- /* inline cache*/
- if (cfg->compile_aot)
- EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, NULL);
- else
- EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
-
- /*The wrapper doesn't inline well so the bloat of inlining doesn't pay off.*/
-
- *sp++ = emit_castclass_with_cache (cfg, klass, args, &bblock);
+ *sp = emit_castclass_with_cache_nonshared (cfg, sp [0], klass, &bblock);
+ sp ++;
ip += 5;
inline_costs += 2;
} else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
save_cast_details (cfg, klass, sp [0]->dreg, TRUE, &bblock);
costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
- iargs, ip, cfg->real_offset, dont_inline, TRUE);
+ iargs, ip, cfg->real_offset, TRUE, &bblock);
reset_cast_details (cfg);
CHECK_CFG_EXCEPTION;
g_assert (costs > 0);
ip += 5;
cfg->real_offset += 5;
- bblock = cfg->cbb;
*sp++ = iargs [0];
iargs [0] = sp [0];
costs = inline_method (cfg, mono_isinst, mono_method_signature (mono_isinst),
- iargs, ip, cfg->real_offset, dont_inline, TRUE);
+ iargs, ip, cfg->real_offset, TRUE, &bblock);
CHECK_CFG_EXCEPTION;
g_assert (costs > 0);
ip += 5;
cfg->real_offset += 5;
- bblock = cfg->cbb;
*sp++= iargs [0];
if (generic_class_is_reference_type (cfg, klass)) {
/* CASTCLASS FIXME kill this huge slice of duplicated code*/
if (!context_used && mini_class_has_reference_variant_generic_argument (cfg, klass, context_used)) {
- MonoInst *args [3];
-
- /* obj */
- args [0] = *sp;
-
- /* klass */
- EMIT_NEW_CLASSCONST (cfg, args [1], klass);
-
- /* inline cache*/
- /*FIXME AOT support*/
- if (cfg->compile_aot)
- EMIT_NEW_AOTCONST (cfg, args [2], MONO_PATCH_INFO_CASTCLASS_CACHE, NULL);
- else
- EMIT_NEW_PCONST (cfg, args [2], mono_domain_alloc0 (cfg->domain, sizeof (gpointer)));
-
- /* The wrapper doesn't inline well so the bloat of inlining doesn't pay off. */
- *sp++ = emit_castclass_with_cache (cfg, klass, args, &bblock);
+ *sp = emit_castclass_with_cache_nonshared (cfg, sp [0], klass, &bblock);
+ sp ++;
ip += 5;
inline_costs += 2;
} else if (!context_used && (mono_class_is_marshalbyref (klass) || klass->flags & TYPE_ATTRIBUTE_INTERFACE)) {
iargs [0] = sp [0];
costs = inline_method (cfg, mono_castclass, mono_method_signature (mono_castclass),
- iargs, ip, cfg->real_offset, dont_inline, TRUE);
+ iargs, ip, cfg->real_offset, TRUE, &bblock);
CHECK_CFG_EXCEPTION;
g_assert (costs > 0);
ip += 5;
cfg->real_offset += 5;
- bblock = cfg->cbb;
*sp++ = iargs [0];
inline_costs += costs;
if (!field)
LOAD_ERROR;
if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_field (method, field))
- FIELD_ACCESS_FAILURE;
+ FIELD_ACCESS_FAILURE (method, field);
mono_class_init (klass);
if (is_instance && *ip != CEE_LDFLDA && is_magic_tls_access (field))
if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
costs = inline_method (cfg, stfld_wrapper, mono_method_signature (stfld_wrapper),
- iargs, ip, cfg->real_offset, dont_inline, TRUE);
+ iargs, ip, cfg->real_offset, TRUE, &bblock);
CHECK_CFG_EXCEPTION;
g_assert (costs > 0);
cfg->real_offset += 5;
- bblock = cfg->cbb;
inline_costs += costs;
} else {
EMIT_NEW_ICONST (cfg, iargs [3], klass->valuetype ? field->offset - sizeof (MonoObject) : field->offset);
if (cfg->opt & MONO_OPT_INLINE || cfg->compile_aot) {
costs = inline_method (cfg, wrapper, mono_method_signature (wrapper),
- iargs, ip, cfg->real_offset, dont_inline, TRUE);
+ iargs, ip, cfg->real_offset, TRUE, &bblock);
CHECK_CFG_EXCEPTION;
- bblock = cfg->cbb;
g_assert (costs > 0);
cfg->real_offset += 5;
cil_method = cmethod;
if (!dont_verify && !cfg->skip_visibility && !mono_method_can_access_method (method, cmethod))
- METHOD_ACCESS_FAILURE;
+ METHOD_ACCESS_FAILURE (method, cil_method);
if (mono_security_cas_enabled ()) {
if (check_linkdemand (cfg, method, cmethod))
* stack overflows which is different behavior than the
* non-inlined case, thus disable inlining in this case.
*/
- goto inline_failure;
+ INLINE_FAILURE("localloc");
MONO_INST_NEW (cfg, ins, OP_LOCALLOC);
ins->dreg = alloc_preg (cfg);
}
}
- g_slist_free (class_inits);
- dont_inline = g_list_remove (dont_inline, method);
-
if (inline_costs < 0) {
char *mname;
mono_cfg_set_exception (cfg, MONO_EXCEPTION_INVALID_PROGRAM);
cfg->exception_message = g_strdup_printf ("Method %s is too complex.", mname);
g_free (mname);
- cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
- mono_basic_block_free (original_bb);
- return -1;
}
if ((cfg->verbose_level > 2) && (cfg->method == method))
mono_print_code (cfg, "AFTER METHOD-TO-IR");
- cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
- mono_basic_block_free (original_bb);
- return inline_costs;
+ goto cleanup;
exception_exit:
g_assert (cfg->exception_type != MONO_EXCEPTION_NONE);
goto cleanup;
- inline_failure:
- goto cleanup;
-
- load_error:
- mono_cfg_set_exception (cfg, MONO_EXCEPTION_TYPE_LOAD);
- goto cleanup;
-
unverified:
set_exception_type_from_invalid_il (cfg, method, ip);
goto cleanup;
cleanup:
g_slist_free (class_inits);
mono_basic_block_free (original_bb);
- dont_inline = g_list_remove (dont_inline, method);
+ cfg->dont_inline = g_list_remove (cfg->dont_inline, method);
cfg->headers_to_free = g_slist_prepend_mempool (cfg->mempool, cfg->headers_to_free, header);
- return -1;
+ if (cfg->exception_type)
+ return -1;
+ else
+ return inline_costs;
}
static int
cfg->lmf_ir_mono_lmf = TRUE;
#endif
}
-
-#ifndef HOST_WIN32
- cfg->arch_eh_jit_info = 1;
-#endif
}
static void
}
}
-/*MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD*/
gpointer
mono_arch_install_handler_block_guard (MonoJitInfo *ji, MonoJitExceptionInfo *clause, MonoContext *ctx, gpointer new_value)
{
- int offset;
gpointer *sp, old_value;
char *bp;
- const unsigned char *handler;
-
- /*Decode the first instruction to figure out where did we store the spvar*/
- /*Our jit MUST generate the following:
- mov %rsp, ?(%rbp)
-
- Which is encoded as: REX.W 0x89 mod_rm
- mod_rm (rsp, rbp, imm) which can be: (imm will never be zero)
- mod (reg + imm8): 01 reg(rsp): 100 rm(rbp): 101 -> 01100101 (0x65)
- mod (reg + imm32): 10 reg(rsp): 100 rm(rbp): 101 -> 10100101 (0xA5)
-
- FIXME can we generate frameless methods on this case?
-
- */
- handler = clause->handler_start;
-
- /*REX.W*/
- if (*handler != 0x48)
- return NULL;
- ++handler;
-
- /*mov r, r/m */
- if (*handler != 0x89)
- return NULL;
- ++handler;
-
- if (*handler == 0x65)
- offset = *(signed char*)(handler + 1);
- else if (*handler == 0xA5)
- offset = *(int*)(handler + 1);
- else
- return NULL;
/*Load the spvar*/
bp = MONO_CONTEXT_GET_BP (ctx);
- sp = *(gpointer*)(bp + offset);
+ sp = *(gpointer*)(bp + clause->exvar_offset);
old_value = *sp;
if (old_value < ji->code_start || (char*)old_value > ((char*)ji->code_start + ji->code_size))
#endif /* !HOST_WIN32 && !__native_client__ */
-#if defined (__APPLE__)
-
-#define MONO_ARCH_NOMAP32BIT
-
-#elif defined (__NetBSD__)
-
-#define REG_RAX 14
-#define REG_RCX 3
-#define REG_RDX 2
-#define REG_RBX 13
-#define REG_RSP 24
-#define REG_RBP 12
-#define REG_RSI 1
-#define REG_RDI 0
-#define REG_R8 4
-#define REG_R9 5
-#define REG_R10 6
-#define REG_R11 7
-#define REG_R12 8
-#define REG_R13 9
-#define REG_R14 10
-#define REG_R15 11
-#define REG_RIP 21
-
-#define MONO_ARCH_NOMAP32BIT
-
-#elif defined (__OpenBSD__)
-
-#define MONO_ARCH_NOMAP32BIT
-
-#elif defined (__DragonFly__)
-
-#define MONO_ARCH_NOMAP32BIT
-
-#elif defined (__FreeBSD__) || defined(__FreeBSD_kernel__)
-
-#define REG_RAX 7
-#define REG_RCX 4
-#define REG_RDX 3
-#define REG_RBX 8
-#define REG_RSP 23
-#define REG_RBP 9
-#define REG_RSI 2
-#define REG_RDI 1
-#define REG_R8 5
-#define REG_R9 6
-#define REG_R10 10
-#define REG_R11 11
-#define REG_R12 12
-#define REG_R13 13
-#define REG_R14 14
-#define REG_R15 15
-#define REG_RIP 20
-
-/*
- * FreeBSD does not have MAP_32BIT, so code allocated by the code manager might not have a
- * 32 bit address.
- */
-#define MONO_ARCH_NOMAP32BIT
-
-#endif /* __FreeBSD__ */
+#if !defined(__linux__)
+#define MONO_ARCH_NOMAP32BIT 1
+#endif
#ifdef HOST_WIN32
#define MONO_AMD64_ARG_REG1 AMD64_RCX
#define MONO_ARCH_EMULATE_FREM 1
#define MONO_ARCH_HAVE_IS_INT_OVERFLOW 1
-#define MONO_ARCH_ENABLE_REGALLOC_IN_EH_BLOCKS 1
#define MONO_ARCH_ENABLE_MONO_LMF_VAR 1
#define MONO_ARCH_HAVE_INVALIDATE_METHOD 1
#define MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE 1
#define MONO_ARCH_ENABLE_GLOBAL_RA 1
#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
#define MONO_ARCH_HAVE_LIVERANGE_OPS 1
-#define MONO_ARCH_HAVE_XP_UNWIND 1
#define MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX 1
#define MONO_ARCH_MONITOR_OBJECT_REG MONO_AMD64_ARG_REG1
#define MONO_ARCH_HAVE_GET_TRAMPOLINES 1
#define MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE 1
#define MONO_ARCH_LLVM_SUPPORTED 1
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
#define MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD 1
#define MONO_ARCH_HAVE_CARD_TABLE_WBARRIER 1
#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
return mono_arm_get_exception_trampolines (aot);
}
+gpointer
+mono_arch_install_handler_block_guard (MonoJitInfo *ji, MonoJitExceptionInfo *clause, MonoContext *ctx, gpointer new_value)
+{
+ gpointer *lr_loc;
+ char *old_value;
+ char *bp;
+
+ /*Load the spvar*/
+ bp = MONO_CONTEXT_GET_BP (ctx);
+ lr_loc = (gpointer*)(bp + clause->exvar_offset);
+
+ old_value = *lr_loc;
+ if ((char*)old_value < (char*)ji->code_start || (char*)old_value > ((char*)ji->code_start + ji->code_size))
+ return old_value;
+
+ *lr_loc = new_value;
+
+ return old_value;
+}
+
#if defined(MONO_ARCH_SOFT_DEBUG_SUPPORTED)
/*
* mono_arch_set_breakpoint:
#define MONO_ARCH_NEED_DIV_CHECK 1
#define MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE
-#define MONO_ARCH_HAVE_XP_UNWIND 1
#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
#define ARM_NUM_REG_ARGS (ARM_LAST_ARG_REG-ARM_FIRST_ARG_REG+1)
#define MONO_ARCH_AOT_SUPPORTED 1
#define MONO_ARCH_LLVM_SUPPORTED 1
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
#define MONO_ARCH_GSHARED_SUPPORTED 1
#define MONO_ARCH_DYN_CALL_SUPPORTED 1
#define MONO_ARCH_GC_MAPS_SUPPORTED 1
#define MONO_ARCH_HAVE_SETUP_ASYNC_CALLBACK 1
#define MONO_ARCH_HAVE_CONTEXT_SET_INT_REG 1
+#define MONO_ARCH_HAVE_HANDLER_BLOCK_GUARD 1
#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
#define MONO_ARCH_GSHAREDVT_SUPPORTED 1
#define MONO_ARCH_HAVE_GENERAL_RGCTX_LAZY_FETCH_TRAMPOLINE 1
if (frame->ji)
method = jinfo_get_method (frame->ji);
- if (method) {
- gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, mono_domain_get ());
+ if (method && frame->domain) {
+ gchar *location = mono_debug_print_stack_frame (method, frame->native_offset, frame->domain);
g_string_append_printf (p, " %s\n", location);
g_free (location);
} else
#define MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE 1
#define MONO_ARCH_HAVE_SAVE_UNWIND_INFO 1
#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
#endif /* __MONO_MINI_IA64_H__ */
#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
#define MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX 1
-#define MONO_ARCH_HAVE_XP_UNWIND 1
#define MONO_ARCH_HAVE_CREATE_DELEGATE_TRAMPOLINE 1
#define MONO_ARCH_HAVE_SETUP_RESUME_FROM_SIGNAL_HANDLER_CTX 1
#define MONO_ARCH_GSHARED_SUPPORTED 1
#define MONO_ARCH_NEED_DIV_CHECK 1
#define MONO_ARCH_NO_IOV_CHECK 1
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
-
#define MIPS_NUM_REG_ARGS (MIPS_LAST_ARG_REG-MIPS_FIRST_ARG_REG+1)
#define MIPS_NUM_REG_FPARGS (MIPS_LAST_FPARG_REG-MIPS_FIRST_FPARG_REG+1)
#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
#define MONO_ARCH_HAVE_FULL_AOT_TRAMPOLINES 1
-#define MONO_ARCH_HAVE_XP_UNWIND 1
#define MONO_ARCH_GSHARED_SUPPORTED 1
#if !defined(MONO_CROSS_COMPILE) && !defined(TARGET_PS3)
#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
#endif
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
#define MONO_ARCH_HAVE_OP_TAIL_CALL 1
#define PPC_NUM_REG_ARGS (PPC_LAST_ARG_REG-PPC_FIRST_ARG_REG+1)
#define MONO_ARCH_IMT_REG s390_r9
#define MONO_ARCH_VTABLE_REG MONO_ARCH_IMT_REG
#define MONO_ARCH_RGCTX_REG MONO_ARCH_IMT_REG
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
-#define MONO_ARCH_HAVE_XP_UNWIND 1
#define MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX 1
#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
#define MONO_ARCH_HAVE_CONTEXT_SET_INT_REG 1
#define MONO_ARCH_NO_EMULATE_LONG_SHIFT_OPS
#endif
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
-
#ifndef __GNUC__
/* assume Sun compiler if not GCC */
static void * __builtin_return_address(int depth)
* We use one vtable trampoline per vtable slot index, so we need only the vtable,
* the other two can be computed from the vtable + the slot index.
*/
-#ifndef MONO_ARCH_THIS_AS_FIRST_ARG
- /* All architectures should support this */
- g_assert_not_reached ();
-#endif
/*
* Obtain the vtable from the 'this' arg.
cfg->vret_addr = mono_compile_create_var (cfg, &mono_defaults.int_class->byval_arg, OP_ARG);
}
-#ifdef MONO_X86_NO_PUSHES
- cfg->arch.no_pushes = TRUE;
-#endif
-
if (cfg->method->save_lmf) {
cfg->create_lmf_var = TRUE;
cfg->lmf_ir = TRUE;
if (cfg->compile_aot) {
sig_reg = mono_alloc_ireg (cfg);
MONO_EMIT_NEW_SIGNATURECONST (cfg, sig_reg, tmp_sig);
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, cinfo->sig_cookie.offset, sig_reg);
- } else {
- MONO_EMIT_NEW_UNALU (cfg, OP_X86_PUSH, -1, sig_reg);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, cinfo->sig_cookie.offset, sig_reg);
} else {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, X86_ESP, cinfo->sig_cookie.offset, tmp_sig);
- } else {
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_X86_PUSH_IMM, -1, -1, tmp_sig);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE_IMM (cfg, OP_STORE_MEMBASE_IMM, X86_ESP, cinfo->sig_cookie.offset, tmp_sig);
}
}
MonoInst *def;
/* Needs checking if the feature will be enabled again */
- g_assert (!cfg->arch.no_pushes);
+ g_assert_not_reached ();
/* On x86, the offsets are from the sp value before the start of the call sequence */
if (t == NULL)
if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG))
sentinelpos = sig->sentinelpos + (sig->hasthis ? 1 : 0);
- if (cinfo->need_stack_align && !cfg->arch.no_pushes) {
- MONO_INST_NEW (cfg, arg, OP_SUB_IMM);
- arg->dreg = X86_ESP;
- arg->sreg1 = X86_ESP;
- arg->inst_imm = cinfo->stack_align_amount;
- MONO_ADD_INS (cfg->cbb, arg);
- for (i = 0; i < cinfo->stack_align_amount; i += sizeof (mgreg_t)) {
- sp_offset += 4;
-
- emit_gc_param_slot_def (cfg, sp_offset, NULL);
- }
- }
-
if (sig_ret && MONO_TYPE_ISSTRUCT (sig_ret)) {
if (cinfo->ret.storage == ArgValuetypeInReg) {
/*
/* Handle the case where there are no implicit arguments */
if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (n == sentinelpos)) {
emit_sig_cookie (cfg, call, cinfo);
- sp_offset = (cfg->arch.no_pushes) ? cinfo->sig_cookie.offset : (sp_offset + 4);
+ sp_offset = cinfo->sig_cookie.offset;
emit_gc_param_slot_def (cfg, sp_offset, NULL);
}
if (cinfo->vtype_retaddr && cinfo->vret_arg_index == 1 && i == 0) {
MonoInst *vtarg;
+
/* Push the vret arg before the first argument */
- if (cfg->arch.no_pushes) {
- MONO_INST_NEW (cfg, vtarg, OP_STORE_MEMBASE_REG);
- vtarg->type = STACK_MP;
- vtarg->inst_destbasereg = X86_ESP;
- vtarg->sreg1 = call->vret_var->dreg;
- vtarg->inst_offset = cinfo->ret.offset;
- MONO_ADD_INS (cfg->cbb, vtarg);
- sp_offset = cinfo->ret.offset;
- } else {
- MONO_INST_NEW (cfg, vtarg, OP_X86_PUSH);
- vtarg->type = STACK_MP;
- vtarg->sreg1 = call->vret_var->dreg;
- MONO_ADD_INS (cfg->cbb, vtarg);
- sp_offset += 4;
- }
- emit_gc_param_slot_def (cfg, sp_offset, NULL);
+ MONO_INST_NEW (cfg, vtarg, OP_STORE_MEMBASE_REG);
+ vtarg->type = STACK_MP;
+ vtarg->inst_destbasereg = X86_ESP;
+ vtarg->sreg1 = call->vret_var->dreg;
+ vtarg->inst_offset = cinfo->ret.offset;
+ MONO_ADD_INS (cfg->cbb, vtarg);
+ emit_gc_param_slot_def (cfg, cinfo->ret.offset, NULL);
}
if (i >= sig->hasthis)
MONO_ADD_INS (cfg->cbb, arg);
if (ainfo->storage != ArgValuetypeInReg) {
- sp_offset = (cfg->arch.no_pushes) ? ainfo->offset : (sp_offset + size);
- emit_gc_param_slot_def (cfg, sp_offset, orig_type);
+ emit_gc_param_slot_def (cfg, ainfo->offset, orig_type);
}
}
} else {
case ArgOnStack:
if (!t->byref) {
if (t->type == MONO_TYPE_R4) {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
- } else {
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SUB_IMM, X86_ESP, X86_ESP, 4);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, X86_ESP, 0, in->dreg);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER4_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
argsize = 4;
} else if (t->type == MONO_TYPE_R8) {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
- } else {
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SUB_IMM, X86_ESP, X86_ESP, 8);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, X86_ESP, 0, in->dreg);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORER8_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
argsize = 8;
} else if (t->type == MONO_TYPE_I8 || t->type == MONO_TYPE_U8) {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset + 4, in->dreg + 2);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg + 1);
- } else {
- MONO_EMIT_NEW_UNALU (cfg, OP_X86_PUSH, -1, in->dreg + 2);
- MONO_EMIT_NEW_UNALU (cfg, OP_X86_PUSH, -1, in->dreg + 1);
- sp_offset += 4;
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset + 4, in->dreg + 2);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg + 1);
argsize = 4;
} else {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
- } else {
- arg->opcode = OP_X86_PUSH;
- MONO_ADD_INS (cfg->cbb, arg);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
argsize = 4;
}
} else {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
- } else {
- arg->opcode = OP_X86_PUSH;
- MONO_ADD_INS (cfg->cbb, arg);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, in->dreg);
argsize = 4;
}
break;
g_assert_not_reached ();
}
- sp_offset = (cfg->arch.no_pushes) ? ainfo->offset : (sp_offset + argsize);
-
if (cfg->compute_gc_maps) {
if (argsize == 4) {
/* FIXME: The == STACK_OBJ check might be fragile ? */
/* this */
if (call->need_unbox_trampoline)
/* The unbox trampoline transforms this into a managed pointer */
- emit_gc_param_slot_def (cfg, sp_offset, &mono_defaults.int_class->this_arg);
+ emit_gc_param_slot_def (cfg, ainfo->offset, &mono_defaults.int_class->this_arg);
else
- emit_gc_param_slot_def (cfg, sp_offset, &mono_defaults.object_class->byval_arg);
+ emit_gc_param_slot_def (cfg, ainfo->offset, &mono_defaults.object_class->byval_arg);
} else {
- emit_gc_param_slot_def (cfg, sp_offset, orig_type);
+ emit_gc_param_slot_def (cfg, ainfo->offset, orig_type);
}
} else {
/* i8/r8 */
- for (j = 0; j < argsize; j += 4) {
- if (cfg->arch.no_pushes)
- emit_gc_param_slot_def (cfg, sp_offset + j, NULL);
- else
- emit_gc_param_slot_def (cfg, sp_offset - j, NULL);
- }
+ for (j = 0; j < argsize; j += 4)
+ emit_gc_param_slot_def (cfg, ainfo->offset + j, NULL);
}
}
}
if (!sig->pinvoke && (sig->call_convention == MONO_CALL_VARARG) && (i == sentinelpos)) {
/* Emit the signature cookie just before the implicit arguments */
emit_sig_cookie (cfg, call, cinfo);
- sp_offset = (cfg->arch.no_pushes) ? cinfo->sig_cookie.offset : (sp_offset + 4);
- emit_gc_param_slot_def (cfg, sp_offset, NULL);
+ emit_gc_param_slot_def (cfg, cinfo->sig_cookie.offset, NULL);
}
}
mono_call_inst_add_outarg_reg (cfg, call, vtarg->dreg, cinfo->ret.reg, FALSE);
} else if (cinfo->vtype_retaddr && cinfo->vret_arg_index == 0) {
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, cinfo->ret.offset, call->vret_var->dreg);
- sp_offset = cinfo->ret.offset;
- } else {
- MonoInst *vtarg;
- MONO_INST_NEW (cfg, vtarg, OP_X86_PUSH);
- vtarg->type = STACK_MP;
- vtarg->sreg1 = call->vret_var->dreg;
- MONO_ADD_INS (cfg->cbb, vtarg);
- sp_offset += 4;
- }
- emit_gc_param_slot_def (cfg, sp_offset, NULL);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, cinfo->ret.offset, call->vret_var->dreg);
+ emit_gc_param_slot_def (cfg, cinfo->ret.offset, NULL);
}
-
- /* if the function returns a struct on stack, the called method already does a ret $0x4 */
- if (!cfg->arch.no_pushes)
- cinfo->stack_usage -= cinfo->callee_stack_pop;
}
call->stack_usage = cinfo->stack_usage;
call->stack_align_amount = cinfo->stack_align_amount;
- if (!cfg->arch.no_pushes)
- cfg->arch.param_area_size = MAX (cfg->arch.param_area_size, sp_offset);
}
void
{
MonoCallInst *call = (MonoCallInst*)ins->inst_p0;
ArgInfo *ainfo = ins->inst_p1;
- MonoInst *arg;
int size = ins->backend.size;
if (ainfo->storage == ArgValuetypeInReg) {
else {
if (cfg->gsharedvt && mini_is_gsharedvt_klass (cfg, ins->klass)) {
/* Pass by addr */
- if (cfg->arch.no_pushes) {
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, src->dreg);
- } else {
- MONO_INST_NEW (cfg, arg, OP_X86_PUSH);
- arg->sreg1 = src->dreg;
- MONO_ADD_INS (cfg->cbb, arg);
- }
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, src->dreg);
} else if (size <= 4) {
- if (cfg->arch.no_pushes) {
- int dreg = mono_alloc_ireg (cfg);
- MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, 0);
- MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, dreg);
- } else {
- MONO_INST_NEW (cfg, arg, OP_X86_PUSH_MEMBASE);
- arg->sreg1 = src->dreg;
- MONO_ADD_INS (cfg->cbb, arg);
- }
+ int dreg = mono_alloc_ireg (cfg);
+ MONO_EMIT_NEW_LOAD_MEMBASE (cfg, dreg, src->dreg, 0);
+ MONO_EMIT_NEW_STORE_MEMBASE (cfg, OP_STORE_MEMBASE_REG, X86_ESP, ainfo->offset, dreg);
} else if (size <= 20) {
- if (cfg->arch.no_pushes) {
- mini_emit_memcpy (cfg, X86_ESP, ainfo->offset, src->dreg, 0, size, 4);
- } else {
- MONO_EMIT_NEW_BIALU_IMM (cfg, OP_SUB_IMM, X86_ESP, X86_ESP, ALIGN_TO (size, 4));
- mini_emit_memcpy (cfg, X86_ESP, 0, src->dreg, 0, size, 4);
- }
+ mini_emit_memcpy (cfg, X86_ESP, ainfo->offset, src->dreg, 0, size, 4);
} else {
- if (cfg->arch.no_pushes) {
- // FIXME: Code growth
- mini_emit_memcpy (cfg, X86_ESP, ainfo->offset, src->dreg, 0, size, 4);
- } else {
- MONO_INST_NEW (cfg, arg, OP_X86_PUSH_OBJ);
- arg->inst_basereg = src->dreg;
- arg->inst_offset = 0;
- arg->inst_imm = size;
-
- MONO_ADD_INS (cfg->cbb, arg);
- }
+ // FIXME: Code growth
+ mini_emit_memcpy (cfg, X86_ESP, ainfo->offset, src->dreg, 0, size, 4);
}
}
}
x86_push_reg (code, X86_EDI);
x86_mov_reg_imm (code, X86_ECX, (0x1000 >> 2));
x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
- if (cfg->param_area && cfg->arch.no_pushes)
+ if (cfg->param_area)
x86_lea_membase (code, X86_EDI, X86_ESP, 12 + ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
else
x86_lea_membase (code, X86_EDI, X86_ESP, 12);
x86_mov_reg_reg (code, X86_ECX, sreg, 4);
x86_alu_reg_reg (code, X86_XOR, X86_EAX, X86_EAX);
- if (cfg->param_area && cfg->arch.no_pushes)
+ if (cfg->param_area)
x86_lea_membase (code, X86_EDI, X86_ESP, offset + ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
else
x86_lea_membase (code, X86_EDI, X86_ESP, offset);
}
ins->flags |= MONO_INST_GC_CALLSITE;
ins->backend.pc_offset = code - cfg->native_code;
- if (call->stack_usage && !CALLCONV_IS_STDCALL (call->signature) && !cfg->arch.no_pushes) {
- /* a pop is one byte, while an add reg, imm is 3. So if there are 4 or 8
- * bytes to pop, we want to use pops. GCC does this (note it won't happen
- * for P4 or i686 because gcc will avoid using pop push at all. But we aren't
- * smart enough to do that optimization yet
- *
- * It turns out that on my P4, doing two pops for 8 bytes on the stack makes
- * mcs botstrap slow down. However, doing 1 pop for 4 bytes creates a small,
- * (most likely from locality benefits). People with other processors should
- * check on theirs to see what happens.
- */
- if (call->stack_usage == 4) {
- /* we want to use registers that won't get used soon, so use
- * ecx, as eax will get allocated first. edx is used by long calls,
- * so we can't use that.
- */
-
- x86_pop_reg (code, X86_ECX);
- } else {
- x86_alu_reg_imm (code, X86_ADD, X86_ESP, call->stack_usage);
- }
- } else if (cinfo->callee_stack_pop && cfg->arch.no_pushes) {
+ if (cinfo->callee_stack_pop) {
/* Have to compensate for the stack space popped by the callee */
x86_alu_reg_imm (code, X86_SUB, X86_ESP, cinfo->callee_stack_pop);
}
code = emit_move_return_value (cfg, ins, code);
break;
}
- case OP_X86_PUSH:
- g_assert (!cfg->arch.no_pushes);
- x86_push_reg (code, ins->sreg1);
- break;
- case OP_X86_PUSH_IMM:
- g_assert (!cfg->arch.no_pushes);
- x86_push_imm (code, ins->inst_imm);
- break;
- case OP_X86_PUSH_MEMBASE:
- g_assert (!cfg->arch.no_pushes);
- x86_push_membase (code, ins->inst_basereg, ins->inst_offset);
- break;
- case OP_X86_PUSH_OBJ:
- g_assert (!cfg->arch.no_pushes);
- x86_alu_reg_imm (code, X86_SUB, X86_ESP, ins->inst_imm);
- x86_push_reg (code, X86_EDI);
- x86_push_reg (code, X86_ESI);
- x86_push_reg (code, X86_ECX);
- if (ins->inst_offset)
- x86_lea_membase (code, X86_ESI, ins->inst_basereg, ins->inst_offset);
- else
- x86_mov_reg_reg (code, X86_ESI, ins->inst_basereg, 4);
- x86_lea_membase (code, X86_EDI, X86_ESP, 12);
- x86_mov_reg_imm (code, X86_ECX, (ins->inst_imm >> 2));
- x86_cld (code);
- x86_prefix (code, X86_REP_PREFIX);
- x86_movsd (code);
- x86_pop_reg (code, X86_ECX);
- x86_pop_reg (code, X86_ESI);
- x86_pop_reg (code, X86_EDI);
- break;
case OP_X86_LEA:
x86_lea_memindex (code, ins->dreg, ins->sreg1, ins->inst_imm, ins->sreg2, ins->backend.shift_amount);
break;
x86_alu_reg_imm (code, X86_AND, ins->sreg1, ~(MONO_ARCH_LOCALLOC_ALIGNMENT - 1));
code = mono_emit_stack_alloc (cfg, code, ins);
x86_mov_reg_reg (code, ins->dreg, X86_ESP, 4);
- if (cfg->param_area && cfg->arch.no_pushes)
- x86_alu_reg_imm (code, X86_ADD, ins->dreg, ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
+ if (cfg->param_area)
+ x86_alu_reg_imm (code, X86_ADD, ins->dreg, ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
break;
case OP_LOCALLOC_IMM: {
guint32 size = ins->inst_imm;
x86_alu_reg_imm (code, X86_SUB, X86_ESP, size);
x86_mov_reg_reg (code, ins->dreg, X86_ESP, 4);
}
- if (cfg->param_area && cfg->arch.no_pushes)
- x86_alu_reg_imm (code, X86_ADD, ins->dreg, ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
+ if (cfg->param_area)
+ x86_alu_reg_imm (code, X86_ADD, ins->dreg, ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
break;
}
case OP_THROW: {
case OP_START_HANDLER: {
MonoInst *spvar = mono_find_spvar_for_region (cfg, bb->region);
x86_mov_membase_reg (code, spvar->inst_basereg, spvar->inst_offset, X86_ESP, 4);
- if (cfg->param_area && cfg->arch.no_pushes) {
+ if (cfg->param_area)
x86_alu_reg_imm (code, X86_SUB, X86_ESP, ALIGN_TO (cfg->param_area, MONO_ARCH_FRAME_ALIGNMENT));
- }
break;
}
case OP_ENDFINALLY: {
cfg->frame_reg = X86_ESP;
}
- if (cfg->arch.no_pushes) {
- cfg->stack_offset += cfg->param_area;
- cfg->stack_offset = ALIGN_TO (cfg->stack_offset, MONO_ARCH_FRAME_ALIGNMENT);
- }
+ cfg->stack_offset += cfg->param_area;
+ cfg->stack_offset = ALIGN_TO (cfg->stack_offset, MONO_ARCH_FRAME_ALIGNMENT);
alloc_size = cfg->stack_offset;
pos = 0;
#endif /* HAVE_WORKING_SIGALTSTACK */
#endif /* !HOST_WIN32 */
-/* #define MONO_X86_NO_PUSHES 1 */
-
#define MONO_ARCH_SUPPORT_TASKLETS 1
#ifndef DISABLE_SIMD
typedef struct {
gboolean need_stack_frame_inited;
gboolean need_stack_frame;
- gboolean no_pushes;
int sp_fp_offset, param_area_size;
} MonoCompileArch;
#define MONO_ARCH_RGCTX_REG MONO_ARCH_IMT_REG
#define MONO_ARCH_HAVE_GENERALIZED_IMT_THUNK 1
#define MONO_ARCH_HAVE_LIVERANGE_OPS 1
-#define MONO_ARCH_HAVE_XP_UNWIND 1
#define MONO_ARCH_HAVE_SIGCTX_TO_MONOCTX 1
#if defined(__linux__) || defined (__APPLE__)
#define MONO_ARCH_MONITOR_OBJECT_REG X86_EAX
#define MONO_ARCH_GSHARED_SUPPORTED 1
#define MONO_ARCH_HAVE_LLVM_IMT_TRAMPOLINE 1
#define MONO_ARCH_LLVM_SUPPORTED 1
-#define MONO_ARCH_THIS_AS_FIRST_ARG 1
#if defined(MONO_ARCH_USE_SIGACTION) || defined(TARGET_WIN32)
#define MONO_ARCH_SOFT_DEBUG_SUPPORTED 1
case MONO_PATCH_INFO_JIT_TLS_ID:
case MONO_PATCH_INFO_MONITOR_ENTER:
case MONO_PATCH_INFO_MONITOR_EXIT:
- case MONO_PATCH_INFO_CASTCLASS_CACHE:
case MONO_PATCH_INFO_GOT_OFFSET:
return (ji->type << 8);
+ case MONO_PATCH_INFO_CASTCLASS_CACHE:
+ return (ji->type << 8) | (ji->data.index);
case MONO_PATCH_INFO_SWITCH:
return (ji->type << 8) | ji->data.table->table_size;
case MONO_PATCH_INFO_GSHAREDVT_METHOD:
return ji1->data.gsharedvt_method->method == ji2->data.gsharedvt_method->method;
case MONO_PATCH_INFO_DELEGATE_TRAMPOLINE:
return ji1->data.del_tramp->klass == ji2->data.del_tramp->klass && ji1->data.del_tramp->method == ji2->data.del_tramp->method && ji1->data.del_tramp->virtual == ji2->data.del_tramp->virtual;
+ case MONO_PATCH_INFO_CASTCLASS_CACHE:
+ return ji1->data.index == ji2->data.index;
default:
if (ji1->data.target != ji2->data.target)
return 0;
GSList *tmp;
MonoMethodHeader *header;
MonoJitInfo *jinfo;
- int num_clauses;
- int generic_info_size, arch_eh_info_size = 0;
- int holes_size = 0, num_holes = 0, cas_size = 0;
+ MonoJitInfoFlags flags = JIT_INFO_NONE;
+ int num_clauses, num_holes = 0;
guint32 stack_size = 0;
g_assert (method_to_compile == cfg->method);
header = cfg->header;
if (cfg->generic_sharing_context)
- generic_info_size = sizeof (MonoGenericJitInfo);
- else
- generic_info_size = 0;
+ flags |= JIT_INFO_HAS_GENERIC_JIT_INFO;
if (cfg->arch_eh_jit_info) {
MonoJitArgumentInfo *arg_info;
stack_size = mono_arch_get_argument_info (cfg->generic_sharing_context, sig, sig->param_count, arg_info);
if (stack_size)
- arch_eh_info_size = sizeof (MonoArchEHJitInfo);
+ flags |= JIT_INFO_HAS_ARCH_EH_INFO;
}
- if (cfg->has_unwind_info_for_epilog && !arch_eh_info_size)
- arch_eh_info_size = sizeof (MonoArchEHJitInfo);
+ if (cfg->has_unwind_info_for_epilog && !(flags & JIT_INFO_HAS_ARCH_EH_INFO))
+ flags |= JIT_INFO_HAS_ARCH_EH_INFO;
if (cfg->try_block_holes) {
for (tmp = cfg->try_block_holes; tmp; tmp = tmp->next) {
++num_holes;
}
if (num_holes)
- holes_size = sizeof (MonoTryBlockHoleTableJitInfo) + num_holes * sizeof (MonoTryBlockHoleJitInfo);
+ flags |= JIT_INFO_HAS_TRY_BLOCK_HOLES;
if (G_UNLIKELY (cfg->verbose_level >= 4))
printf ("Number of try block holes %d\n", num_holes);
}
- if (mono_security_method_has_declsec (cfg->method_to_register)) {
- cas_size = sizeof (MonoMethodCasInfo);
- }
+ if (mono_security_method_has_declsec (cfg->method_to_register))
+ flags |= JIT_INFO_HAS_ARCH_EH_INFO;
if (COMPILE_LLVM (cfg))
num_clauses = cfg->llvm_ex_info_len;
else
num_clauses = header->num_clauses;
- if (cfg->method->dynamic) {
- jinfo = g_malloc0 (MONO_SIZEOF_JIT_INFO + (num_clauses * sizeof (MonoJitExceptionInfo)) +
- generic_info_size + holes_size + arch_eh_info_size + cas_size);
- } else {
- jinfo = mono_domain_alloc0 (cfg->domain, MONO_SIZEOF_JIT_INFO +
- (num_clauses * sizeof (MonoJitExceptionInfo)) +
- generic_info_size + holes_size + arch_eh_info_size + cas_size);
- }
-
- jinfo->d.method = cfg->method_to_register;
- jinfo->code_start = cfg->native_code;
- jinfo->code_size = cfg->code_len;
+ if (cfg->method->dynamic)
+ jinfo = g_malloc0 (mono_jit_info_size (flags, num_clauses, num_holes));
+ else
+ jinfo = mono_domain_alloc0 (cfg->domain, mono_jit_info_size (flags, num_clauses, num_holes));
+ mono_jit_info_init (jinfo, cfg->method_to_register, cfg->native_code, cfg->code_len, flags, num_clauses, num_holes);
jinfo->domain_neutral = (cfg->opt & MONO_OPT_SHARED) != 0;
- jinfo->num_clauses = num_clauses;
if (COMPILE_LLVM (cfg))
jinfo->from_llvm = TRUE;
MonoGenericJitInfo *gi;
GSList *loclist = NULL;
- jinfo->has_generic_jit_info = 1;
-
gi = mono_jit_info_get_generic_jit_info (jinfo);
g_assert (gi);
MonoTryBlockHoleTableJitInfo *table;
int i;
- jinfo->has_try_block_holes = 1;
table = mono_jit_info_get_try_block_hole_table_info (jinfo);
table->num_holes = (guint16)num_holes;
i = 0;
g_assert (i == num_holes);
}
- if (arch_eh_info_size) {
+ if (jinfo->has_arch_eh_info) {
MonoArchEHJitInfo *info;
- jinfo->has_arch_eh_info = 1;
info = mono_jit_info_get_arch_eh_info (jinfo);
info->stack_size = stack_size;
}
- if (cas_size) {
- jinfo->has_cas_info = 1;
- }
-
if (COMPILE_LLVM (cfg)) {
if (num_clauses)
memcpy (&jinfo->clauses [0], &cfg->llvm_ex_info [0], num_clauses * sizeof (MonoJitExceptionInfo));
gboolean run_cctors = (flags & JIT_FLAG_RUN_CCTORS) ? 1 : 0;
gboolean compile_aot = (flags & JIT_FLAG_AOT) ? 1 : 0;
gboolean full_aot = (flags & JIT_FLAG_FULL_AOT) ? 1 : 0;
+#ifdef ENABLE_LLVM
gboolean llvm = (flags & JIT_FLAG_LLVM) ? 1 : 0;
+#endif
InterlockedIncrement (&mono_jit_stats.methods_compiled);
if (mono_profiler_get_events () & MONO_PROFILE_JIT_COMPILATION)
if (cfg->generic_sharing_context) {
method_to_register = method_to_compile;
+ cfg->gshared = TRUE;
} else {
g_assert (method == method_to_compile);
method_to_register = method;
/* SSAPRE is not supported on linear IR */
cfg->opt &= ~MONO_OPT_SSAPRE;
- i = mono_method_to_ir (cfg, method_to_compile, NULL, NULL, NULL, NULL, NULL, 0, FALSE);
+ i = mono_method_to_ir (cfg, method_to_compile, NULL, NULL, NULL, NULL, 0, FALSE);
if (i < 0) {
if (try_generic_shared && cfg->exception_type == MONO_EXCEPTION_GENERIC_SHARING_FAILED) {
domain->runtime_info = NULL;
}
+#ifdef ENABLE_LLVM
+static gboolean
+llvm_init_inner (void)
+{
+ if (!mono_llvm_load (NULL))
+ return FALSE;
+
+ mono_llvm_init ();
+ return TRUE;
+}
+#endif
+
+/*
+ * mini_llvm_init:
+ *
+ * Load and initialize LLVM support.
+ * Return TRUE on success.
+ */
+gboolean
+mini_llvm_init (void)
+{
+#ifdef ENABLE_LLVM
+ static gboolean llvm_inited;
+ static gboolean init_result;
+
+ mono_loader_lock_if_inited ();
+ if (!llvm_inited) {
+ init_result = llvm_init_inner ();
+ llvm_inited = TRUE;
+ }
+ mono_loader_unlock_if_inited ();
+ return init_result;
+#else
+ return FALSE;
+#endif
+}
+
MonoDomain *
mini_init (const char *filename, const char *runtime_version)
{
mono_threads_install_cleanup (mini_thread_cleanup);
#ifdef MONO_ARCH_HAVE_NOTIFY_PENDING_EXC
- // This is experimental code so provide an env var to switch it off
- if (g_getenv ("MONO_DISABLE_PENDING_EXCEPTIONS")) {
- printf ("MONO_DISABLE_PENDING_EXCEPTIONS env var set.\n");
- } else {
- check_for_pending_exc = FALSE;
- mono_threads_install_notify_pending_exc (mono_arch_notify_pending_exc);
- }
+ check_for_pending_exc = FALSE;
+ mono_threads_install_notify_pending_exc ((MonoThreadNotifyPendingExcFunc)mono_arch_notify_pending_exc);
#endif
#define JIT_TRAMPOLINES_WORK
#endif
/* Version number of the AOT file format */
-#define MONO_AOT_FILE_VERSION 102
+#define MONO_AOT_FILE_VERSION 103
//TODO: This is x86/amd64 specific.
#define mono_simd_shuffle_mask(a,b,c,d) ((a) | ((b) << 2) | ((c) << 4) | ((d) << 6))
#else
int offset;
#endif
+ int index;
MonoBasicBlock *bb;
MonoInst *inst;
MonoMethod *method;
MonoGenericSharingContext gsctx;
MonoGenericContext *gsctx_context;
- gboolean gsharedvt;
-
MonoGSharedVtMethodInfo *gsharedvt_info;
/* Points to the gsharedvt locals area at runtime */
guint has_atomic_cas_i4 : 1;
guint check_pinvoke_callconv : 1;
guint has_unwind_info_for_epilog : 1;
+ guint disable_inline : 1;
+ guint gshared : 1;
+ guint gsharedvt : 1;
gpointer debug_info;
guint32 lmf_offset;
guint16 *intvars;
guint32 encoded_unwind_ops_len;
GSList* unwind_ops;
+ GList* dont_inline;
+
/* Fields used by the local reg allocator */
void* reginfo;
int reginfo_len;
/* Symbol used to refer to this method in generated assembly */
char *asm_symbol;
char *llvm_method_name;
+ int castclass_cache_index;
MonoJitExceptionInfo *llvm_ex_info;
guint32 llvm_ex_info_len;
void mono_llvm_check_method_supported (MonoCompile *cfg) MONO_LLVM_INTERNAL;
void mono_llvm_free_domain_info (MonoDomain *domain) MONO_LLVM_INTERNAL;
+gboolean mini_llvm_init (void);
+
gboolean mono_method_blittable (MonoMethod *method) MONO_INTERNAL;
gboolean mono_method_same_domain (MonoJitInfo *caller, MonoJitInfo *callee) MONO_INTERNAL;
int mini_exception_id_by_name (const char *name) MONO_INTERNAL;
int mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_bblock, MonoBasicBlock *end_bblock,
- MonoInst *return_var, GList *dont_inline, MonoInst **inline_args,
+ MonoInst *return_var, MonoInst **inline_args,
guint inline_offset, gboolean is_virtual_call) MONO_INTERNAL;
MonoInst *mono_decompose_opcode (MonoCompile *cfg, MonoInst *ins) MONO_INTERNAL;
MonoMethod* mono_arch_find_imt_method (mgreg_t *regs, guint8 *code) MONO_INTERNAL;
MonoVTable* mono_arch_find_static_call_vtable (mgreg_t *regs, guint8 *code) MONO_INTERNAL;
gpointer mono_arch_build_imt_thunk (MonoVTable *vtable, MonoDomain *domain, MonoIMTCheckItem **imt_entries, int count, gpointer fail_tramp) MONO_INTERNAL;
-void mono_arch_notify_pending_exc (void) MONO_INTERNAL;
+void mono_arch_notify_pending_exc (MonoThreadInfo *info) MONO_INTERNAL;
guint8* mono_arch_get_call_target (guint8 *code) MONO_INTERNAL;
guint32 mono_arch_get_plt_info_offset (guint8 *plt_entry, mgreg_t *regs, guint8 *code) MONO_INTERNAL;
GSList *mono_arch_get_trampolines (gboolean aot) MONO_INTERNAL;
tramp = mono_get_trampoline_code (tramp_type);
- mono_domain_lock (domain);
+ if (domain) {
+ mono_domain_lock (domain);
#ifdef USE_JUMP_TABLES
- code = buf = mono_domain_code_reserve_align (domain, size, 4);
+ code = buf = mono_domain_code_reserve_align (domain, size, 4);
#else
- code = buf = mono_domain_code_reserve_align (domain, size, 4);
- if ((short_branch = branch_for_target_reachable (code + 4, tramp))) {
- size = 12;
- mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
+ code = buf = mono_domain_code_reserve_align (domain, size, 4);
+ if ((short_branch = branch_for_target_reachable (code + 4, tramp))) {
+ size = 12;
+ mono_domain_code_commit (domain, code, SPEC_TRAMP_SIZE, size);
}
#endif
- mono_domain_unlock (domain);
+ mono_domain_unlock (domain);
+ } else {
+ code = buf = mono_global_codeman_reserve (size);
+ }
#ifdef USE_JUMP_TABLES
/* For jumptables case we always generate the same code for trampolines,
return buf;
}
+static gpointer
+handler_block_trampoline_helper (gpointer *ptr)
+{
+ MonoJitTlsData *jit_tls = mono_native_tls_get_value (mono_jit_tls_id);
+ return jit_tls->handler_block_return_address;
+}
+
+gpointer
+mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+ guint8 *tramp;
+ guint8 *code, *buf;
+ int tramp_size = 64;
+ MonoJumpInfo *ji = NULL;
+ GSList *unwind_ops = NULL;
+
+ g_assert (!aot);
+
+ code = buf = mono_global_codeman_reserve (tramp_size);
+
+ tramp = mono_arch_create_specific_trampoline (NULL, MONO_TRAMPOLINE_HANDLER_BLOCK_GUARD, NULL, NULL);
+
+ /*
+ This trampoline restore the call chain of the handler block then jumps into the code that deals with it.
+ */
+
+ /*
+ * We are in a method frame after the call emitted by OP_CALL_HANDLER.
+ */
+ /* Obtain jit_tls->handler_block_return_address */
+ ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
+ ARM_B (code, 0);
+ *(gpointer*)code = handler_block_trampoline_helper;
+ code += 4;
+
+ /* Set it as the return address so the trampoline will return to it */
+ ARM_MOV_REG_REG (code, ARMREG_LR, ARMREG_R0);
+
+ /* Call the trampoline */
+ ARM_LDR_IMM (code, ARMREG_R0, ARMREG_PC, 0);
+ code = emit_bx (code, ARMREG_R0);
+ *(gpointer*)code = tramp;
+ code += 4;
+
+ mono_arch_flush_icache (buf, code - buf);
+ g_assert (code - buf <= tramp_size);
+
+ if (info)
+ *info = mono_tramp_info_create ("handler_block_trampoline", buf, code - buf, ji, unwind_ops);
+
+ return buf;
+}
+
#else
guchar*
g_assert_not_reached ();
return NULL;
}
+
+gpointer
+mono_arch_create_handler_block_trampoline (MonoTrampInfo **info, gboolean aot)
+{
+ g_assert_not_reached ();
+ return NULL;
+}
#endif /* DISABLE_JIT */
SGEN_CONFIGURATIONS = \
"|plain" \
- "major=marksweep-par|ms-par" \
"major=marksweep-conc|ms-conc" \
"major=marksweep-conc,minor=split|ms-conc-split" \
"minor=split|ms-split" \
"minor=split,alloc-ratio=95|ms-split-95" \
"|plain-clear-at-gc|clear-at-gc" \
- "major=marksweep-par|ms-par-clear-at-gc|clear-at-gc" \
"major=marksweep-conc|ms-conc-clear-at-gc|clear-at-gc" \
"minor=split|ms-split-clear-at-gc|clear-at-gc"
test-oom: $(OOM_TESTS)
@for fn in $+ ; do \
echo "Testing $$fn ..."; \
- MONO_GC_PARAMS=max-heap-size=16m,major=marksweep-par MONO_ENV_OPTIONS="--gc=sgen" $(RUNTIME) $$fn > $$fn.stdout || exit 1; \
MONO_GC_PARAMS=max-heap-size=16m MONO_ENV_OPTIONS="--gc=sgen" $(RUNTIME) $$fn > $$fn.stdout || exit 1; \
MONO_GC_PARAMS=max-heap-size=16m $(RUNTIME) $$fn > $$fn.stdout || exit 1; \
done
lock (monitor) {
foreach (Process p in processes) {
Console.WriteLine (process_data [p].test);
+ p.Kill ();
}
}
return 1;
#define MONO_THREAD_VAR_OFFSET(var,offset) (offset) = -1
#endif
-#elif defined(__APPLE__) && (defined(__i386__) || defined(__x86_64__))
+#elif defined(TARGET_MACH) && (defined(__i386__) || defined(__x86_64__))
#define MONO_HAVE_FAST_TLS
#define MONO_FAST_TLS_SET(x,y) pthread_setspecific(x, y)
#elif (defined(__arm__) && !defined(MONO_CROSS_COMPILE)) || (defined(TARGET_ARM)) /* defined(__x86_64__) */
+#include <mono/arch/arm/arm-codegen.h>
+
typedef struct {
mgreg_t pc;
mgreg_t regs [16];
if (size)
*size = res;
return buf;
+#elif defined(__HAIKU__)
+ /* FIXME: Add back the code from 9185fcc305e43428d0f40f3ee37c8a405d41c9ae */
+ g_assert_not_reached ();
+ return NULL;
#else
const char *name;
void **buf = NULL;
if (threads_callbacks.thread_register) {
if (threads_callbacks.thread_register (info, baseptr) == NULL) {
- g_warning ("thread registation failed\n");
+ // g_warning ("thread registation failed\n");
g_free (info);
return NULL;
}
for (;;) {
const char *suspend_error = "Unknown error";
if (!(info = mono_thread_info_suspend_sync (id, interrupt_kernel, &suspend_error))) {
- g_warning ("failed to suspend thread %p due to %s, hopefully it is dead", (gpointer)id, suspend_error);
+ // g_warning ("failed to suspend thread %p due to %s, hopefully it is dead", (gpointer)id, suspend_error);
mono_thread_info_suspend_unlock ();
return NULL;
}
break;
if (!mono_thread_info_core_resume (info)) {
- g_warning ("failed to resume thread %p, hopefully it is dead", (gpointer)id);
+ // g_warning ("failed to resume thread %p, hopefully it is dead", (gpointer)id);
mono_hazard_pointer_clear (mono_hazard_pointer_get (), 1);
mono_thread_info_suspend_unlock ();
return NULL;
#if defined(HOST_WIN32)
return !disable_new_interrupt;
#endif
-#if defined (__i386__)
+#if defined (__i386__) || defined(__x86_64__)
return !disable_new_interrupt;
#endif
-#if defined(__arm__) && !defined(__APPLE__)
+#if defined(__arm__)
return !disable_new_interrupt;
#endif
return FALSE;
/al1
/al2
/caspol
+/cert-sync
/cert2spc
/certmgr
/cccheck
#endif
#if _WIN32_WINNT < 0x0502
-/* Required for Vectored Exception Handling.
- Interlocked* functions are also not available in XP SP1 and below
-*/
-#undef _WIN32_WINNT
-#define _WIN32_WINNT 0x0502
+#error "Mono requires WinXP SP2 or later"
#endif /* _WIN32_WINNT < 0x0502 */
/*
/* #undef HAVE_GETPRIORITY */
/* Define to 1 if you have the `GetProcessId' function. */
-#if (_WIN32_WINNT >= 0x0502)
#define HAVE_GETPROCESSID 1
-#endif
/* Define to 1 if you have the `getpwnam_r' function. */
/* #undef HAVE_GETPWNAM_R */