<commit_before>﻿//  Copyright 2011-2012 <NAME>, <NAME>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//  A commercial license of this software can also be purchased. 
//  Please see <http://www.networkcommsdotnet.com/licenses> for details.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using DPSBase;
using System.Net.Sockets;

namespace NetworkCommsDotNet
{
    /// <summary>
    /// Global connection base class for NetworkCommsDotNet. Most user interactions happen using a connection object. Extended by <see cref="TCPConnection"/> and <see cref="UDPConnection"/>.
    /// </summary>
    public abstract partial class Connection
    {
        static volatile bool shutdownWorkerThreads = false;
        static object staticConnectionLocker = new object();
        static Thread connectionKeepAliveWorker;

        /// <summary>
        /// Private static TCP constructor which sets any connection defaults
        /// </summary>
        static Connection()
        {
            ConnectionKeepAlivePollIntervalSecs = 30;
        }

        /// <summary>
        /// The interval between keep alive polls of all connections. Set to int.MaxValue to disable keep alive poll
        /// </summary>
        public static int ConnectionKeepAlivePollIntervalSecs { get; set; }

        /// <summary>
        /// Starts the connectionKeepAliveWorker thread if it is not already started
        /// </summary>
        protected static void TriggerConnectionKeepAliveThread()
        {
            lock (staticConnectionLocker)
            {
                if (!shutdownWorkerThreads && (connectionKeepAliveWorker == null || connectionKeepAliveWorker.ThreadState == ThreadState.Stopped))
                {
                    connectionKeepAliveWorker = new Thread(ConnectionKeepAliveWorker);
                    connectionKeepAliveWorker.Name = "ConnectionKeepAliveWorker";
                    connectionKeepAliveWorker.Start();
                }
            }
        }

        /// <summary>
        /// A single static worker thread which keeps connections alive
        /// </summary>
        private static void ConnectionKeepAliveWorker()
        {
            if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Connection keep alive polling thread has started.");
            DateTime lastPollCheck = DateTime.Now;

            while (!shutdownWorkerThreads)
            {
                try
                {
                    //We have a short sleep here so that we can exit the thread fairly quickly if we need too
                    if (ConnectionKeepAlivePollIntervalSecs == int.MaxValue)
                        Thread.Sleep(5000);
                    else
                        Thread.Sleep(100);

                    //Any connections which we have not seen in the last poll interval get tested using a null packet
                    if (ConnectionKeepAlivePollIntervalSecs < int.MaxValue && (DateTime.Now - lastPollCheck).TotalSeconds > (double)ConnectionKeepAlivePollIntervalSecs)
                    {
                        AllConnectionsSendNullPacketKeepAlive();
                        lastPollCheck = DateTime.Now;
                    }
                }
                catch (Exception ex)
                {
                    NetworkComms.LogError(ex, "ConnectionKeepAlivePollError");
                }
            }
        }

        /// <summary>
        /// Polls all existing connections based on ConnectionKeepAlivePollIntervalSecs value. Serverside connections are polled slightly earlier than client side to help reduce potential congestion.
        /// </summary>
        /// <param name="returnImmediately"></param>
        private static void AllConnectionsSendNullPacketKeepAlive(bool returnImmediately = false)
        {
            //Loop through all connections and test the alive state
            List<Connection> allConnections = NetworkComms.GetExistingConnection();

            List<WaitHandle> connectionCheckHandles = new List<WaitHandle>();

            for (int i = 0; i < allConnections.Count; i++)
            {
                int innerIndex = i;
                ManualResetEvent handle = new ManualResetEvent(false);
                               
                connectionCheckHandles.Add(handle);
                ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
                {
                    try
                    {
                        //If the connection is server side we poll preferentially
                        if (allConnections[innerIndex] != null)
                        {
                            if (allConnections[innerIndex].ConnectionInfo.ServerSide)
                            {
                                //We check the last incoming traffic time
                                //In scenarios where the client is sending us lots of data there is no need to poll
                                if ((DateTime.Now - allConnections[innerIndex].ConnectionInfo.LastTrafficTime).TotalSeconds > ConnectionKeepAlivePollIntervalSecs)
                                    allConnections[innerIndex].SendNullPacket();
                            }
                            else
                            {
                                //If we are client side we wait upto an additional 3 seconds to do the poll
                                //This means the server will probably beat us
                                if ((DateTime.Now - allConnections[innerIndex].ConnectionInfo.LastTrafficTime).TotalSeconds > ConnectionKeepAlivePollIntervalSecs + 1.0 + (NetworkComms.randomGen.NextDouble() * 2.0))
                                    allConnections[innerIndex].SendNullPacket();
                            }
                        }
                    }
                    catch (Exception) { }
                    finally { handle.Set(); }
                }));
            }

            if (!returnImmediately) WaitHandle.WaitAll(connectionCheckHandles.ToArray());
        }

        /// <summary>
        /// Shutdown any static connection components
        /// </summary>
        /// <param name="threadShutdownTimeoutMS"></param>
        internal static void ShutdownBase(int threadShutdownTimeoutMS = 1000)
        {
            try
            {
                shutdownWorkerThreads = true;

                if (connectionKeepAliveWorker != null && !connectionKeepAliveWorker.Join(threadShutdownTimeoutMS))
                    connectionKeepAliveWorker.Abort();
            }
            catch (Exception ex)
            {
                NetworkComms.LogError(ex, "CommsShutdownError");
            }
            finally
            {
                shutdownWorkerThreads = false;
            }
        }
    }<commit_msg>Bug fix due to change in NullPacketKeepAlive layout.<commit_after>﻿//  Copyright 2011-2012 <NAME>, <NAME>
//
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  This program 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 General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
//
//  A commercial license of this software can also be purchased. 
//  Please see <http://www.networkcommsdotnet.com/licenses> for details.

using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;
using DPSBase;
using System.Net.Sockets;

namespace NetworkCommsDotNet
{
    /// <summary>
    /// Global connection base class for NetworkCommsDotNet. Most user interactions happen using a connection object. Extended by <see cref="TCPConnection"/> and <see cref="UDPConnection"/>.
    /// </summary>
    public abstract partial class Connection
    {
        static volatile bool shutdownWorkerThreads = false;
        static object staticConnectionLocker = new object();
        static Thread connectionKeepAliveWorker;

        /// <summary>
        /// Private static TCP constructor which sets any connection defaults
        /// </summary>
        static Connection()
        {
            ConnectionKeepAlivePollIntervalSecs = 30;
        }

        /// <summary>
        /// The interval between keep alive polls of all connections. Set to int.MaxValue to disable keep alive poll
        /// </summary>
        public static int ConnectionKeepAlivePollIntervalSecs { get; set; }

        /// <summary>
        /// Starts the connectionKeepAliveWorker thread if it is not already started
        /// </summary>
        protected static void TriggerConnectionKeepAliveThread()
        {
            lock (staticConnectionLocker)
            {
                if (!shutdownWorkerThreads && (connectionKeepAliveWorker == null || connectionKeepAliveWorker.ThreadState == ThreadState.Stopped))
                {
                    connectionKeepAliveWorker = new Thread(ConnectionKeepAliveWorker);
                    connectionKeepAliveWorker.Name = "ConnectionKeepAliveWorker";
                    connectionKeepAliveWorker.Start();
                }
            }
        }

        /// <summary>
        /// A single static worker thread which keeps connections alive
        /// </summary>
        private static void ConnectionKeepAliveWorker()
        {
            if (NetworkComms.LoggingEnabled) NetworkComms.Logger.Debug("Connection keep alive polling thread has started.");
            DateTime lastPollCheck = DateTime.Now;

            while (!shutdownWorkerThreads)
            {
                try
                {
                    //We have a short sleep here so that we can exit the thread fairly quickly if we need too
                    if (ConnectionKeepAlivePollIntervalSecs == int.MaxValue)
                        Thread.Sleep(5000);
                    else
                        Thread.Sleep(100);

                    //Any connections which we have not seen in the last poll interval get tested using a null packet
                    if (ConnectionKeepAlivePollIntervalSecs < int.MaxValue && (DateTime.Now - lastPollCheck).TotalSeconds > (double)ConnectionKeepAlivePollIntervalSecs)
                    {
                        AllConnectionsSendNullPacketKeepAlive();
                        lastPollCheck = DateTime.Now;
                    }
                }
                catch (Exception ex)
                {
                    NetworkComms.LogError(ex, "ConnectionKeepAlivePollError");
                }
            }
        }

        /// <summary>
        /// Polls all existing connections based on ConnectionKeepAlivePollIntervalSecs value. Serverside connections are polled slightly earlier than client side to help reduce potential congestion.
        /// </summary>
        /// <param name="returnImmediately"></param>
        private static void AllConnectionsSendNullPacketKeepAlive(bool returnImmediately = false)
        {
            //Loop through all connections and test the alive state
            List<Connection> allConnections = NetworkComms.GetExistingConnection();

            List<WaitHandle> connectionCheckHandles = new List<WaitHandle>();

            for (int i = 0; i < allConnections.Count; i++)
            {
                int innerIndex = i;
                ManualResetEvent handle = new ManualResetEvent(false);
                               
                connectionCheckHandles.Add(handle);
                ThreadPool.QueueUserWorkItem(new WaitCallback((obj) =>
                {
                    try
                    {
                        //If the connection is server side we poll preferentially
                        if (allConnections[innerIndex] != null)
                        {
                            if (allConnections[innerIndex].ConnectionInfo.ServerSide)
                            {
                                //We check the last incoming traffic time
                                //In scenarios where the client is sending us lots of data there is no need to poll
                                if ((DateTime.Now - allConnections[innerIndex].ConnectionInfo.LastTrafficTime).TotalSeconds > ConnectionKeepAlivePollIntervalSecs)
                                    allConnections[innerIndex].SendNullPacket();
                            }
                            else
                            {
                                //If we are client side we wait upto an additional 3 seconds to do the poll
                                //This means the server will probably beat us
                                if ((DateTime.Now - allConnections[innerIndex].ConnectionInfo.LastTrafficTime).TotalSeconds > ConnectionKeepAlivePollIntervalSecs + 1.0 + (NetworkComms.randomGen.NextDouble() * 2.0))
                                    allConnections[innerIndex].SendNullPacket();
                            }
                        }
                    }
                    catch (Exception) { }
                    finally { handle.Set(); }
                }));
            }

            if (!returnImmediately && connectionCheckHandles.Count > 0) WaitHandle.WaitAll(connectionCheckHandles.ToArray());
        }

        /// <summary>
        /// Shutdown any static connection components
        /// </summary>
        /// <param name="threadShutdownTimeoutMS"></param>
        internal static void ShutdownBase(int threadShutdownTimeoutMS = 1000)
        {
            try
            {
                shutdownWorkerThreads = true;

                if (connectionKeepAliveWorker != null && !connectionKeepAliveWorker.Join(threadShutdownTimeoutMS))
                    connectionKeepAliveWorker.Abort();
            }
            catch (Exception ex)
            {
                NetworkComms.LogError(ex, "CommsShutdownError");
            }
            finally
            {
                shutdownWorkerThreads = false;
            }
        }
    }
{'fraction_non_alphanumeric': 0.07429947167792417, 'fraction_numerical': 0.0038119440914866584, 'mean_word_length': 1.651418439716312, 'pattern_counts': {'":': 0, '<': 61, '<?xml version=': 0, '>': 66, 'https://': 0, 'lorem ipsum': 0, 'www.': 4, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 8, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3011376', 'n_tokens_mistral': 3475, 'n_tokens_neox': 3277, 'n_words': 1035}