/// <summary> /// Check the usage of the CPU by this thread. If too much, do a balancing of the sockets. /// </summary> /// <param name="controlInformation"></param> private static void LoadBalancingTimer(ControlThreadInformation controlInformation, long now) { if (ProcessorsCount < 2 || controlInformation._rudpSockets.Count < 1) return; if ((now - controlInformation.LastCheckThreadCPUUsageTS) < CheckThreadCPUUsageInterval) return; lock (_loadBalancingSync) { //---- Calculate time spent long currentUsage = controlInformation.ProcessThread.TotalProcessorTime.Ticks; long spentTime = currentUsage - controlInformation.PreviousProcessorTime; controlInformation.PreviousProcessorTime = currentUsage; // Save it Thread.VolatileWrite(ref _controlThreadCPUUsages[controlInformation.ControlThreadId], spentTime); controlInformation.LastCheckThreadCPUUsageTS = now; long minUsage = Int32.MaxValue; long maxUsage = Int32.MinValue; int minIndex = -1; int maxIndex = -1; //---- Check for the min and max usage for (int index = 0; index < ProcessorsCount; index++) { if (_controlThreadCPUUsages[index] > maxUsage) { maxUsage = _controlThreadCPUUsages[index]; maxIndex = index; } if (_controlThreadCPUUsages[index] < minUsage) { minUsage = _controlThreadCPUUsages[index]; minIndex = index; } } //---- I use too much CPU compared to others float percent = 1.0f / 0.15f; // 15% if ((minUsage * percent) > maxUsage && maxIndex == controlInformation.ControlThreadId) { // Remove the socket from our list RUDPSocket balancingSocket = controlInformation._rudpSockets[0]; controlInformation._rudpSockets.RemoveAt(0); // Put the socket in the new list _controlThreadInformations[minIndex]._rudpSocketsLock.EnterWriteLock(); _controlThreadInformations[minIndex]._rudpSockets.Add(balancingSocket); _controlThreadInformations[minIndex]._rudpSocketsLock.ExitWriteLock(); //System.Console.WriteLine("BALANCING"); } //else System.Console.WriteLine("NO BALANCING"); } }
/// <summary> /// Set up a control thread to use the right CPU /// </summary> /// <param name="controlInformation"></param> private static void UpdateThreadAffinity(ControlThreadInformation controlInformation) { int id = AppDomain.GetCurrentThreadId(); Process process = Process.GetCurrentProcess(); foreach (ProcessThread processThread in process.Threads) if (processThread.Id == id) { controlInformation.ProcessThread = processThread; //processThread.IdealProcessor = processorAffinity; processThread.ProcessorAffinity = new IntPtr(controlInformation.ThreadAffinity); } }
static RUDPStack() { AppDomain.CurrentDomain.ProcessExit += new EventHandler(CurrentDomain_ProcessExit); _isStackRunning = true; //---- Set to the maximum affinity UpdateAffinity(); //---- Set up the control threads _controlThreadInformations = new ControlThreadInformation[ProcessorsCount]; _controlThreadCPUUsages = new long[ProcessorsCount]; int affinity = 1; for (int index = 0; index < ProcessorsCount; index++) { ControlThreadInformation info = new ControlThreadInformation(); //---- Timers info.ThreadAffinity = affinity; info.ControlThreadId = index; info._protocolControlThread = new Thread(new ParameterizedThreadStart(ControlTimer)); info._protocolControlThread.Name = "RUDP Control Timer - CPU(" + index + ")"; info._protocolControlThread.IsBackground = true; info._protocolControlThread.Priority = ThreadPriority.Normal; _controlThreadInformations[index] = info; info._protocolControlThread.Start(info); affinity = affinity * 2; } }
private static bool RetransmissionTimer(RUDPSocket rudp, long now, ControlThreadInformation controlInformation) { int count = rudp._outgoingPackets.Count; bool hasDoFastRetransmit = false; controlInformation._chargeCheckStopWatch.Reset(); controlInformation._chargeCheckStopWatch.Start(); for (int index = 0; index < count; index++) { rudp._outgoingPacketsLock.EnterReadLock(); RUDPOutgoingPacket packet = rudp._outgoingPackets[index]; rudp._outgoingPacketsLock.ExitReadLock(); //---- Not yet sended if (packet.TSLastSend < 0) continue; //---- It is ACKed if (packet.IsACKed) { rudp._outgoingPacketsLock.EnterWriteLock(); rudp._outgoingPackets.RemoveAt(index); rudp._outgoingPacketsLock.ExitWriteLock(); ReleaseOutgoingPacket(packet); index--; count--; continue; } //---- Check for time out if (packet.TSFirstSend > -1 && (now - packet.TSFirstSend) > rudp._sto) { //-- Normal time out // Send connection Reset with ACK OnDisconnected(rudp, DisconnectionReason.TimeOut); return true; } //---- Retransmission or not ? bool fastRetransmit = (packet.PacketId >= rudp._fastRetransmitStartPacketId && packet.PacketId <= rudp._fastRetransmitEndPacketId); if (!fastRetransmit && (now - packet.TSLastSend) < rudp._rto) continue; hasDoFastRetransmit = hasDoFastRetransmit | fastRetransmit; //---- Get the SACK slots to send with SACKSlot slot1 = null, slot2 = null, slot3 = null, slot4 = null; rudp._sackWindow.GetSLACKSlots(out slot1, out slot2, out slot3, out slot4); // Update the payload for the SACK slots UpdatePacketPayload(packet.Payload, slot1, slot2, slot3, slot4); #if CONSOLE_TRACE string acksList = ""; if (slot1 != null) acksList += " [" + slot1.StartPacketId + " <-> " + slot1.EndPacketId + "]"; if (slot2 != null) acksList += " [" + slot2.StartPacketId + " <-> " + slot2.EndPacketId + "]"; if (slot3 != null) acksList += " [" + slot3.StartPacketId + " <-> " + slot3.EndPacketId + "]"; if (slot4 != null) acksList += " [" + slot4.StartPacketId + " <-> " + slot4.EndPacketId + "]"; #endif // Send #if CONSOLE_TRACE Trace("Resend packet(" + rudp.Handle + "): " + packet.PacketId + " RTO=" + rudp._rto + "RTT=" + rudp._rtt + " ACKs:" + acksList); #endif if (SocketSendPacket(rudp, packet, packet.Payload, now)) { rudp._controlWindow.OnTimeOut(packet); // Update packet.Retransmission++; } if (controlInformation._chargeCheckStopWatch.ElapsedMilliseconds > 0) return false; } //---- Reset fast retransmit if (hasDoFastRetransmit) rudp.OnEndFastRetransmit(); return true; }
/// <summary> /// Send all the fragments. /// </summary> /// <returns>True if all the fragments have been sent</returns> static private bool SendFragments(FragmentInformation fragments, ControlThreadInformation controlInformation) { controlInformation._chargeCheckStopWatch.Reset(); controlInformation._chargeCheckStopWatch.Start(); while (fragments.Size > 0) { //---- MSS : Full header (IP + UDP + RUDP) int MSS = fragments.rudp._mtu - (RUDPStack.UDPHeaderLength + RUDPStack.RUDPHeaderLength); int currentLength = Math.Min(MSS, fragments.Size); //---- If reliable, wait for the congestion windows if (fragments.IsReliable && !fragments.rudp._controlWindow.CanSend(currentLength)) return false; //---- Send if (!PushPacketToSend(fragments.rudp, fragments.IsReliable, RUDPPacketChannel.UserPacket, fragments.Payload, fragments.Offset, currentLength)) { fragments.Error = RUDPSocketError.SocketError; if (fragments.AsyncResult != null) OnSocketUnhandledError(fragments.rudp, fragments.Error, fragments.AsyncResult); return true; } fragments.Size -= currentLength; fragments.Offset += currentLength; //--- Avoid to block the Control thread, because sending a lot of fragments (allow to send ACKs too...) if (fragments.Size > 0 && controlInformation._chargeCheckStopWatch.ElapsedMilliseconds > 0) return false; } //---- End of send if (fragments.AsyncResult != null) fragments.rudp._physical.OnEndSend(fragments.rudp, fragments.AsyncResult); return true; }
private static bool TransmissionTimer(RUDPSocket rudp, ControlThreadInformation controlInformation) { if (rudp._fragments.Count < 1) return true; rudp._fragmentsLock.EnterReadLock(); FragmentInformation fragments = rudp._fragments.Last.Value; rudp._fragmentsLock.ExitReadLock(); if (SendFragments(fragments, controlInformation)) { rudp._fragmentsLock.EnterWriteLock(); rudp._fragments.RemoveLast(); rudp._fragmentsLock.ExitWriteLock(); ReleaseFragmentInformation(fragments); return true; } // Else continue for other socket // will try to send packet during next loop ForceFragmentsSending(controlInformation.ControlThreadId); return false; }
private static void ControlTimerProcessing(ControlThreadInformation controlInformation) { while (Thread.CurrentThread.IsAlive && _isStackRunning) { long now = HiResTimer.MicroSeconds; //---- Check load balancing LoadBalancingTimer(controlInformation, now); //---- Processing bool canResetEvent = true; for (int index = controlInformation._rudpSockets.Count - 1; index > -1; index--) { RUDPSocket rudp = controlInformation._rudpSockets[index]; if (rudp._status == RUDPSocketStatus.Closed) { UnregisterRUDPSocket(rudp); continue; } //-- 0 - Transmission canResetEvent &= TransmissionTimer(rudp, controlInformation); //-- 1 - Send canResetEvent &= RetransmissionTimer(rudp, now, controlInformation); //-- 2 - ACKs ACKTimer(rudp, now); //-- 3 - Check for keep alive KeepAliveTimer(rudp, now); //-- 4 - MTU Discovery rudp._pmtuDiscovery.OnHeartBeat(now); //-- 5 - Bandwidth BandwidthTimer(rudp, now); } //---- Do not use 100% //Thread.Sleep(1); controlInformation._protocolControlEvent.WaitOne(1, true); if (canResetEvent) controlInformation._protocolControlEvent.Reset(); } }