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