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