1 /*--------------------------------------------------------------------------
2 Project: HorizonSDK
3 File: rdt_ResendQueue.cpp
4 Copyright 2009 Nintendo. All rights reserved.
5 These coded instructions, statements, and computer programs contain
6 proprietary information of Nintendo of America Inc. and/or Nintendo
7 Company Ltd., and are protected by Federal copyright law. They may
8 not be disclosed to third parties or copied or duplicated in any form,
9 in whole or in part, without the prior written consent of Nintendo.
10 $Date:: 2010-09-16#$
11 $Rev: 26004 $
12 $Author: hiratsu_daisuke $
13 *-------------------------------------------------------------------------
14
15
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 // Unlike data segments, SYN segments do not need to be sent at a high pace.
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 // 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)
75 //
76 // 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.
77 //
78 //
79 // Checks whether the time stamps for the element group recorded in the queue maintains a partial order relation.
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 //
107 // Remove function must be revised. Because it already cannot be assumed that the queue is arranged in the time stamp order.
108 //
109 // With this implementation, if the Remove function target is not at the beginning of the queue, the target is not removed.
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("Start of the resend queue was removed. \n");
121 if(m_queue.IsEmpty())
122 {
123 // Resend mode ends if the resend queue is empty.
124 m_bResendMode = false;
125 }
126 }
127 else
128 {
129 // If even the start cannot get arrival confirmation with ACK, the following segment surely won't be able to.
130 // Leave loop.
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 // Move start element to the end. At this time, the element time stamp is changed.
TryAgain(void)170 void ResendQueue::TryAgain(void)
171 {
172 ASSERT(IsResendMode());
173
174 // Start element to the end. Set resend mode flag.
175 struct SegmentWithTime s = m_queue.Front();
176 m_queue.PopFront();
177 s.timeStamp = nn::rdt::CTR::GetCurrentTimeAsMillisecond();
178 if(!m_bResendMode)
179 {
180 // If first TryAgain function attempt, place mark on element.
181 // Enable resend mode flag.
182 s.resendMark = true;
183 m_bResendMode = true;
184 }
185 m_queue.PushBack(s);
186
187 if(m_queue.Front().resendMark)
188 {
189 // By calling TryAgain function several times, one cycle is complete!
190 // Disable the resend mode flag
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-millisecond resend timeout
216
217 const u32 SEQ = 5000;
218
219 Segment a; // Data is 100 bytes
220 a.SetData(data, 100);
221 a.SetSeqNumber(SEQ);
222
223 Segment b; // Data is 50 bytes
224 b.SetData(data+100, 50);
225 b.SetSeqNumber(SEQ + 100);
226
227 Segment c; // Data is 30 bytes
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); // Should be no changes
235
236 r.Remove(SEQ+99);
237 CU_ASSERT(r.m_queue.Size()==1); // Should still be no changes
238
239 r.Remove(SEQ+100);
240 CU_ASSERT(r.m_queue.Size()==0); // Should be removed
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 // Hereafter, timeout check
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 // Hereafter, checks IsResendMode and TryAgain function
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); // At this point, e is in timeout.
290 CU_ASSERT(r.IsResendMode()); // Since there is one timeout element, must be in resend mode.
291 r.TryAgain();
292 CU_ASSERT(r.IsResendMode());
293 r.TryAgain();
294 CU_ASSERT(r.IsResendMode());
295 r.TryAgain();
296 CU_ASSERT(!r.IsResendMode()); // Since all elements have had a turn, should exit from resend mode.
297 CU_ASSERT(r.m_queue.Size()==3);
298
299 // Check SYN segment
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()); // The timeout value for SYN segments is made to be long so it will not become a resend target.
307 SleepCurrentThread(150);
308 CU_ASSERT(r.IsResendRequired());
309 }
310
311 }}} // namespace nn::rdt::CTR
312