1 /*--------------------------------------------------------------------------
2   Project:  HorizonSDK
3   File:     rdt_ResendQueue.h
4 
5   Copyright 2009 Nintendo.  All rights reserved.
6 
7   These coded instructions, statements, and computer programs contain
8   proprietary information of Nintendo of America Inc. and/or Nintendo
9   Company Ltd., and are protected by Federal copyright law.  They may
10   not be disclosed to third parties or copied or duplicated in any form,
11   in whole or in part, without the prior written consent of Nintendo.
12 
13   $Date:: 2010-09-16#$
14   $Rev: 26004 $
15   $Author: hiratsu_daisuke $
16  *-------------------------------------------------------------------------*/
17 
18 #include "stdafx.h"
19 
20 #ifndef NN_RDT_RESENDQUEUE_H_
21 #define NN_RDT_RESENDQUEUE_H_
22 
23 #include "rdt_Deque.h"
24 
25 #include "rdt_Segment.h"
26 
27 #include "rdt_Utility.h"
28 
29 
30 namespace nn { namespace rdt { namespace CTR {
31 
32 /*!
33     @brief これは再送キューのクラスです。
34     再送キューには、リモートに向けて送信したセグメントのコピーが詰められ、
35     送信時の時刻が記録されます。
36     ただし、このクラス自身は再送処理を行いません。
37 */
38 
39 class ResendQueue{
40 public:
41 /*!
42     @brief 初期化します。
43 */
44     ResendQueue(void);
45 
46 /*!
47     @brief 解放します。
48 */
49    ~ResendQueue(void);
50 
51 /*!
52   @brief             セグメントを再送キューに詰め込みます。
53                      キューの深さをオーバーしてしまうような場合にはfalseが返ります。
54                      詰め込んだセグメントは、この関数呼び出し時点での時刻が関連づけられます。
55   @param[in]  seg    送信したセグメント。
56   @return            Push操作が成功すればtrue、キューが満杯だった等の理由で失敗したときはfalse。
57 */
58     bool Push(const Segment &seg);
59 
60 /*!
61   @brief 時刻に関係なく、キューの先頭要素を返します。
62          キューがカラッポであればfalseが返ります。
63 */
64     bool Front(Segment *pSeg) const;
65 
66 /*!
67   @brief ACKによって、リモート側に届いたことが確認できたセグメントを再送キューから除去します。
68          ack引数には、リモートから届いたセグメントに記録されていたACK番号をそのまま渡します。
69 */
70     void Remove(u32 ack);
71 
72 /*!
73   @brief デフォルト(通常のデータセグメント)のタイムアウト時間を設定します。(単位はミリ秒)
74 */
SetDefaultTimeout(MSEC_T msec)75     void SetDefaultTimeout(MSEC_T msec) { m_defaultTimeout = msec; }
76 
77 /*!
78   @brief キューに詰め込んでから、一定時刻が経過してしまっているセグメントがあれば、
79          trueを返します。さもなければfalse。
80 */
81     bool IsResendRequired(void) const;
82 
83 /*!
84   @brief キューに詰めこまれたセグメントに含まれる、実データ部分の
85          合計を返します。すなわち、到着したかどうか定かでない
86          データのサイズが求められます。
87 */
88     u32 GetTotalDataSize(void) const;
89 
90 /*!
91   @brief 再送キューを空っぽにし、まっさらな状態に戻します。
92 */
93     void Clear(void);
94 
95 
96 /*!
97   @brief 先頭の要素を最後尾に持ってきます。
98          全要素についてTryAgain()し終えると、IsResendMode()状態は解除されます。
99   */
100     void TryAgain(void);
101 
102 /*!
103   @brief キューに含まれる全要素を再送する必要があるモードであるかどうかを判定します。
104          全要素についてTryAgain()し終えると、IsResendMode()状態は解除されます。
105          タイムアウトを迎えたセグメントがキューに見つかったか、
106          あるいはいったんそういうセグメントが見つかり、残りの
107          セグメントを送り終えるのを待っている状態か。
108  */
109     bool IsResendMode(void) const;
110 
111 /*!
112   @brief 再送キューが満杯であるかどうかを判定します。
113          この関数がtrueを返す状況では、それ以上Pushすることは
114          できません。
115  */
IsFull(void)116     bool IsFull(void) const { return m_queue.IsFull(); }
117 
118 /*!
119     @brief デバッグ用の関数です。
120 */
121     void PrintDebugInfo(void) const;
122 
123 /*!
124     @brief CUnitを用いた単体テストです。
125 */
126     static void Test(void);
127 
128 private:
129 /*!
130     @brief コピーコンストラクタは封印します。
131 */
132     ResendQueue           (const ResendQueue&);
133 
134 /*!
135     @brief 代入演算子は封印します。
136 */
137     ResendQueue& operator=(const ResendQueue&);
138 
139     // キューのキャパシティは、受信側から通知されるウィンドウサイズよりも
140     // 大きくないと、キューが満杯になる可能性がある。
141     // RDTのペイロードを1400バイトとすると、16 * 1400 = 22400バイトなので、
142     // 受信側が受信バッファ(ウインドウサイズ)をこれより大きく設定したり
143     // すると、送信側のキューが詰まる可能性が生まれる。
144     static const size_t DEPTH = 16;
145 
146     // TODO: CTR実機で要調整のパラメータ。スループットに重大な影響を及ぼす。
147     // 以前はこの数値が500だった。このとき、256KBのデータ送受信に35秒もかかっていた。
148 //    static const MSEC_T DEFAULT_TIMEOUT = 100;
149     static const MSEC_T DEFAULT_TIMEOUT = 50;  // 150 ~ 180KB
150 
151     // SYNを含むセグメントのタイムアウト値。データセグメントと
152     // 同様の速いペースで再送していると、接続がハーフオープン状態に
153     // なってしまったりするバグを回避するために用意。
154     static const MSEC_T SYN_SEGMENT_TIMEOUT = 200;
155 
156     bool checkTimeStamp(void) const;
157 
158     struct SegmentWithTime
159     {
SegmentWithTimeSegmentWithTime160         SegmentWithTime(void) : timeStamp(0), timeOut(0), resendMark(false) {}
IsTimeOutSegmentWithTime161         bool IsTimeOut(void) const { return (timeStamp + timeOut < GetCurrentTimeAsMillisecond()); }
162 
163         Segment segment;
164         MSEC_T timeStamp;  // 送信時の時刻
165         MSEC_T timeOut;    // ここで指定された時間が経過したなら、再送対象となる。
166         bool resendMark;   // 再送モードに入った瞬間の要素に対してマーキングされる。
167         u8   padding[7];   // パディング
168     };
169 
170     MSEC_T                        m_defaultTimeout; // タイムアウト時間(ミリ秒)
171     Deque<SegmentWithTime, DEPTH> m_queue;          // 再送キュー
172 
173     bool                          m_bResendMode;    // 再送モードフラグ
174     u8                            m_padding[7];
175 };
176 
177 
178 }}} // namespace nn::rdt::CTR
179 
180 #endif  // end of NN_RDT_RESENDQUEUE_H_
181