Merge pull request #92 from komsa-ag/Feature/NoLock

diff --git a/.gitignore b/.gitignore
index e260d61..ad029ec 100644
--- a/.gitignore
+++ b/.gitignore
@@ -243,3 +243,4 @@
 vs_buildtools.exe
 dotnetfx35.exe
 *.exe
+/src/Binaries/*
diff --git a/src/log4net/Appender/RemoteSyslogAppender.cs b/src/log4net/Appender/RemoteSyslogAppender.cs
index 675d6f9..b67abd3 100644
--- a/src/log4net/Appender/RemoteSyslogAppender.cs
+++ b/src/log4net/Appender/RemoteSyslogAppender.cs
@@ -344,88 +344,98 @@
 		/// </remarks>
 		protected override void Append(LoggingEvent loggingEvent)
 		{
-            try
-            {
-                // Priority
-                int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
+			try
+			{
+				// Priority
+				int priority = GeneratePriority(m_facility, GetSeverity(loggingEvent.Level));
 
-                // Identity
-                string identity;
+				// Identity
+				string identity;
 
-                if (m_identity != null)
-                {
-                    identity = m_identity.Format(loggingEvent);
-                }
-                else
-                {
-                    identity = loggingEvent.Domain;
-                }
+				if (m_identity != null)
+				{
+					identity = m_identity.Format(loggingEvent);
+				}
+				else
+				{
+					identity = loggingEvent.Domain;
+				}
 
-                // Message. The message goes after the tag/identity
-                string message = RenderLoggingEvent(loggingEvent);
+				// Message. The message goes after the tag/identity
+				string message = RenderLoggingEvent(loggingEvent);
 
-                Byte[] buffer;
-                int i = 0;
-                char c;
+				byte[] buffer;
+				int i = 0;
 
-                StringBuilder builder = new StringBuilder();
+				StringBuilder builder = new StringBuilder();
 
-                while (i < message.Length)
-                {
-                    // Clear StringBuilder
-                    builder.Length = 0;
+				while (i < message.Length)
+				{
+					// Clear StringBuilder
+					builder.Length = 0;
 
-                    // Write priority
-                    builder.Append('<');
-                    builder.Append(priority);
-                    builder.Append('>');
+					// Write priority
+					builder.Append('<');
+					builder.Append(priority);
+					builder.Append('>');
 
-                    // Write identity
-                    builder.Append(identity);
-                    builder.Append(": ");
+					// Write identity
+					builder.Append(identity);
+					builder.Append(": ");
 
-                    for (; i < message.Length; i++)
-                    {
-                        c = message[i];
+					AppendMessage(message, ref i, builder);
 
-                        // Accept only visible ASCII characters and space. See RFC 3164 section 4.1.3
-                        if (((int)c >= 32) && ((int)c <= 126))
-                        {
-                            builder.Append(c);
-                        }
-                        // If character is newline, break and send the current line
-                        else if ((c == '\r') || (c == '\n'))
-                        {
-                            // Check the next character to handle \r\n or \n\r
-                            if ((message.Length > i + 1) && ((message[i + 1] == '\r') || (message[i + 1] == '\n')))
-                            {
-                                i++;
-                            }
-                            i++;
-                            break;
-                        }
-                    }
-
-                    // Grab as a byte array
-                    buffer = this.Encoding.GetBytes(builder.ToString());
+					// Grab as a byte array
+					buffer = this.Encoding.GetBytes(builder.ToString());
 
 #if NET_4_5 || NETSTANDARD
-                    Client.SendAsync(buffer, buffer.Length, RemoteEndPoint).Wait();
+					Client.SendAsync(buffer, buffer.Length, RemoteEndPoint).Wait();
 #else
-                    this.Client.Send(buffer, buffer.Length, this.RemoteEndPoint);
+					this.Client.Send(buffer, buffer.Length, this.RemoteEndPoint);
 #endif
-                }
-            }
-            catch (Exception e)
-            {
-                ErrorHandler.Error(
-                    "Unable to send logging event to remote syslog " +
-                    this.RemoteAddress.ToString() +
-                    " on port " +
-                    this.RemotePort + ".",
-                    e,
-                    ErrorCode.WriteFailure);
-            }
+				}
+			}
+			catch (Exception e)
+			{
+				ErrorHandler.Error(
+						"Unable to send logging event to remote syslog " +
+						this.RemoteAddress.ToString() +
+						" on port " +
+						this.RemotePort + ".",
+						e,
+						ErrorCode.WriteFailure);
+			}
+		}
+
+		/// <summary>
+		/// Appends the rendered message to the buffer
+		/// </summary>
+		/// <param name="message">rendered message</param>
+		/// <param name="characterIndex">index of the current character in the message</param>
+		/// <param name="builder">buffer</param>
+		protected virtual void AppendMessage(string message, ref int characterIndex, StringBuilder builder)
+		{
+			for (; characterIndex < message.Length; characterIndex++)
+			{
+				char c = message[characterIndex];
+
+				// Accept only visible ASCII characters and space. See RFC 3164 section 4.1.3
+				if (((int)c >= 32) && ((int)c <= 126))
+				{
+					builder.Append(c);
+				}
+				// If character is newline, break and send the current line
+				else if ((c == '\r') || (c == '\n'))
+				{
+					// Check the next character to handle \r\n or \n\r
+					if ((message.Length > characterIndex + 1) && ((message[characterIndex + 1] == '\r') || (message[characterIndex + 1] == '\n')))
+					{
+						characterIndex++;
+					}
+					characterIndex++;
+					break;
+				}
+			}
 		}
 
 		/// <summary>
diff --git a/src/log4net/Util/SystemInfo.cs b/src/log4net/Util/SystemInfo.cs
index 92e2c60..821a230 100644
--- a/src/log4net/Util/SystemInfo.cs
+++ b/src/log4net/Util/SystemInfo.cs
@@ -59,7 +59,7 @@
 		/// Only static methods are exposed from this type.
 		/// </para>
 		/// </remarks>
-		private SystemInfo() 
+		private SystemInfo()
 		{
 		}
 
@@ -142,7 +142,7 @@
 		/// </remarks>
 		public static string ApplicationBaseDirectory
 		{
-			get 
+			get
 			{
 #if NETCF
 -				return System.IO.Path.GetDirectoryName(SystemInfo.EntryAssemblyLocation) + System.IO.Path.DirectorySeparatorChar;
@@ -170,7 +170,7 @@
 		/// </remarks>
 		public static string ConfigurationFileLocation
 		{
-			get 
+			get
 			{
 #if NETCF || NETSTANDARD
 				return SystemInfo.EntryAssemblyLocation+".config";
@@ -180,6 +180,8 @@
 			}
 		}
 
+		private static string entryAssemblyLocation;
+
 		/// <summary>
 		/// Gets the path to the file that first executed in the current <see cref="AppDomain"/>.
 		/// </summary>
@@ -191,16 +193,20 @@
 		/// </remarks>
 		public static string EntryAssemblyLocation
 		{
-			get 
+			get
 			{
+				if (entryAssemblyLocation != null)
+					return entryAssemblyLocation;
 #if NETCF
-				return SystemInfo.NativeEntryAssemblyLocation;
+				return entryAssemblyLocation = SystemInfo.NativeEntryAssemblyLocation;
 #elif NETSTANDARD1_3 // TODO GetEntryAssembly is available for netstandard1.5
-				return AppContext.BaseDirectory;
+				return entryAssemblyLocation = AppContext.BaseDirectory;
 #else
-				return System.Reflection.Assembly.GetEntryAssembly().Location;
+				return entryAssemblyLocation = Assembly.GetEntryAssembly()?.Location
+					?? throw new InvalidOperationException($"Unable to determine EntryAssembly location: EntryAssembly is null. Try explicitly setting {nameof(SystemInfo)}.{nameof(EntryAssemblyLocation)}");
 #endif
 			}
+			set => entryAssemblyLocation = value;
 		}
 
 		/// <summary>
@@ -227,7 +233,7 @@
 		/// </remarks>
 		public static int CurrentThreadId
 		{
-			get 
+			get
 			{
 #if NETCF_1_0
 				return System.Threading.Thread.CurrentThread.GetHashCode();
@@ -295,10 +301,10 @@
 							s_hostName = Environment.MachineName;
 #endif
 						}
-						catch(InvalidOperationException)
+						catch (InvalidOperationException)
 						{
 						}
-						catch(System.Security.SecurityException)
+						catch (System.Security.SecurityException)
 						{
 							// We may get a security exception looking up the machine name
 							// You must have Unrestricted EnvironmentPermission to access resource
@@ -343,7 +349,7 @@
 						s_appFriendlyName = AppDomain.CurrentDomain.FriendlyName;
 #endif
 					}
-					catch(System.Security.SecurityException)
+					catch (System.Security.SecurityException)
 					{
 						// This security exception will occur if the caller does not have 
 						// some undefined set of SecurityPermission flags.
@@ -357,7 +363,7 @@
 							string assemblyLocation = SystemInfo.EntryAssemblyLocation;
 							s_appFriendlyName = System.IO.Path.GetFileName(assemblyLocation);
 						}
-						catch(System.Security.SecurityException)
+						catch (System.Security.SecurityException)
 						{
 							// Caller needs path discovery permission
 						}
@@ -392,35 +398,35 @@
 		/// will be set per AppDomain.
 		/// </para>
 		/// </remarks>
-        [Obsolete("Use ProcessStartTimeUtc and convert to local time if needed.")]
+		[Obsolete("Use ProcessStartTimeUtc and convert to local time if needed.")]
 		public static DateTime ProcessStartTime
 		{
 			get { return s_processStartTimeUtc.ToLocalTime(); }
 		}
 
-        /// <summary>
-        /// Get the UTC start time for the current process.
-        /// </summary>
-        /// <remarks>
-        /// <para>
-        /// This is the UTC time at which the log4net library was loaded into the
-        /// AppDomain. Due to reports of a hang in the call to <c>System.Diagnostics.Process.StartTime</c>
-        /// this is not the start time for the current process.
-        /// </para>
-        /// <para>
-        /// The log4net library should be loaded by an application early during its
-        /// startup, therefore this start time should be a good approximation for
-        /// the actual start time.
-        /// </para>
-        /// <para>
-        /// Note that AppDomains may be loaded and unloaded within the
-        /// same process without the process terminating, however this start time
-        /// will be set per AppDomain.
-        /// </para>
-        /// </remarks>
-        public static DateTime ProcessStartTimeUtc
-        {
-            get { return s_processStartTimeUtc; }
+		/// <summary>
+		/// Get the UTC start time for the current process.
+		/// </summary>
+		/// <remarks>
+		/// <para>
+		/// This is the UTC time at which the log4net library was loaded into the
+		/// AppDomain. Due to reports of a hang in the call to <c>System.Diagnostics.Process.StartTime</c>
+		/// this is not the start time for the current process.
+		/// </para>
+		/// <para>
+		/// The log4net library should be loaded by an application early during its
+		/// startup, therefore this start time should be a good approximation for
+		/// the actual start time.
+		/// </para>
+		/// <para>
+		/// Note that AppDomains may be loaded and unloaded within the
+		/// same process without the process terminating, however this start time
+		/// will be set per AppDomain.
+		/// </para>
+		/// </remarks>
+		public static DateTime ProcessStartTimeUtc
+		{
+			get { return s_processStartTimeUtc; }
 		}
 
 		/// <summary>
@@ -483,7 +489,7 @@
 #if NETCF
 			return "Not supported on Microsoft .NET Compact Framework";
 #elif NETSTANDARD1_3
-            return "Not supported on .NET Core";
+						return "Not supported on .NET Core";
 #else
 			if (myAssembly.GlobalAssemblyCache)
 			{
@@ -665,7 +671,7 @@
 			return GetTypeFromString(relativeType.Assembly, typeName, throwOnError, ignoreCase);
 #endif
 		}
-		
+
 #if !NETSTANDARD1_3
 		/// <summary>
 		/// Loads the type specified in the type string.
@@ -715,7 +721,7 @@
 		public static Type GetTypeFromString(Assembly relativeAssembly, string typeName, bool throwOnError, bool ignoreCase)
 		{
 			// Check if the type name specifies the assembly name
-			if(typeName.IndexOf(',') == -1)
+			if (typeName.IndexOf(',') == -1)
 			{
 				//LogLog.Debug(declaringType, "SystemInfo: Loading type ["+typeName+"] from assembly ["+relativeAssembly.FullName+"]");
 #if NETSTANDARD1_3
@@ -737,7 +743,7 @@
 				{
 					loadedAssemblies = AppDomain.CurrentDomain.GetAssemblies();
 				}
-				catch(System.Security.SecurityException)
+				catch (System.Security.SecurityException)
 				{
 					// Insufficient permissions to get the list of loaded assemblies
 				}
@@ -746,33 +752,33 @@
 				{
 					Type fallback = null;
 					// Search the loaded assemblies for the type
-					foreach (Assembly assembly in loadedAssemblies) 
+					foreach (Assembly assembly in loadedAssemblies)
 					{
 						Type t = assembly.GetType(typeName, false, ignoreCase);
 						if (t != null)
 						{
 							// Found type in loaded assembly
-							LogLog.Debug(declaringType, "Loaded type ["+typeName+"] from assembly ["+assembly.FullName+"] by searching loaded assemblies.");
-                                                        if (assembly.GlobalAssemblyCache)
-                                                        {
-                                                            fallback = t;
-                                                        }
-                                                        else
-                                                        {
-                                                            return t;
-                                                        }
+							LogLog.Debug(declaringType, "Loaded type [" + typeName + "] from assembly [" + assembly.FullName + "] by searching loaded assemblies.");
+							if (assembly.GlobalAssemblyCache)
+							{
+								fallback = t;
+							}
+							else
+							{
+								return t;
+							}
 						}
 					}
-                                        if (fallback != null)
-                                        {
-                                            return fallback;
-                                        }
+					if (fallback != null)
+					{
+						return fallback;
+					}
 				}
 
 				// Didn't find the type
 				if (throwOnError)
 				{
-					throw new TypeLoadException("Could not load type ["+typeName+"]. Tried assembly ["+relativeAssembly.FullName+"] and all loaded assemblies");
+					throw new TypeLoadException("Could not load type [" + typeName + "]. Tried assembly [" + relativeAssembly.FullName + "] and all loaded assemblies");
 				}
 				return null;
 #endif
@@ -938,20 +944,20 @@
 #endif
 		}
 
-        /// <summary>
-        /// Parse a string into an <see cref="Int16"/> value
-        /// </summary>
-        /// <param name="s">the string to parse</param>
-        /// <param name="val">out param where the parsed value is placed</param>
-        /// <returns><c>true</c> if the string was able to be parsed into an integer</returns>
-        /// <remarks>
-        /// <para>
-        /// Attempts to parse the string into an integer. If the string cannot
-        /// be parsed then this method returns <c>false</c>. The method does not throw an exception.
-        /// </para>
-        /// </remarks>
-        public static bool TryParse(string s, out short val)
-        {
+		/// <summary>
+		/// Parse a string into an <see cref="Int16"/> value
+		/// </summary>
+		/// <param name="s">the string to parse</param>
+		/// <param name="val">out param where the parsed value is placed</param>
+		/// <returns><c>true</c> if the string was able to be parsed into an integer</returns>
+		/// <remarks>
+		/// <para>
+		/// Attempts to parse the string into an integer. If the string cannot
+		/// be parsed then this method returns <c>false</c>. The method does not throw an exception.
+		/// </para>
+		/// </remarks>
+		public static bool TryParse(string s, out short val)
+		{
 #if NETCF
 			val = 0;
 			try
@@ -965,28 +971,28 @@
 
 			return false;
 #else
-            // Initialise out param
-            val = 0;
+			// Initialise out param
+			val = 0;
 
-            try 
-            {
-                double doubleVal;
-                if (Double.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out doubleVal))
-                {
-                    val = Convert.ToInt16(doubleVal);
-                    return true;
-                }
-            }
-            catch
-            {
-                // Ignore exception, just return false
-            }
+			try
+			{
+				double doubleVal;
+				if (Double.TryParse(s, System.Globalization.NumberStyles.Integer, System.Globalization.CultureInfo.InvariantCulture, out doubleVal))
+				{
+					val = Convert.ToInt16(doubleVal);
+					return true;
+				}
+			}
+			catch
+			{
+				// Ignore exception, just return false
+			}
 
-            return false;
+			return false;
 #endif
-        }
+		}
 
-        /// <summary>
+		/// <summary>
 		/// Lookup an application setting
 		/// </summary>
 		/// <param name="key">the application settings key to lookup</param>
@@ -1008,7 +1014,7 @@
 				return ConfigurationSettings.AppSettings[key];
 #endif
 			}
-			catch(Exception ex)
+			catch (Exception ex)
 			{
 				// If an exception is thrown here then it looks like the config file does not parse correctly.
 				LogLog.Error(declaringType, "Exception while reading ConfigurationSettings. Check your .config file is well formed XML.", ex);
@@ -1086,28 +1092,28 @@
 #endif
 		}
 
-        /// <summary>
-        /// Tests two strings for equality, the ignoring case.
-        /// </summary>
-        /// <remarks>
-        /// If the platform permits, culture information is ignored completely (ordinal comparison).
-        /// The aim of this method is to provide a fast comparison that deals with <c>null</c> and ignores different casing.
-        /// It is not supposed to deal with various, culture-specific habits.
-        /// Use it to compare against pure ASCII constants, like keywords etc.
-        /// </remarks>
-        /// <param name="a">The one string.</param>
-        /// <param name="b">The other string.</param>
-        /// <returns><c>true</c> if the strings are equal, <c>false</c> otherwise.</returns>
-        public static Boolean EqualsIgnoringCase(String a, String b)
-        {
+		/// <summary>
+		/// Tests two strings for equality, the ignoring case.
+		/// </summary>
+		/// <remarks>
+		/// If the platform permits, culture information is ignored completely (ordinal comparison).
+		/// The aim of this method is to provide a fast comparison that deals with <c>null</c> and ignores different casing.
+		/// It is not supposed to deal with various, culture-specific habits.
+		/// Use it to compare against pure ASCII constants, like keywords etc.
+		/// </remarks>
+		/// <param name="a">The one string.</param>
+		/// <param name="b">The other string.</param>
+		/// <returns><c>true</c> if the strings are equal, <c>false</c> otherwise.</returns>
+		public static Boolean EqualsIgnoringCase(String a, String b)
+		{
 #if NET_1_0 || NET_1_1 || NETCF_1_0
-            return string.Compare(a, b, true, System.Globalization.CultureInfo.InvariantCulture) == 0
+						return string.Compare(a, b, true, System.Globalization.CultureInfo.InvariantCulture) == 0
 #elif NETSTANDARD1_3
-            return CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0;
+						return CultureInfo.InvariantCulture.CompareInfo.Compare(a, b, CompareOptions.IgnoreCase) == 0;
 #else // >= .NET-2.0
-            return String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
+			return String.Equals(a, b, StringComparison.OrdinalIgnoreCase);
 #endif
-        }
+		}
 
 		#endregion Public Static Methods
 
@@ -1169,14 +1175,14 @@
 
 		#region Private Static Fields
 
-	    /// <summary>
-	    /// The fully qualified type of the SystemInfo class.
-	    /// </summary>
-	    /// <remarks>
-	    /// Used by the internal logger to record the Type of the
-	    /// log message.
-	    /// </remarks>
-	    private static readonly Type declaringType = typeof(SystemInfo);
+		/// <summary>
+		/// The fully qualified type of the SystemInfo class.
+		/// </summary>
+		/// <remarks>
+		/// Used by the internal logger to record the Type of the
+		/// log message.
+		/// </remarks>
+		private static readonly Type declaringType = typeof(SystemInfo);
 
 		/// <summary>
 		/// Cache the host name for the current machine