1 /*--------------------------------------------------------------------------
2   Project:  HorizonSDK
3   File:     rdt_ResendQueue.cpp
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 #include "rdt_ResendQueue.h"
21 
22 #include <string.h>
23 
24 #include "Test.h"
25 
26 #include "rdt_Utility.h"
27 
28 
29 namespace
30 {
31 
32 }  // end of anonymous namespace
33 
34 namespace nn { namespace rdt { namespace CTR {
35 
36 
ResendQueue(void)37 ResendQueue::ResendQueue(void)
38      :m_bResendMode(false)
39 {
40     Clear();
41 }
42 
43 
~ResendQueue(void)44 ResendQueue::~ResendQueue(void)
45 {
46 }
47 
48 
Push(const Segment & seg)49 bool ResendQueue::Push(const Segment &seg)
50 {
51     if(m_queue.IsFull())
52     {
53         return false;
54     }
55 
56     // TORIAEZU
57     SegmentWithTime swt;
58     swt.segment   = seg;
59     swt.timeStamp = nn::rdt::CTR::GetCurrentTimeAsMillisecond();
60 
61     // データセグメントと違い、SYNセグメントはハイペースで送る必要はない。
62     if(seg.IsSyn())
63     {
64         swt.timeOut = SYN_SEGMENT_TIMEOUT;
65     }
66     else
67     {
68         swt.timeOut = m_defaultTimeout;
69     }
70 
71 
72     if(!m_queue.IsEmpty())
73     {
74         // 新しい実装では、タイムスタンプの半順序関係は
75         // もはや保証できなくなっています(再送モードでは、半順序関係が崩れる)
76         // しかし、その状態でこの関数が呼び出される(=再送処理が終わっていないのに、
77         // 新しいデータを送信しようとしている)のは問題であるので、このコードは
78         // もう少し残しておきます。
79         // キューに記録されている要素群のタイムスタンプが半順序関係を
80         // 保っているかどうかをチェックします。
81         ASSERT(swt.timeStamp >= m_queue.Back().timeStamp);
82     }
83 
84     m_queue.PushBack(swt);
85 
86     return true;
87 }
88 
89 
Front(Segment * pSeg) const90 bool ResendQueue::Front(Segment *pSeg) const
91 {
92     ASSERT(pSeg!=NULL);
93 
94     if(m_queue.IsEmpty())
95     {
96         return false;
97     }
98     else
99     {
100         *pSeg = m_queue.Front().segment;
101         return true;
102     }
103 }
104 
105 
106 // TORIAEZU
107 // Remove()については見直しが必要。もはやキューがタイムスタンプの順に並ぶことは
108 // 仮定できなくなってしまっているから。
109 // この実装だと、Remove()対象がキューの先頭以外にある場合に、対象が除去されない。
Remove(u32 ack)110 void ResendQueue::Remove(u32 ack)
111 {
112     while(!m_queue.IsEmpty())
113     {
114         const Segment &seg = m_queue.Front().segment;
115         u32 lastSeq = seg.GetLastSeqNumber();
116 
117         if(0 < static_cast<s32>(ack) - static_cast<s32>(lastSeq))
118         {
119             m_queue.PopFront();
120             VERBOSE("再送キューの先頭を除去しました。\n");
121             if(m_queue.IsEmpty())
122             {
123                 // 再送キューをカラッポにしてしまったのならば、再送モードは終了。
124                 m_bResendMode = false;
125             }
126         }
127         else
128         {
129             // 先頭でさえACKによる到達確認が取れないのであれば、
130             // 後続のセグメントは言うに及ばす。ループを抜ける。
131             break;
132         }
133     }
134 }
135 
136 
IsResendRequired(void) const137 bool ResendQueue::IsResendRequired(void) const
138 {
139     if(m_queue.IsEmpty())
140     {
141         return false;
142     }
143     else
144     {
145         return m_queue.Front().IsTimeOut();
146     }
147 }
148 
149 
GetTotalDataSize(void) const150 u32 ResendQueue::GetTotalDataSize(void) const
151 {
152     u32 ret = 0;
153     for(size_t i= 0; i<m_queue.Size(); ++i)
154     {
155         ret += m_queue.At(i).segment.GetDataLength();
156     }
157 
158     return ret;
159 }
160 
161 
Clear(void)162 void ResendQueue::Clear(void)
163 {
164     m_queue.Clear();
165     m_defaultTimeout = DEFAULT_TIMEOUT;
166 }
167 
168 
169 // 先頭要素を後ろに。このとき、要素のタイムスタンプは変更される。
TryAgain(void)170 void ResendQueue::TryAgain(void)
171 {
172     ASSERT(IsResendMode());
173 
174     // 先頭要素を後ろへ。再送モードフラグもセット。
175     struct SegmentWithTime s = m_queue.Front();
176     m_queue.PopFront();
177     s.timeStamp = nn::rdt::CTR::GetCurrentTimeAsMillisecond();
178     if(!m_bResendMode)
179     {
180         // 初めてのTryAgain()だったら、要素にマークを付ける。
181         // 再送モードフラグも立てる。
182         s.resendMark = true;
183         m_bResendMode = true;
184     }
185     m_queue.PushBack(s);
186 
187     if(m_queue.Front().resendMark)
188     {
189         // TryAgain()を何度も呼び出すことで、一巡した!
190         // 再送モードフラグは落とす
191         m_queue.Front().resendMark = false;
192         m_bResendMode = false;
193     }
194 }
195 
196 
IsResendMode(void) const197 bool ResendQueue::IsResendMode(void) const
198 {
199     return m_bResendMode || IsResendRequired();
200 }
201 
202 
PrintDebugInfo(void) const203 void ResendQueue::PrintDebugInfo(void) const
204 {
205     LOG("Number of ResendQueue elements: %d, total data size is %d bytes.\n", m_queue.Size(), GetTotalDataSize());
206 }
207 
208 
Test(void)209 void ResendQueue::Test(void)
210 {
211     char data[100 + 50 + 30];
212     memset(data, 0, 100 + 50 + 30);
213 
214     ResendQueue r;
215     r.SetDefaultTimeout(500);  // 500ミリ秒の再送タイムアウト
216 
217     const u32 SEQ = 5000;
218 
219     Segment a;  // データは100バイトとする
220     a.SetData(data, 100);
221     a.SetSeqNumber(SEQ);
222 
223     Segment b;  // データは50バイトとする
224     b.SetData(data+100, 50);
225     b.SetSeqNumber(SEQ + 100);
226 
227     Segment c;  // データは30バイトとする
228     c.SetData(data+100+50, 30);
229     c.SetSeqNumber(SEQ + 100 + 50);
230 
231     CU_ASSERT(r.Push(a));
232     CU_ASSERT(r.m_queue.Size()==1);
233     r.Remove(SEQ);
234     CU_ASSERT(r.m_queue.Size()==1);  // 変化はないはず
235 
236     r.Remove(SEQ+99);
237     CU_ASSERT(r.m_queue.Size()==1);  // まだ変化はないはず
238 
239     r.Remove(SEQ+100);
240     CU_ASSERT(r.m_queue.Size()==0);  // 取り除かれるはず
241 
242     CU_ASSERT(r.Push(b));
243     CU_ASSERT(r.Push(c));
244     CU_ASSERT(r.m_queue.Size()==2);
245 
246     r.Remove(SEQ+101);
247     CU_ASSERT(r.m_queue.Size()==2);
248 
249     r.Remove(SEQ+149);
250     CU_ASSERT(r.m_queue.Size()==2);
251 
252     r.Remove(SEQ+180);
253     CU_ASSERT(r.m_queue.Size()==0);
254 
255     // 以降、タイムアウトのチェック
256 
257     r.SetDefaultTimeout(500);
258     Segment d;
259     CU_ASSERT(r.Push(a));
260     CU_ASSERT(r.m_queue.Size()==1);
261     CU_ASSERT(!r.IsResendRequired());
262     SleepCurrentThread(200);
263     CU_ASSERT(!r.IsResendRequired());
264     SleepCurrentThread(500);
265     CU_ASSERT(r.IsResendRequired());
266     CU_ASSERT(r.IsResendRequired());
267     CU_ASSERT(r.m_queue.Size()==1);
268 
269     CU_ASSERT(r.Push(a));
270     SleepCurrentThread(300);
271     CU_ASSERT(r.Push(b));
272     SleepCurrentThread(300);
273     CU_ASSERT(r.m_queue.Size()==3);
274     CU_ASSERT(r.IsResendRequired());
275     r.Remove(SEQ+100);
276     CU_ASSERT(!r.IsResendRequired());
277     CU_ASSERT(r.m_queue.Size()==1);
278 
279     // 以降、IsResendMode()、TryAgain()のチェック。
280     Segment e, f, g;
281     r.Clear();
282     r.SetDefaultTimeout(500);
283     CU_ASSERT(r.m_queue.Size()==0);
284     CU_ASSERT(!r.IsResendMode());
285     CU_ASSERT(r.Push(e));
286     SleepCurrentThread(400);
287     CU_ASSERT(r.Push(f));
288     CU_ASSERT(r.Push(g));
289     SleepCurrentThread(200);  // この時点で、eはタイムアウトになっている。
290     CU_ASSERT(r.IsResendMode());  // 1個、タイムアウトの要素があるので、再送モードになっていなければならない。
291     r.TryAgain();
292     CU_ASSERT(r.IsResendMode());
293     r.TryAgain();
294     CU_ASSERT(r.IsResendMode());
295     r.TryAgain();
296     CU_ASSERT(!r.IsResendMode()); // 全要素が一巡したので、再送モードからは外れるはず。
297     CU_ASSERT(r.m_queue.Size()==3);
298 
299     // SYNセグメントのチェック
300     r.Clear();
301     Segment s;
302     s.SetSyn();
303     r.SetDefaultTimeout(10);
304     r.Push(s);
305     SleepCurrentThread(150);
306     CU_ASSERT(!r.IsResendRequired());  // SYNセグメントのタイムアウト値は長めにしてあるので、まだ再送対象にはならない。
307     SleepCurrentThread(150);
308     CU_ASSERT(r.IsResendRequired());
309 }
310 
311 }}} // namespace nn::rdt::CTR
312