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