RESET/ACK code review update.
[platform/upstream/iotivity.git] / resource / csdk / connectivity / src / caretransmission.c
1 /******************************************************************
2  *
3  * Copyright 2014 Samsung Electronics All Rights Reserved.
4  *
5  *
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  ******************************************************************/
20
21 // Defining _BSD_SOURCE or _DEFAULT_SOURCE causes header files to expose
22 // definitions that may otherwise be skipped. Skipping can cause implicit
23 // declaration warnings and/or bugs and subtle problems in code execution.
24 // For glibc information on feature test macros,
25 // Refer http://www.gnu.org/software/libc/manual/html_node/Feature-Test-Macros.html
26 //
27 // This file requires #define use due to random()
28 // For details on compatibility and glibc support,
29 // Refer http://www.gnu.org/software/libc/manual/html_node/BSD-Random.html
30 #define _DEFAULT_SOURCE
31 #define _BSD_SOURCE
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <unistd.h>
37 #include <glib.h>
38
39 #ifdef __ANDROID__
40 #include <linux/time.h>
41 #endif
42
43 #include "caretransmission.h"
44 #include "caremotehandler.h"
45 #include "caprotocolmessage.h"
46 #include "oic_malloc.h"
47 #include "logger.h"
48
49 #define TAG PCF("CA")
50
51 typedef struct
52 {
53     uint64_t timeStamp;                 /**< last sent time. microseconds */
54     uint64_t timeout;                   /**< timeout value. microseconds */
55     uint8_t triedCount;                 /**< retransmission count */
56     uint16_t messageId;                 /**< coap PDU message id */
57     CARemoteEndpoint_t *endpoint;       /**< remote endpoint */
58     void *pdu;                          /**< coap PDU */
59     uint32_t size;                      /**< coap PDU size */
60 } CARetransmissionData_t;
61
62 /**
63  * @brief   getCurrent monotonic time
64  * @return  current time in microseconds
65  */
66 uint64_t getCurrentTimeInMicroSeconds();
67
68 /**
69  * @brief   check timeout routine
70  * @param   currentTime     [IN]microseconds
71  * @param   timeStamp       [IN]microseconds
72  * @param   timeoutValue    [IN]microseconds
73  * @param   triedCount      [IN]Number of retransmission tried
74  * @return  true if the timeout period has elapsed, false otherwise
75  */
76 static bool CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint64_t timeoutValue,
77                            uint8_t triedCount)
78 {
79     // #1. calculate timeout
80     uint32_t milliTimeoutValue = timeoutValue * 0.001;
81     uint64_t timeout = (milliTimeoutValue << triedCount) * (uint64_t) 1000;
82
83     if (currentTime >= timeStamp + timeout)
84     {
85         OIC_LOG_V(DEBUG, TAG, "%d microseconds time out!!, tried count(%d)", timeout, triedCount);
86         return true;
87     }
88
89     return false;
90 }
91
92 /**
93  * @brief   timeout value is
94  *          between DEFAULT_ACK_TIMEOUT_SEC and
95  *          (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
96  *          DEFAULT_RANDOM_FACTOR       1.5 (CoAP)
97  * @return  microseconds.
98  */
99 static uint64_t CAGetTimeoutValue()
100 {
101     return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (random() & 0xFF)) >> 8)) *
102             (uint64_t) 1000;
103 }
104
105 static void CACheckRetransmissionList(CARetransmission_t *context)
106 {
107     if (NULL == context)
108     {
109         OIC_LOG(ERROR, TAG, "context is null..");
110         return;
111     }
112
113     // mutex lock
114     u_mutex_lock(context->threadMutex);
115
116     uint32_t i = 0;
117     uint32_t len = u_arraylist_length(context->dataList);
118
119     for (i = 0; i < len; i++)
120     {
121         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
122
123         if (NULL == retData)
124         {
125             continue;
126         }
127
128         uint64_t currentTime = getCurrentTimeInMicroSeconds();
129
130         if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
131         {
132             // #2. if time's up, send the data.
133             if (context->dataSendMethod != NULL)
134             {
135                 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
136                           retData->messageId);
137                 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
138             }
139
140             // #3. increase the retransmission count and update timestamp.
141             retData->timeStamp = currentTime;
142             retData->triedCount++;
143         }
144
145         // #4. if tried count is max, remove the retransmission data from list.
146         if (retData->triedCount >= context->config.tryingCount)
147         {
148             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
149
150             if (removedData != NULL)
151             {
152                 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
153                           message id(%d)", removedData->messageId);
154
155                 // callback for retransmit timeout
156                 if (context->timeoutCallback != NULL)
157                 {
158                     context->timeoutCallback(removedData->endpoint, removedData->pdu,
159                                              removedData->size);
160                 }
161
162                 CADestroyRemoteEndpointInternal(removedData->endpoint);
163                 OICFree(removedData->pdu);
164
165                 OICFree(removedData);
166
167                 // modify loop value.
168                 len = u_arraylist_length(context->dataList);
169
170                 --i;
171             }
172             else
173             {
174                 OIC_LOG(ERROR, TAG, "arraylist remove error");
175             }
176
177         }
178     }
179
180     // mutex unlock
181     u_mutex_unlock(context->threadMutex);
182 }
183
184 static void CARetransmissionBaseRoutine(void *threadValue)
185 {
186     OIC_LOG(DEBUG, TAG, "retransmission main thread start..");
187
188     CARetransmission_t *context = (CARetransmission_t *) threadValue;
189
190     if (NULL == context)
191     {
192         OIC_LOG(ERROR, TAG, "thread data passing error!!");
193
194         return;
195     }
196
197     while (!context->isStop)
198     {
199         // mutex lock
200         u_mutex_lock(context->threadMutex);
201
202         if (u_arraylist_length(context->dataList) <= 0)
203         {
204             // if list is empty, thread will wait
205             OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
206
207             // wait
208             u_cond_wait(context->threadCond, context->threadMutex);
209
210             OIC_LOG(DEBUG, TAG, "wake up..");
211         }
212         else
213         {
214             // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
215             OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds",
216                       RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
217
218             // wait
219             u_cond_wait_until(context->threadCond, context->threadMutex,
220                               RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
221         }
222
223         // mutex unlock
224         u_mutex_unlock(context->threadMutex);
225
226         // check stop flag
227         if (context->isStop)
228         {
229             continue;
230         }
231
232         CACheckRetransmissionList(context);
233     }
234
235     u_cond_signal(context->threadCond);
236
237     OIC_LOG(DEBUG, TAG, "retransmission main thread end..");
238
239 }
240
241 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
242                                       CADataSendMethod_t retransmissionSendMethod,
243                                       CATimeoutCallback_t timeoutCallback,
244                                       CARetransmissionConfig_t* config)
245 {
246     if (NULL == context)
247     {
248         OIC_LOG(ERROR, TAG, "thread instance is empty..");
249         return CA_STATUS_INVALID_PARAM;
250     }
251
252     if (NULL == handle)
253     {
254         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
255         return CA_STATUS_INVALID_PARAM;
256     }
257
258     OIC_LOG(DEBUG, TAG, "thread initialize..");
259
260     memset(context, 0, sizeof(CARetransmission_t));
261
262     CARetransmissionConfig_t cfg = { 0 };
263
264     if (NULL == config)
265     {
266         // setDefault
267         cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
268         cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
269     }
270     else
271     {
272         cfg = *config;
273     }
274
275     // set send thread data
276     context->threadPool = handle;
277     context->threadMutex = u_mutex_new();
278     context->threadCond = u_cond_new();
279     context->dataSendMethod = retransmissionSendMethod;
280     context->timeoutCallback = timeoutCallback;
281     context->config = cfg;
282     context->isStop = false;
283     context->dataList = u_arraylist_create();
284
285     return CA_STATUS_OK;
286 }
287
288 CAResult_t CARetransmissionStart(CARetransmission_t *context)
289 {
290     if (NULL == context)
291     {
292         OIC_LOG(ERROR, TAG, "context is empty..");
293         return CA_STATUS_INVALID_PARAM;
294     }
295
296     if (NULL == context->threadPool)
297     {
298         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
299         return CA_STATUS_INVALID_PARAM;
300     }
301
302     CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
303                                             context);
304
305     if (res != CA_STATUS_OK)
306     {
307         OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
308         return res;
309     }
310
311     return res;
312 }
313
314 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
315                                     const CARemoteEndpoint_t* endpoint, const void* pdu,
316                                     uint32_t size)
317 {
318     if (NULL == context || NULL == endpoint || NULL == pdu)
319     {
320         OIC_LOG(ERROR, TAG, "invalid parameter..");
321         return CA_STATUS_INVALID_PARAM;
322     }
323
324     // #0. check support connectivity type
325     if (!(context->config.supportType & endpoint->connectivityType))
326     {
327         OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
328                   endpoint->connectivityType);
329         return CA_NOT_SUPPORTED;
330     }
331
332     // #1. check PDU method type and get message id.
333     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
334     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
335
336     OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
337
338     if (type != CA_MSG_CONFIRM)
339     {
340         OIC_LOG(DEBUG, TAG, "not supported message type for retransmission..");
341         return CA_NOT_SUPPORTED;
342     }
343
344     // create retransmission data
345     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
346                                           1, sizeof(CARetransmissionData_t));
347
348     if (NULL == retData)
349     {
350         OIC_LOG(ERROR, TAG, "memory error!!");
351         return CA_MEMORY_ALLOC_FAILED;
352     }
353
354     // copy PDU data
355     void *pduData = (void *) OICMalloc(size);
356     if (NULL == pduData)
357     {
358         OICFree(retData);
359         OIC_LOG_V(ERROR, TAG, "memory error!!");
360         return CA_MEMORY_ALLOC_FAILED;
361     }
362     memcpy(pduData, pdu, size);
363
364     // clone remote endpoint
365     CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
366     if (NULL == remoteEndpoint)
367     {
368         OICFree(retData);
369         OICFree(pduData);
370         OIC_LOG(ERROR, TAG, "memory error!!");
371         return CA_MEMORY_ALLOC_FAILED;
372     }
373
374     // #2. add additional information. (time stamp, retransmission count...)
375     retData->timeStamp = getCurrentTimeInMicroSeconds();
376     retData->timeout = CAGetTimeoutValue();
377     retData->triedCount = 0;
378     retData->messageId = messageId;
379     retData->endpoint = remoteEndpoint;
380     retData->pdu = pduData;
381     retData->size = size;
382
383     // mutex lock
384     u_mutex_lock(context->threadMutex);
385
386     uint32_t i = 0;
387     uint32_t len = u_arraylist_length(context->dataList);
388
389     // #3. add data into list
390     for (i = 0; i < len; i++)
391     {
392         CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
393
394         if (NULL == currData)
395         {
396             continue;
397         }
398
399         // found index
400         if (NULL != currData->endpoint && currData->messageId == messageId
401             && (currData->endpoint->connectivityType == endpoint->connectivityType))
402         {
403             OIC_LOG(ERROR, TAG, "Duplicate message ID");
404
405             // mutex unlock
406             u_mutex_unlock(context->threadMutex);
407
408             OICFree(retData);
409             OICFree(pduData);
410             OICFree(remoteEndpoint);
411             return CA_STATUS_FAILED;
412         }
413     }
414
415     u_arraylist_add(context->dataList, (void *) retData);
416
417     // notify the thread
418     u_cond_signal(context->threadCond);
419
420     // mutex unlock
421     u_mutex_unlock(context->threadMutex);
422
423     return CA_STATUS_OK;
424 }
425
426 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
427                                         const CARemoteEndpoint_t *endpoint, const void *pdu,
428                                         uint32_t size, void **retransmissionPdu)
429 {
430     OIC_LOG_V(DEBUG, TAG, "IN - CARetransmissionReceivedData");
431     if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
432     {
433         OIC_LOG(ERROR, TAG, "invalid parameter..");
434         return CA_STATUS_INVALID_PARAM;
435     }
436
437     // #0. check support connectivity type
438     if (!(context->config.supportType & endpoint->connectivityType))
439     {
440         OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
441                   endpoint->connectivityType);
442         return CA_STATUS_OK;
443     }
444
445     // #1. check PDU method type and get message id.
446     // ACK, RST --> remove the CON data
447     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
448     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
449
450     OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
451
452     if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
453     {
454         return CA_STATUS_OK;
455     }
456
457     // mutex lock
458     u_mutex_lock(context->threadMutex);
459
460     uint32_t i = 0;
461     uint32_t len = u_arraylist_length(context->dataList);
462
463     // find index
464     for (i = 0; i < len; i++)
465     {
466         CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
467                 context->dataList, i);
468
469         if (NULL == retData)
470         {
471             continue;
472         }
473
474         // found index
475         if (NULL != retData->endpoint && retData->messageId == messageId
476             && (retData->endpoint->connectivityType == endpoint->connectivityType))
477         {
478             // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
479             // if retransmission was finish..token will be unavailable.
480             if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
481             {
482                 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY..");
483
484                 if (NULL == retData->pdu)
485                 {
486                     OIC_LOG(ERROR, TAG, "retData->pdu is null");
487                     OICFree(retData);
488                     // mutex unlock
489                     u_mutex_unlock(context->threadMutex);
490
491                     return CA_STATUS_FAILED;
492                 }
493
494                 // copy PDU data
495                 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
496                 if ((*retransmissionPdu) == NULL)
497                 {
498                     OICFree(retData);
499                     OIC_LOG(ERROR, TAG, "memory error!!");
500
501                     // mutex unlock
502                     u_mutex_unlock(context->threadMutex);
503
504                     return CA_MEMORY_ALLOC_FAILED;
505                 }
506                 memcpy((*retransmissionPdu), retData->pdu, retData->size);
507             }
508
509             // #2. remove data from list
510             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
511             if (NULL == removedData)
512             {
513                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
514
515                 // mutex unlock
516                 u_mutex_unlock(context->threadMutex);
517
518                 return CA_STATUS_FAILED;
519             }
520
521             OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)",
522                       messageId);
523
524             CADestroyRemoteEndpointInternal(removedData->endpoint);
525             OICFree(removedData->pdu);
526             OICFree(removedData);
527
528             break;
529         }
530     }
531
532     // mutex unlock
533     u_mutex_unlock(context->threadMutex);
534
535     OIC_LOG(DEBUG, TAG, "OUT - CARetransmissionReceivedData");
536     return CA_STATUS_OK;
537 }
538
539 CAResult_t CARetransmissionStop(CARetransmission_t *context)
540 {
541     if (NULL == context)
542     {
543         OIC_LOG(ERROR, TAG, "context is empty..");
544         return CA_STATUS_INVALID_PARAM;
545     }
546
547     OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
548
549     // mutex lock
550     u_mutex_lock(context->threadMutex);
551
552     // set stop flag
553     context->isStop = true;
554
555     // notity the thread
556     u_cond_signal(context->threadCond);
557
558     u_cond_wait(context->threadCond, context->threadMutex);
559
560     // mutex unlock
561     u_mutex_unlock(context->threadMutex);
562
563     return CA_STATUS_OK;
564 }
565
566 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
567 {
568     if (NULL == context)
569     {
570         OIC_LOG(ERROR, TAG, "context is empty..");
571         return CA_STATUS_INVALID_PARAM;
572     }
573
574     OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
575
576     u_mutex_free(context->threadMutex);
577     context->threadMutex = NULL;
578     u_cond_free(context->threadCond);
579     u_arraylist_free(&context->dataList);
580
581     return CA_STATUS_OK;
582 }
583
584 uint64_t getCurrentTimeInMicroSeconds()
585 {
586     uint64_t currentTime = 0;
587
588 #ifdef __ANDROID__
589     struct timespec getTs;
590
591     clock_gettime(CLOCK_MONOTONIC, &getTs);
592
593     currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
594     OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
595 #else
596     currentTime = g_get_monotonic_time();
597 #endif
598     return currentTime;
599 }