1 /*---------------------------------------------------------------------------*
2 Project: Horizon
3 File: rdt_ResendQueue.cpp
4
5 Copyright (C)2009-2012 Nintendo Co., Ltd. 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 $Rev: 46347 $
14 *---------------------------------------------------------------------------*/
15
16 #include "stdafx.h"
17
18 #include "rdt_ResendQueue.h"
19
20 #include <string.h>
21
22 #include "Test.h"
23
24 #include "rdt_Utility.h"
25
26
27 namespace
28 {
29
30 } // End of anonymous namespace
31
32 namespace nn { namespace rdt { namespace CTR {
33
34
ResendQueue(void)35 ResendQueue::ResendQueue(void)
36 :m_bResendMode(false)
37 {
38 Clear();
39 }
40
41
~ResendQueue(void)42 ResendQueue::~ResendQueue(void)
43 {
44 }
45
46
Push(const Segment & seg)47 bool ResendQueue::Push(const Segment &seg)
48 {
49 if(m_queue.IsFull())
50 {
51 return false;
52 }
53
54 // TORIAEZU
55 SegmentWithTime swt;
56 swt.segment = seg;
57 swt.timeStamp = nn::rdt::CTR::GetCurrentTimeAsMillisecond();
58
59 // Unlike data segments, SYN segments do not need to be sent at a high pace.
60 if(seg.IsSyn())
61 {
62 swt.timeOut = SYN_SEGMENT_TIMEOUT;
63 }
64 else
65 {
66 swt.timeOut = m_defaultTimeout;
67 }
68
69
70 if(!m_queue.IsEmpty())
71 {
72 // With the new implementation, a partially ordered relationship for timestamps is no longer guaranteed.
73 // (With resend mode, the partially ordered relationship falls apart.)
74 // But, because it is a problem to call this function in this state (that is, trying to send new data before the resend process is finished), this code will be left for a while longer.
75 //
76 //
77 // Checks whether the timestamps of the element group registered in the queue have a partially ordered relationship.
78 //
79 ASSERT(swt.timeStamp >= m_queue.Back().timeStamp);
80 }
81
82 m_queue.PushBack(swt);
83
84 return true;
85 }
86
87
Front(Segment * pSeg) const88 bool ResendQueue::Front(Segment *pSeg) const
89 {
90 ASSERT(pSeg!=NULL);
91
92 if(m_queue.IsEmpty())
93 {
94 return false;
95 }
96 else
97 {
98 *pSeg = m_queue.Front().segment;
99 return true;
100 }
101 }
102
103
104 //
105 // The Remove function must be reviewed because it cannot be assumed that the queue is arranged in timestamp order.
106 //
107 // With this implementation, if the Remove function target is not at the start of the queue, the target is not deleted.
Remove(u32 ack)108 void ResendQueue::Remove(u32 ack)
109 {
110 while(!m_queue.IsEmpty())
111 {
112 const Segment &seg = m_queue.Front().segment;
113 u32 lastSeq = seg.GetLastSeqNumber();
114
115 if(0 < static_cast<s32>(ack) - static_cast<s32>(lastSeq))
116 {
117 m_queue.PopFront();
118 VERBOSE("The start of the resend queue was deleted. \n");
119 if(m_queue.IsEmpty())
120 {
121 // Resend mode ends if the resend queue is empty.
122 m_bResendMode = false;
123 }
124 }
125 else
126 {
127 // If an arrival confirmation by ACK cannot be acquired even at the start, the same goes without saying for subsequent segments.
128 // Leave loop.
129 break;
130 }
131 }
132 }
133
134
IsResendRequired(void) const135 bool ResendQueue::IsResendRequired(void) const
136 {
137 if(m_queue.IsEmpty())
138 {
139 return false;
140 }
141 else
142 {
143 return m_queue.Front().IsTimeOut();
144 }
145 }
146
147
GetTotalDataSize(void) const148 u32 ResendQueue::GetTotalDataSize(void) const
149 {
150 u32 ret = 0;
151 for(size_t i= 0; i<m_queue.Size(); ++i)
152 {
153 ret += m_queue.At(i).segment.GetDataLength();
154 }
155
156 return ret;
157 }
158
159
Clear(void)160 void ResendQueue::Clear(void)
161 {
162 m_queue.Clear();
163 m_defaultTimeout = DEFAULT_TIMEOUT;
164 }
165
166
167 // Move the start element to the end. At this time, the element's timestamp is changed.
TryAgain(void)168 void ResendQueue::TryAgain(void)
169 {
170 ASSERT(IsResendMode());
171
172 // Move the start element to the end. Set the resend mode flag.
173 struct SegmentWithTime s = m_queue.Front();
174 m_queue.PopFront();
175 s.timeStamp = nn::rdt::CTR::GetCurrentTimeAsMillisecond();
176 if(!m_bResendMode)
177 {
178 // If this is the first TryAgain function, set a mark in the element.
179 // Resend mode flag also enabled.
180 s.resendMark = true;
181 m_bResendMode = true;
182 }
183 m_queue.PushBack(s);
184
185 if(m_queue.Front().resendMark)
186 {
187 // By calling TryAgain function several times, one cycle is complete!
188 // Disable the resend mode flag
189 m_queue.Front().resendMark = false;
190 m_bResendMode = false;
191 }
192 }
193
194
IsResendMode(void) const195 bool ResendQueue::IsResendMode(void) const
196 {
197 return m_bResendMode || IsResendRequired();
198 }
199
200
PrintDebugInfo(void) const201 void ResendQueue::PrintDebugInfo(void) const
202 {
203 LOG("Number of ResendQueue elements: %d, total data size is %d bytes.\n", m_queue.Size(), GetTotalDataSize());
204 }
205
206
Test(void)207 void ResendQueue::Test(void)
208 {
209 char data[100 + 50 + 30];
210 memset(data, 0, 100 + 50 + 30);
211
212 ResendQueue r;
213 r.SetDefaultTimeout(500); // 500-millisecond resend timeout
214
215 const u32 SEQ = 5000;
216
217 Segment a; // The data is 100 bytes.
218 a.SetData(data, 100);
219 a.SetSeqNumber(SEQ);
220
221 Segment b; // The data is 50 bytes.
222 b.SetData(data+100, 50);
223 b.SetSeqNumber(SEQ + 100);
224
225 Segment c; // The data is 30 bytes.
226 c.SetData(data+100+50, 30);
227 c.SetSeqNumber(SEQ + 100 + 50);
228
229 CU_ASSERT(r.Push(a));
230 CU_ASSERT(r.m_queue.Size()==1);
231 r.Remove(SEQ);
232 CU_ASSERT(r.m_queue.Size()==1); // Should be no change.
233
234 r.Remove(SEQ+99);
235 CU_ASSERT(r.m_queue.Size()==1); // Still should be no change.
236
237 r.Remove(SEQ+100);
238 CU_ASSERT(r.m_queue.Size()==0); // Should be removed.
239
240 CU_ASSERT(r.Push(b));
241 CU_ASSERT(r.Push(c));
242 CU_ASSERT(r.m_queue.Size()==2);
243
244 r.Remove(SEQ+101);
245 CU_ASSERT(r.m_queue.Size()==2);
246
247 r.Remove(SEQ+149);
248 CU_ASSERT(r.m_queue.Size()==2);
249
250 r.Remove(SEQ+180);
251 CU_ASSERT(r.m_queue.Size()==0);
252
253 // Timeout check.
254
255 r.SetDefaultTimeout(500);
256 Segment d;
257 CU_ASSERT(r.Push(a));
258 CU_ASSERT(r.m_queue.Size()==1);
259 CU_ASSERT(!r.IsResendRequired());
260 SleepCurrentThread(200);
261 CU_ASSERT(!r.IsResendRequired());
262 SleepCurrentThread(500);
263 CU_ASSERT(r.IsResendRequired());
264 CU_ASSERT(r.IsResendRequired());
265 CU_ASSERT(r.m_queue.Size()==1);
266
267 CU_ASSERT(r.Push(a));
268 SleepCurrentThread(300);
269 CU_ASSERT(r.Push(b));
270 SleepCurrentThread(300);
271 CU_ASSERT(r.m_queue.Size()==3);
272 CU_ASSERT(r.IsResendRequired());
273 r.Remove(SEQ+100);
274 CU_ASSERT(!r.IsResendRequired());
275 CU_ASSERT(r.m_queue.Size()==1);
276
277 // Check the IsResendMode and TryAgain functions.
278 Segment e, f, g;
279 r.Clear();
280 r.SetDefaultTimeout(500);
281 CU_ASSERT(r.m_queue.Size()==0);
282 CU_ASSERT(!r.IsResendMode());
283 CU_ASSERT(r.Push(e));
284 SleepCurrentThread(400);
285 CU_ASSERT(r.Push(f));
286 CU_ASSERT(r.Push(g));
287 SleepCurrentThread(200); // At this time, e is in timeout.
288 CU_ASSERT(r.IsResendMode()); // Since there is one timeout element, must be in resend mode.
289 r.TryAgain();
290 CU_ASSERT(r.IsResendMode());
291 r.TryAgain();
292 CU_ASSERT(r.IsResendMode());
293 r.TryAgain();
294 CU_ASSERT(!r.IsResendMode()); // Since all elements were cycled, should exit from resend mode.
295 CU_ASSERT(r.m_queue.Size()==3);
296
297 // Check SYN segment.
298 r.Clear();
299 Segment s;
300 s.SetSyn();
301 r.SetDefaultTimeout(10);
302 r.Push(s);
303 SleepCurrentThread(150);
304 CU_ASSERT(!r.IsResendRequired()); // Because the SYN segment timeout value is set to be long, it is not a resend target yet.
305 SleepCurrentThread(150);
306 CU_ASSERT(r.IsResendRequired());
307 }
308
309 }}} // namespace nn::rdt::CTR
310