Merge branch 'master' into extended-easysetup
[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
32 // Defining _POSIX_C_SOURCE macro with 199309L (or greater) as value
33 // causes header files to expose definitions
34 // corresponding to the POSIX.1b, Real-time extensions
35 // (IEEE Std 1003.1b-1993) specification
36 //
37 // For this specific file, see use of clock_gettime,
38 // Refer to http://pubs.opengroup.org/stage7tc1/functions/clock_gettime.html
39 // and to http://man7.org/linux/man-pages/man2/clock_gettime.2.html
40 #ifndef _POSIX_C_SOURCE
41 #define _POSIX_C_SOURCE 200809L
42 #endif
43
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47
48 #ifndef SINGLE_THREAD
49 #ifdef HAVE_UNISTD_H
50 #include <unistd.h>
51 #endif
52 #ifdef HAVE_SYS_TIME_H
53 #include <sys/time.h>
54 #endif
55 #if HAVE_SYS_TIMEB_H
56 #include <sys/timeb.h>
57 #endif
58 #ifdef HAVE_TIME_H
59 #include <time.h>
60 #endif
61 #endif
62
63 #if defined(__ANDROID__)
64 #include <linux/time.h>
65 #endif
66
67 #include "caretransmission.h"
68 #include "caremotehandler.h"
69 #include "caprotocolmessage.h"
70 #include "oic_malloc.h"
71 #include "ocrandom.h"
72 #include "logger.h"
73
74 #define TAG "OIC_CA_RETRANS"
75
76 typedef struct
77 {
78     uint64_t timeStamp;                 /**< last sent time. microseconds */
79 #ifndef SINGLE_THREAD
80     uint64_t timeout;                   /**< timeout value. microseconds */
81 #endif
82     uint8_t triedCount;                 /**< retransmission count */
83     uint16_t messageId;                 /**< coap PDU message id */
84     CAEndpoint_t *endpoint;             /**< remote endpoint */
85     void *pdu;                          /**< coap PDU */
86     uint32_t size;                      /**< coap PDU size */
87 } CARetransmissionData_t;
88
89 static const uint64_t USECS_PER_SEC = 1000000;
90 static const uint64_t MSECS_PER_SEC = 1000;
91
92 /**
93  * @brief   getCurrent monotonic time
94  * @return  current time in microseconds
95  */
96 uint64_t getCurrentTimeInMicroSeconds();
97
98 #ifndef SINGLE_THREAD
99 /**
100  * @brief   timeout value is
101  *          between DEFAULT_ACK_TIMEOUT_SEC and
102  *          (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
103  *          DEFAULT_RANDOM_FACTOR       1.5 (CoAP)
104  * @return  microseconds.
105  */
106 static uint64_t CAGetTimeoutValue()
107 {
108 #ifdef HAVE_SRANDOM
109     return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * OCGetRandomByte()) >> 8)) *
110             (uint64_t) 1000;
111 #else
112     return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * OCGetRandomByte()) >> 8)) *
113             (uint64_t) 1000;
114 #endif
115 }
116
117 CAResult_t CARetransmissionStart(CARetransmission_t *context)
118 {
119     if (NULL == context)
120     {
121         OIC_LOG(ERROR, TAG, "context is empty");
122         return CA_STATUS_INVALID_PARAM;
123     }
124
125     if (NULL == context->threadPool)
126     {
127         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
128         return CA_STATUS_INVALID_PARAM;
129     }
130
131     CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
132                                              context);
133
134     if (CA_STATUS_OK != res)
135     {
136         OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
137         return res;
138     }
139
140     return res;
141 }
142 #endif
143
144 /**
145  * @brief   check timeout routine
146  * @param   currentTime     [IN]microseconds
147  * @param   retData         [IN]retransmission data
148  * @return  true if the timeout period has elapsed, false otherwise
149  */
150 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
151 {
152 #ifndef SINGLE_THREAD
153     // #1. calculate timeout
154     uint32_t milliTimeoutValue = retData->timeout * 0.001;
155     uint64_t timeout = (milliTimeoutValue << retData->triedCount) * (uint64_t) 1000;
156
157     if (currentTime >= retData->timeStamp + timeout)
158     {
159         OIC_LOG_V(DEBUG, TAG, "%llu microseconds time out!!, tried count(%d)",
160                   timeout, retData->triedCount);
161         return true;
162     }
163 #else
164     // #1. calculate timeout
165     uint64_t timeOut = (2 << retData->triedCount) * 1000000;
166
167     if (currentTime >= retData->timeStamp + timeOut)
168     {
169         OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d",
170                   (2 << retData->triedCount), retData->triedCount);
171         return true;
172     }
173 #endif
174     return false;
175 }
176
177 static void CACheckRetransmissionList(CARetransmission_t *context)
178 {
179     if (NULL == context)
180     {
181         OIC_LOG(ERROR, TAG, "context is null");
182         return;
183     }
184
185     // mutex lock
186     ca_mutex_lock(context->threadMutex);
187
188     uint32_t i = 0;
189     uint32_t len = u_arraylist_length(context->dataList);
190
191     for (i = 0; i < len; i++)
192     {
193         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
194
195         if (NULL == retData)
196         {
197             continue;
198         }
199
200         uint64_t currentTime = getCurrentTimeInMicroSeconds();
201
202         if (CACheckTimeout(currentTime, retData))
203         {
204             // #2. if time's up, send the data.
205             if (NULL != context->dataSendMethod)
206             {
207                 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
208                           retData->messageId);
209                 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
210             }
211
212             // #3. increase the retransmission count and update timestamp.
213             retData->timeStamp = currentTime;
214             retData->triedCount++;
215         }
216
217         // #4. if tried count is max, remove the retransmission data from list.
218         if (retData->triedCount >= context->config.tryingCount)
219         {
220             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
221             if (NULL == removedData)
222             {
223                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
224                 // mutex unlock
225                 ca_mutex_unlock(context->threadMutex);
226                 return;
227             }
228             OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
229                       "msgid=%d", removedData->messageId);
230
231             // callback for retransmit timeout
232             if (NULL != context->timeoutCallback)
233             {
234                 context->timeoutCallback(removedData->endpoint, removedData->pdu,
235                                          removedData->size);
236             }
237
238             CAFreeEndpoint(removedData->endpoint);
239             OICFree(removedData->pdu);
240
241             OICFree(removedData);
242
243             // modify loop value.
244             len = u_arraylist_length(context->dataList);
245             --i;
246         }
247     }
248
249     // mutex unlock
250     ca_mutex_unlock(context->threadMutex);
251 }
252
253 void CARetransmissionBaseRoutine(void *threadValue)
254 {
255     OIC_LOG(DEBUG, TAG, "retransmission main thread start");
256
257     CARetransmission_t *context = (CARetransmission_t *) threadValue;
258
259     if (NULL == context)
260     {
261         OIC_LOG(ERROR, TAG, "thread data passing error");
262
263         return;
264     }
265
266 #ifdef SINGLE_THREAD
267     if (true == context->isStop)
268     {
269         OIC_LOG(DEBUG, TAG, "thread stopped");
270         return;
271     }
272     CACheckRetransmissionList(context);
273 #else
274
275     while (!context->isStop)
276     {
277         // mutex lock
278         ca_mutex_lock(context->threadMutex);
279
280         if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
281         {
282             // if list is empty, thread will wait
283             OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
284
285             // wait
286             ca_cond_wait(context->threadCond, context->threadMutex);
287
288             OIC_LOG(DEBUG, TAG, "wake up..");
289         }
290         else if (!context->isStop)
291         {
292             // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
293             OIC_LOG_V(DEBUG, TAG, "wait..(%lld)microseconds",
294                       RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
295
296             // wait
297             uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
298             ca_cond_wait_for(context->threadCond, context->threadMutex, absTime );
299         }
300         else
301         {
302             // we are stopping, so we want to unlock and finish stopping
303         }
304
305         // mutex unlock
306         ca_mutex_unlock(context->threadMutex);
307
308         // check stop flag
309         if (context->isStop)
310         {
311             continue;
312         }
313
314         CACheckRetransmissionList(context);
315     }
316
317     ca_mutex_lock(context->threadMutex);
318     ca_cond_signal(context->threadCond);
319     ca_mutex_unlock(context->threadMutex);
320
321 #endif
322     OIC_LOG(DEBUG, TAG, "retransmission main thread end");
323
324 }
325
326 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
327                                       ca_thread_pool_t handle,
328                                       CADataSendMethod_t retransmissionSendMethod,
329                                       CATimeoutCallback_t timeoutCallback,
330                                       CARetransmissionConfig_t* config)
331 {
332     if (NULL == context)
333     {
334         OIC_LOG(ERROR, TAG, "thread instance is empty");
335         return CA_STATUS_INVALID_PARAM;
336     }
337 #ifndef SINGLE_THREAD
338     if (NULL == handle)
339     {
340         OIC_LOG(ERROR, TAG, "thread pool handle is empty");
341         return CA_STATUS_INVALID_PARAM;
342     }
343 #endif
344     OIC_LOG(DEBUG, TAG, "thread initialize");
345
346     memset(context, 0, sizeof(CARetransmission_t));
347
348     CARetransmissionConfig_t cfg = { .supportType = DEFAULT_RETRANSMISSION_TYPE,
349                                      .tryingCount = DEFAULT_RETRANSMISSION_COUNT };
350
351     if (config)
352     {
353         cfg = *config;
354     }
355
356     // set send thread data
357     context->threadPool = handle;
358     context->threadMutex = ca_mutex_new();
359     context->threadCond = ca_cond_new();
360     context->dataSendMethod = retransmissionSendMethod;
361     context->timeoutCallback = timeoutCallback;
362     context->config = cfg;
363     context->isStop = false;
364     context->dataList = u_arraylist_create();
365
366     return CA_STATUS_OK;
367 }
368
369 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
370                                     const CAEndpoint_t *endpoint,
371                                     const void *pdu, uint32_t size)
372 {
373     if (NULL == context || NULL == endpoint || NULL == pdu)
374     {
375         OIC_LOG(ERROR, TAG, "invalid parameter");
376         return CA_STATUS_INVALID_PARAM;
377     }
378
379     // #0. check support transport type
380     if (!(context->config.supportType & endpoint->adapter))
381     {
382         OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
383         return CA_NOT_SUPPORTED;
384     }
385
386     // #1. check PDU method type and get message id.
387     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
388     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
389
390     OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
391
392     if (CA_MSG_CONFIRM != type)
393     {
394         OIC_LOG(DEBUG, TAG, "not supported message type");
395         return CA_NOT_SUPPORTED;
396     }
397
398     // create retransmission data
399     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
400                                           1, sizeof(CARetransmissionData_t));
401
402     if (NULL == retData)
403     {
404         OIC_LOG(ERROR, TAG, "memory error");
405         return CA_MEMORY_ALLOC_FAILED;
406     }
407
408     // copy PDU data
409     void *pduData = (void *) OICMalloc(size);
410     if (NULL == pduData)
411     {
412         OICFree(retData);
413         OIC_LOG(ERROR, TAG, "memory error");
414         return CA_MEMORY_ALLOC_FAILED;
415     }
416     memcpy(pduData, pdu, size);
417
418     // clone remote endpoint
419     CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
420     if (NULL == remoteEndpoint)
421     {
422         OICFree(retData);
423         OICFree(pduData);
424         OIC_LOG(ERROR, TAG, "memory error");
425         return CA_MEMORY_ALLOC_FAILED;
426     }
427
428     // #2. add additional information. (time stamp, retransmission count...)
429     retData->timeStamp = getCurrentTimeInMicroSeconds();
430 #ifndef SINGLE_THREAD
431     retData->timeout = CAGetTimeoutValue();
432 #endif
433     retData->triedCount = 0;
434     retData->messageId = messageId;
435     retData->endpoint = remoteEndpoint;
436     retData->pdu = pduData;
437     retData->size = size;
438 #ifndef SINGLE_THREAD
439     // mutex lock
440     ca_mutex_lock(context->threadMutex);
441
442     uint32_t i = 0;
443     uint32_t len = u_arraylist_length(context->dataList);
444
445     // #3. add data into list
446     for (i = 0; i < len; i++)
447     {
448         CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
449
450         if (NULL == currData)
451         {
452             continue;
453         }
454
455         // found index
456         if (NULL != currData->endpoint && currData->messageId == messageId
457             && (currData->endpoint->adapter == endpoint->adapter))
458         {
459             OIC_LOG(ERROR, TAG, "Duplicate message ID");
460
461             // mutex unlock
462             ca_mutex_unlock(context->threadMutex);
463
464             OICFree(retData);
465             OICFree(pduData);
466             OICFree(remoteEndpoint);
467             return CA_STATUS_FAILED;
468         }
469     }
470
471     u_arraylist_add(context->dataList, (void *) retData);
472
473     // notify the thread
474     ca_cond_signal(context->threadCond);
475
476     // mutex unlock
477     ca_mutex_unlock(context->threadMutex);
478
479 #else
480     u_arraylist_add(context->dataList, (void *) retData);
481
482     CACheckRetransmissionList(context);
483 #endif
484     return CA_STATUS_OK;
485 }
486
487 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
488                                         const CAEndpoint_t *endpoint, const void *pdu,
489                                         uint32_t size, void **retransmissionPdu)
490 {
491     OIC_LOG(DEBUG, TAG, "IN");
492     if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
493     {
494         OIC_LOG(ERROR, TAG, "invalid parameter");
495         return CA_STATUS_INVALID_PARAM;
496     }
497
498     // #0. check support transport type
499     if (!(context->config.supportType & endpoint->adapter))
500     {
501         OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
502         return CA_STATUS_OK;
503     }
504
505     // #1. check PDU method type and get message id.
506     // ACK, RST --> remove the CON data
507     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
508     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
509     CAResponseResult_t code = CAGetCodeFromPduBinaryData(pdu, size);
510
511     OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d, code=%d",
512               type, messageId, code);
513
514     if (((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
515         || (CA_MSG_RESET == type && CA_EMPTY != code))
516     {
517         return CA_STATUS_OK;
518     }
519
520     // mutex lock
521     ca_mutex_lock(context->threadMutex);
522     uint32_t len = u_arraylist_length(context->dataList);
523
524     // find index
525     uint32_t i;
526     for (i = 0; i < len; i++)
527     {
528         CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
529                 context->dataList, i);
530
531         if (NULL == retData)
532         {
533             continue;
534         }
535
536         // found index
537         if (NULL != retData->endpoint && retData->messageId == messageId
538             && (retData->endpoint->adapter == endpoint->adapter))
539         {
540             // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
541             // if retransmission was finish..token will be unavailable.
542             if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
543             {
544                 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
545
546                 if (NULL == retData->pdu)
547                 {
548                     OIC_LOG(ERROR, TAG, "retData->pdu is null");
549                     OICFree(retData);
550                     // mutex unlock
551                     ca_mutex_unlock(context->threadMutex);
552
553                     return CA_STATUS_FAILED;
554                 }
555
556                 // copy PDU data
557                 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
558                 if ((*retransmissionPdu) == NULL)
559                 {
560                     OICFree(retData);
561                     OIC_LOG(ERROR, TAG, "memory error");
562
563                     // mutex unlock
564                     ca_mutex_unlock(context->threadMutex);
565
566                     return CA_MEMORY_ALLOC_FAILED;
567                 }
568                 memcpy((*retransmissionPdu), retData->pdu, retData->size);
569             }
570
571             // #2. remove data from list
572             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
573             if (NULL == removedData)
574             {
575                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
576
577                 // mutex unlock
578                 ca_mutex_unlock(context->threadMutex);
579
580                 return CA_STATUS_FAILED;
581             }
582
583             OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
584
585             CAFreeEndpoint(removedData->endpoint);
586             OICFree(removedData->pdu);
587             OICFree(removedData);
588
589             break;
590         }
591     }
592
593     // mutex unlock
594     ca_mutex_unlock(context->threadMutex);
595
596     OIC_LOG(DEBUG, TAG, "OUT");
597     return CA_STATUS_OK;
598 }
599
600 CAResult_t CARetransmissionStop(CARetransmission_t *context)
601 {
602     if (NULL == context)
603     {
604         OIC_LOG(ERROR, TAG, "context is empty..");
605         return CA_STATUS_INVALID_PARAM;
606     }
607
608     OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
609
610     // mutex lock
611     ca_mutex_lock(context->threadMutex);
612
613     // set stop flag
614     context->isStop = true;
615
616     // notify the thread
617     ca_cond_signal(context->threadCond);
618
619     ca_cond_wait(context->threadCond, context->threadMutex);
620
621     // mutex unlock
622     ca_mutex_unlock(context->threadMutex);
623
624     return CA_STATUS_OK;
625 }
626
627 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
628 {
629     if (NULL == context)
630     {
631         OIC_LOG(ERROR, TAG, "context is empty..");
632         return CA_STATUS_INVALID_PARAM;
633     }
634
635     OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
636
637     ca_mutex_free(context->threadMutex);
638     context->threadMutex = NULL;
639     ca_cond_free(context->threadCond);
640     u_arraylist_free(&context->dataList);
641
642     return CA_STATUS_OK;
643 }
644
645 uint64_t getCurrentTimeInMicroSeconds()
646 {
647     OIC_LOG(DEBUG, TAG, "IN");
648     uint64_t currentTime = 0;
649
650 #ifdef __ANDROID__
651     struct timespec getTs;
652
653     clock_gettime(CLOCK_MONOTONIC, &getTs);
654
655     currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
656     OIC_LOG_V(DEBUG, TAG, "current time = %lld", currentTime);
657 #elif defined __ARDUINO__
658     currentTime = millis() * 1000;
659     OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
660 #else
661 #if _POSIX_TIMERS > 0
662     struct timespec ts;
663     clock_gettime(CLOCK_MONOTONIC, &ts);
664     currentTime = ts.tv_sec * USECS_PER_SEC + ts.tv_nsec / 1000;
665 #elif defined(_WIN32)
666     struct __timeb64 tb;
667     _ftime64_s(&tb);
668     currentTime = tb.time * USECS_PER_SEC + tb.millitm * MSECS_PER_SEC;
669 #else
670     struct timeval tv;
671     gettimeofday(&tv, NULL);
672     currentTime = tv.tv_sec * USECS_PER_SEC + tv.tv_usec;
673 #endif
674 #endif
675
676     OIC_LOG(DEBUG, TAG, "OUT");
677     return currentTime;
678 }