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 // In the new implementation, the partial order relation of time stamps is no longer guaranteed. (In send mode, the partial order relationship does not hold)
73 //
74 // However, since it is a problem for this function to be called in that state (= attempting to send new data before the resend process completes), this code will be left for a little longer.
75 //
76 //
77 // Checks whether the time stamps for the element group recorded in the queue maintains a partial order relation.
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 // Remove function must be revised. Because it already cannot be assumed that the queue is arranged in the time stamp order.
106 //
107 // With this implementation, if the Remove function target is not at the beginning of the queue, the target is not removed.
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("Start of the resend queue was removed. \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 even the start cannot get arrival confirmation with ACK, the following segment surely won't be able to.
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 start element to the end. At this time, the element time stamp is changed.
TryAgain(void)168 void ResendQueue::TryAgain(void)
169 {
170 ASSERT(IsResendMode());
171
172 // Start element to the end. Set 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 first TryAgain function attempt, place mark on element.
179 // Enable resend mode flag.
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; // Data is 100 bytes
218 a.SetData(data, 100);
219 a.SetSeqNumber(SEQ);
220
221 Segment b; // Data is 50 bytes
222 b.SetData(data+100, 50);
223 b.SetSeqNumber(SEQ + 100);
224
225 Segment c; // 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 changes
233
234 r.Remove(SEQ+99);
235 CU_ASSERT(r.m_queue.Size()==1); // Should still be no changes
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 // Hereafter, 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 // Hereafter, checks IsResendMode and TryAgain function
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 point, 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 have had a turn, 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()); // The timeout value for SYN segments is made to be long so it will not become a resend target.
305 SleepCurrentThread(150);
306 CU_ASSERT(r.IsResendRequired());
307 }
308
309 }}} // namespace nn::rdt::CTR
310