/*-------------------------------------------------------------------------- Project: HorizonSDK File: rdt_ResendQueue.h Copyright 2009 Nintendo. All rights reserved. These coded instructions, statements, and computer programs contain proprietary information of Nintendo of America Inc. and/or Nintendo Company Ltd., and are protected by Federal copyright law. They may not be disclosed to third parties or copied or duplicated in any form, in whole or in part, without the prior written consent of Nintendo. $Date:: 2010-09-16#$ $Rev: 26004 $ $Author: hiratsu_daisuke $ *-------------------------------------------------------------------------*/ #include "stdafx.h" #ifndef NN_RDT_RESENDQUEUE_H_ #define NN_RDT_RESENDQUEUE_H_ #include "rdt_Deque.h" #include "rdt_Segment.h" #include "rdt_Utility.h" namespace nn { namespace rdt { namespace CTR { /*! @brief これは再送キューのクラスです。 再送キューには、リモートに向けて送信したセグメントのコピーが詰められ、 送信時の時刻が記録されます。 ただし、このクラス自身は再送処理を行いません。 */ class ResendQueue{ public: /*! @brief 初期化します。 */ ResendQueue(void); /*! @brief 解放します。 */ ~ResendQueue(void); /*! @brief セグメントを再送キューに詰め込みます。 キューの深さをオーバーしてしまうような場合にはfalseが返ります。 詰め込んだセグメントは、この関数呼び出し時点での時刻が関連づけられます。 @param[in] seg 送信したセグメント。 @return Push操作が成功すればtrue、キューが満杯だった等の理由で失敗したときはfalse。 */ bool Push(const Segment &seg); /*! @brief 時刻に関係なく、キューの先頭要素を返します。 キューがカラッポであればfalseが返ります。 */ bool Front(Segment *pSeg) const; /*! @brief ACKによって、リモート側に届いたことが確認できたセグメントを再送キューから除去します。 ack引数には、リモートから届いたセグメントに記録されていたACK番号をそのまま渡します。 */ void Remove(u32 ack); /*! @brief デフォルト(通常のデータセグメント)のタイムアウト時間を設定します。(単位はミリ秒) */ void SetDefaultTimeout(MSEC_T msec) { m_defaultTimeout = msec; } /*! @brief キューに詰め込んでから、一定時刻が経過してしまっているセグメントがあれば、 trueを返します。さもなければfalse。 */ bool IsResendRequired(void) const; /*! @brief キューに詰めこまれたセグメントに含まれる、実データ部分の 合計を返します。すなわち、到着したかどうか定かでない データのサイズが求められます。 */ u32 GetTotalDataSize(void) const; /*! @brief 再送キューを空っぽにし、まっさらな状態に戻します。 */ void Clear(void); /*! @brief 先頭の要素を最後尾に持ってきます。 全要素についてTryAgain()し終えると、IsResendMode()状態は解除されます。 */ void TryAgain(void); /*! @brief キューに含まれる全要素を再送する必要があるモードであるかどうかを判定します。 全要素についてTryAgain()し終えると、IsResendMode()状態は解除されます。 タイムアウトを迎えたセグメントがキューに見つかったか、 あるいはいったんそういうセグメントが見つかり、残りの セグメントを送り終えるのを待っている状態か。 */ bool IsResendMode(void) const; /*! @brief 再送キューが満杯であるかどうかを判定します。 この関数がtrueを返す状況では、それ以上Pushすることは できません。 */ bool IsFull(void) const { return m_queue.IsFull(); } /*! @brief デバッグ用の関数です。 */ void PrintDebugInfo(void) const; /*! @brief CUnitを用いた単体テストです。 */ static void Test(void); private: /*! @brief コピーコンストラクタは封印します。 */ ResendQueue (const ResendQueue&); /*! @brief 代入演算子は封印します。 */ ResendQueue& operator=(const ResendQueue&); // キューのキャパシティは、受信側から通知されるウィンドウサイズよりも // 大きくないと、キューが満杯になる可能性がある。 // RDTのペイロードを1400バイトとすると、16 * 1400 = 22400バイトなので、 // 受信側が受信バッファ(ウインドウサイズ)をこれより大きく設定したり // すると、送信側のキューが詰まる可能性が生まれる。 static const size_t DEPTH = 16; // TODO: CTR実機で要調整のパラメータ。スループットに重大な影響を及ぼす。 // 以前はこの数値が500だった。このとき、256KBのデータ送受信に35秒もかかっていた。 // static const MSEC_T DEFAULT_TIMEOUT = 100; static const MSEC_T DEFAULT_TIMEOUT = 50; // 150 ~ 180KB // SYNを含むセグメントのタイムアウト値。データセグメントと // 同様の速いペースで再送していると、接続がハーフオープン状態に // なってしまったりするバグを回避するために用意。 static const MSEC_T SYN_SEGMENT_TIMEOUT = 200; bool checkTimeStamp(void) const; struct SegmentWithTime { SegmentWithTime(void) : timeStamp(0), timeOut(0), resendMark(false) {} bool IsTimeOut(void) const { return (timeStamp + timeOut < GetCurrentTimeAsMillisecond()); } Segment segment; MSEC_T timeStamp; // 送信時の時刻 MSEC_T timeOut; // ここで指定された時間が経過したなら、再送対象となる。 bool resendMark; // 再送モードに入った瞬間の要素に対してマーキングされる。 u8 padding[7]; // パディング }; MSEC_T m_defaultTimeout; // タイムアウト時間(ミリ秒) Deque m_queue; // 再送キュー bool m_bResendMode; // 再送モードフラグ u8 m_padding[7]; }; }}} // namespace nn::rdt::CTR #endif // end of NN_RDT_RESENDQUEUE_H_