/** * @file llpacketring.cpp * @brief implementation of LLPacketRing class for a packet. * * $LicenseInfo:firstyear=2001&license=viewergpl$ * * Copyright (c) 2001-2008, Linden Research, Inc. * * Second Life Viewer Source Code * The source code in this file ("Source Code") is provided by Linden Lab * to you under the terms of the GNU General Public License, version 2.0 * ("GPL"), unless you have obtained a separate licensing agreement * ("Other License"), formally executed by you and Linden Lab. Terms of * the GPL can be found in doc/GPL-license.txt in this distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2 * * There are special exceptions to the terms and conditions of the GPL as * it is applied to this Source Code. View the full text of the exception * in the file doc/FLOSS-exception.txt in this software distribution, or * online at http://secondlifegrid.net/programs/open_source/licensing/flossexception * * By copying, modifying or distributing this software, you acknowledge * that you have read and understood your obligations described above, * and agree to abide by those obligations. * * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY, * COMPLETENESS OR PERFORMANCE. * $/LicenseInfo$ */ #include "linden_common.h" #include "llpacketring.h" // linden library includes #include "llerror.h" #include "lltimer.h" #include "timing.h" #include "llrand.h" #include "u64.h" /////////////////////////////////////////////////////////// LLPacketRing::LLPacketRing () : mUseInThrottle(FALSE), mUseOutThrottle(FALSE), mInThrottle(256000.f), mOutThrottle(64000.f), mActualBitsIn(0), mActualBitsOut(0), mMaxBufferLength(64000), mInBufferLength(0), mOutBufferLength(0), mDropPercentage(0.0f), mPacketsToDrop(0x0) { } /////////////////////////////////////////////////////////// LLPacketRing::~LLPacketRing () { cleanup(); } /////////////////////////////////////////////////////////// void LLPacketRing::cleanup () { LLPacketBuffer *packetp; while (!mReceiveQueue.empty()) { packetp = mReceiveQueue.front(); delete packetp; mReceiveQueue.pop(); } while (!mSendQueue.empty()) { packetp = mSendQueue.front(); delete packetp; mSendQueue.pop(); } } /////////////////////////////////////////////////////////// void LLPacketRing::dropPackets (U32 num_to_drop) { mPacketsToDrop += num_to_drop; } /////////////////////////////////////////////////////////// void LLPacketRing::setDropPercentage (F32 percent_to_drop) { mDropPercentage = percent_to_drop; } void LLPacketRing::setUseInThrottle(const BOOL use_throttle) { mUseInThrottle = use_throttle; } void LLPacketRing::setUseOutThrottle(const BOOL use_throttle) { mUseOutThrottle = use_throttle; } void LLPacketRing::setInBandwidth(const F32 bps) { mInThrottle.setRate(bps); } void LLPacketRing::setOutBandwidth(const F32 bps) { mOutThrottle.setRate(bps); } /////////////////////////////////////////////////////////// S32 LLPacketRing::receiveFromRing (S32 socket, char *datap) { if (mInThrottle.checkOverflow(0)) { // We don't have enough bandwidth, don't give them a packet. return 0; } LLPacketBuffer *packetp = NULL; if (mReceiveQueue.empty()) { // No packets on the queue, don't give them any. return 0; } S32 packet_size = 0; packetp = mReceiveQueue.front(); mReceiveQueue.pop(); packet_size = packetp->getSize(); if (packetp->getData() != NULL) { memcpy(datap, packetp->getData(), packet_size); /*Flawfinder: ignore*/ } // need to set sender IP/port!! mLastSender = packetp->getHost(); delete packetp; this->mInBufferLength -= packet_size; // Adjust the throttle mInThrottle.throttleOverflow(packet_size * 8.f); return packet_size; } /////////////////////////////////////////////////////////// S32 LLPacketRing::receivePacket (S32 socket, char *datap) { S32 packet_size = 0; // If using the throttle, simulate a limited size input buffer. if (mUseInThrottle) { BOOL done = FALSE; // push any current net packet (if any) onto delay ring while (!done) { LLPacketBuffer *packetp; packetp = new LLPacketBuffer(socket); if (packetp->getSize()) { mActualBitsIn += packetp->getSize() * 8; // Fake packet loss if (mDropPercentage && (ll_frand(100.f) < mDropPercentage)) { mPacketsToDrop++; } if (mPacketsToDrop) { delete packetp; packetp = NULL; packet_size = 0; mPacketsToDrop--; } } // If we faked packet loss, then we don't have a packet // to use for buffer overflow testing if (packetp) { if (mInBufferLength + packetp->getSize() > mMaxBufferLength) { // Toss it. llwarns << "Throwing away packet, overflowing buffer" << llendl; delete packetp; packetp = NULL; } else if (packetp->getSize()) { mReceiveQueue.push(packetp); mInBufferLength += packetp->getSize(); } else { delete packetp; packetp = NULL; done = true; } } else { // No packetp, keep going? - no packetp == faked packet loss } } // Now, grab data off of the receive queue according to our // throttled bandwidth settings. packet_size = receiveFromRing(socket, datap); } else { // no delay, pull straight from net packet_size = receive_packet(socket, datap); mLastSender = ::get_sender(); if (packet_size) // did we actually get a packet? { if (mDropPercentage && (ll_frand(100.f) < mDropPercentage)) { mPacketsToDrop++; } if (mPacketsToDrop) { packet_size = 0; mPacketsToDrop--; } } } return packet_size; } BOOL LLPacketRing::sendPacket(int h_socket, char * send_buffer, S32 buf_size, LLHost host) { BOOL status = TRUE; if (!mUseOutThrottle) { return send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() ); } else { mActualBitsOut += buf_size * 8; LLPacketBuffer *packetp = NULL; // See if we've got enough throttle to send a packet. while (!mOutThrottle.checkOverflow(0.f)) { // While we have enough bandwidth, send a packet from the queue or the current packet S32 packet_size = 0; if (!mSendQueue.empty()) { // Send a packet off of the queue LLPacketBuffer *packetp = mSendQueue.front(); mSendQueue.pop(); mOutBufferLength -= packetp->getSize(); packet_size = packetp->getSize(); status = send_packet(h_socket, packetp->getData(), packet_size, packetp->getHost().getAddress(), packetp->getHost().getPort()); delete packetp; // Update the throttle mOutThrottle.throttleOverflow(packet_size * 8.f); } else { // If the queue's empty, we can just send this packet right away. status = send_packet(h_socket, send_buffer, buf_size, host.getAddress(), host.getPort() ); packet_size = buf_size; // Update the throttle mOutThrottle.throttleOverflow(packet_size * 8.f); // This was the packet we're sending now, there are no other packets // that we need to send return status; } } // We haven't sent the incoming packet, add it to the queue if (mOutBufferLength + buf_size > mMaxBufferLength) { // Nuke this packet, we overflowed the buffer. // Toss it. llwarns << "Throwing away outbound packet, overflowing buffer" << llendl; } else { static LLTimer queue_timer; if ((mOutBufferLength > 4192) && queue_timer.getElapsedTimeF32() > 1.f) { // Add it to the queue llinfos << "Outbound packet queue " << mOutBufferLength << " bytes" << llendl; queue_timer.reset(); } packetp = new LLPacketBuffer(host, send_buffer, buf_size); mOutBufferLength += packetp->getSize(); mSendQueue.push(packetp); } } return status; }