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