Use `URI` all caps in documentation
[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 "iotivity_config.h"
45 #include <stdio.h>
46 #include <stdlib.h>
47 #include <string.h>
48
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 #ifdef HAVE_SYS_TIMEB_H
56 #include <sys/timeb.h>
57 #endif
58 #ifdef HAVE_TIME_H
59 #include <time.h>
60 #endif
61
62 #if defined(__ANDROID__)
63 #include <linux/time.h>
64 #endif
65
66 #include "caretransmission.h"
67 #include "caremotehandler.h"
68 #include "caprotocolmessage.h"
69 #include "oic_malloc.h"
70 #include "oic_time.h"
71 #include "experimental/ocrandom.h"
72 #include "experimental/logger.h"
73
74 #define TAG "OIC_CA_RETRANS"
75
76 typedef struct
77 {
78     uint64_t timeStamp;                 /**< last sent time. microseconds */
79     uint64_t timeout;                   /**< timeout value. microseconds */
80     uint8_t triedCount;                 /**< retransmission count */
81     uint16_t messageId;                 /**< coap PDU message id */
82     CADataType_t dataType;              /**< data Type (Request/Response) */
83     CAEndpoint_t *endpoint;             /**< remote endpoint */
84     void *pdu;                          /**< coap PDU */
85     uint32_t size;                      /**< coap PDU size */
86 } CARetransmissionData_t;
87
88 static const uint64_t USECS_PER_SEC = 1000000;
89 static const uint64_t USECS_PER_MSEC = 1000;
90 static const uint64_t MSECS_PER_SEC = 1000;
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(void)
100 {
101     uint8_t randomValue = 0;
102     if (!OCGetRandomBytes(&randomValue, sizeof(randomValue)))
103     {
104         OIC_LOG(ERROR, TAG, "OCGetRandomBytes failed");
105     }
106
107     return ((DEFAULT_ACK_TIMEOUT_SEC * MSECS_PER_SEC) + ((MSECS_PER_SEC * (uint64_t)randomValue) >> 8)) *
108              MSECS_PER_SEC;
109 }
110
111 CAResult_t CARetransmissionStart(CARetransmission_t *context)
112 {
113     if (NULL == context)
114     {
115         OIC_LOG(ERROR, TAG, "context is empty");
116         return CA_STATUS_INVALID_PARAM;
117     }
118
119     if (NULL == context->threadPool)
120     {
121         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
122         return CA_STATUS_INVALID_PARAM;
123     }
124
125     CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
126                                              context);
127
128     if (CA_STATUS_OK != res)
129     {
130         OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
131         return res;
132     }
133
134     return res;
135 }
136
137 /**
138  * @brief   check timeout routine
139  * @param[in] currentTime  microseconds
140  * @param[in] retData      retransmission data
141  * @return  true if the timeout period has elapsed, false otherwise
142  */
143 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
144 {
145     // #1. calculate timeout
146     uint64_t milliTimeoutValue = retData->timeout / USECS_PER_MSEC;
147     uint64_t timeout = (milliTimeoutValue << retData->triedCount) * USECS_PER_MSEC;
148
149     if (currentTime >= retData->timeStamp + timeout)
150     {
151         OIC_LOG_V(DEBUG, TAG, "%" PRIu64 " microseconds time out!!, tried count(%d)",
152                   timeout, retData->triedCount);
153         return true;
154     }
155     return false;
156 }
157
158 static void CACheckRetransmissionList(CARetransmission_t *context)
159 {
160     if (NULL == context)
161     {
162         OIC_LOG(ERROR, TAG, "context is null");
163         return;
164     }
165
166     // mutex lock
167     oc_mutex_lock(context->threadMutex);
168
169     size_t len = u_arraylist_length(context->dataList);
170
171     for (size_t i = 0; i < len; i++)
172     {
173         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
174
175         if (NULL == retData)
176         {
177             continue;
178         }
179
180         uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
181
182         if (CACheckTimeout(currentTime, retData))
183         {
184             // #2. if time's up, send the data.
185             if (NULL != context->dataSendMethod)
186             {
187                 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
188                           retData->messageId);
189                 context->dataSendMethod(retData->endpoint, retData->pdu,
190                                         retData->size, retData->dataType);
191             }
192
193             // #3. increase the retransmission count and update timestamp.
194             retData->timeStamp = currentTime;
195             retData->triedCount++;
196         }
197
198         // #4. if tried count is max, remove the retransmission data from list.
199         if (retData->triedCount >= context->config.tryingCount)
200         {
201             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
202             if (NULL == removedData)
203             {
204                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
205                 // mutex unlock
206                 oc_mutex_unlock(context->threadMutex);
207                 return;
208             }
209             OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
210                       "msgid=%d", removedData->messageId);
211
212             // callback for retransmit timeout
213             if (NULL != context->timeoutCallback)
214             {
215                 context->timeoutCallback(removedData->endpoint, removedData->pdu,
216                                          removedData->size);
217             }
218
219             CAFreeEndpoint(removedData->endpoint);
220             OICFree(removedData->pdu);
221
222             OICFree(removedData);
223
224             // modify loop value.
225             len = u_arraylist_length(context->dataList);
226             --i;
227         }
228     }
229
230     // mutex unlock
231     oc_mutex_unlock(context->threadMutex);
232 }
233
234 void CARetransmissionBaseRoutine(void *threadValue)
235 {
236     OIC_LOG(DEBUG, TAG, "retransmission main thread start");
237
238     CARetransmission_t *context = (CARetransmission_t *) threadValue;
239
240     if (NULL == context)
241     {
242         OIC_LOG(ERROR, TAG, "thread data passing error");
243
244         return;
245     }
246
247
248     while (!context->isStop)
249     {
250         // mutex lock
251         oc_mutex_lock(context->threadMutex);
252
253         if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
254         {
255             // if list is empty, thread will wait
256             OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
257
258             // wait
259             oc_cond_wait(context->threadCond, context->threadMutex);
260
261             OIC_LOG(DEBUG, TAG, "wake up..");
262         }
263         else if (!context->isStop)
264         {
265             // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
266             OIC_LOG_V(DEBUG, TAG, "wait..(%" PRIu64 ")microseconds",
267                       RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
268
269             // wait
270             uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
271             oc_cond_wait_for(context->threadCond, context->threadMutex, absTime );
272         }
273         else
274         {
275             // we are stopping, so we want to unlock and finish stopping
276         }
277
278         // mutex unlock
279         oc_mutex_unlock(context->threadMutex);
280
281         // check stop flag
282         if (context->isStop)
283         {
284             continue;
285         }
286
287         CACheckRetransmissionList(context);
288     }
289
290     oc_mutex_lock(context->threadMutex);
291     oc_cond_signal(context->threadCond);
292     oc_mutex_unlock(context->threadMutex);
293
294     OIC_LOG(DEBUG, TAG, "retransmission main thread end");
295
296 }
297
298 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
299                                       ca_thread_pool_t handle,
300                                       CADataSendMethod_t retransmissionSendMethod,
301                                       CATimeoutCallback_t timeoutCallback,
302                                       CARetransmissionConfig_t* config)
303 {
304     if (NULL == context)
305     {
306         OIC_LOG(ERROR, TAG, "thread instance is empty");
307         return CA_STATUS_INVALID_PARAM;
308     }
309     if (NULL == handle)
310     {
311         OIC_LOG(ERROR, TAG, "thread pool handle is empty");
312         return CA_STATUS_INVALID_PARAM;
313     }
314     OIC_LOG(DEBUG, TAG, "thread initialize");
315
316     memset(context, 0, sizeof(CARetransmission_t));
317
318     CARetransmissionConfig_t cfg = { .supportType = DEFAULT_RETRANSMISSION_TYPE,
319                                      .tryingCount = DEFAULT_RETRANSMISSION_COUNT };
320
321     if (config)
322     {
323         cfg = *config;
324     }
325
326     // set send thread data
327     context->threadPool = handle;
328     context->threadMutex = oc_mutex_new();
329     context->threadCond = oc_cond_new();
330     context->dataSendMethod = retransmissionSendMethod;
331     context->timeoutCallback = timeoutCallback;
332     context->config = cfg;
333     context->isStop = false;
334     context->dataList = u_arraylist_create();
335
336     return CA_STATUS_OK;
337 }
338
339 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
340                                     const CAEndpoint_t *endpoint,
341                                     CADataType_t dataType,
342                                     const void *pdu, uint32_t size)
343 {
344     if (NULL == context || NULL == endpoint || NULL == pdu)
345     {
346         OIC_LOG(ERROR, TAG, "invalid parameter");
347         return CA_STATUS_INVALID_PARAM;
348     }
349
350     // #0. check support transport type
351     if (!(context->config.supportType & endpoint->adapter))
352     {
353         OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
354         return CA_NOT_SUPPORTED;
355     }
356
357     // #1. check PDU method type and get message id.
358     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
359     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
360
361     OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
362
363     if (CA_MSG_CONFIRM != type)
364     {
365         OIC_LOG(DEBUG, TAG, "not supported message type");
366         return CA_NOT_SUPPORTED;
367     }
368
369     // create retransmission data
370     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
371                                           1, sizeof(CARetransmissionData_t));
372
373     if (NULL == retData)
374     {
375         OIC_LOG(ERROR, TAG, "memory error");
376         return CA_MEMORY_ALLOC_FAILED;
377     }
378
379     // copy PDU data
380     void *pduData = (void *) OICMalloc(size);
381     if (NULL == pduData)
382     {
383         OICFree(retData);
384         OIC_LOG(ERROR, TAG, "memory error");
385         return CA_MEMORY_ALLOC_FAILED;
386     }
387     memcpy(pduData, pdu, size);
388
389     // clone remote endpoint
390     CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
391     if (NULL == remoteEndpoint)
392     {
393         OICFree(retData);
394         OICFree(pduData);
395         OIC_LOG(ERROR, TAG, "memory error");
396         return CA_MEMORY_ALLOC_FAILED;
397     }
398
399     // #2. add additional information. (time stamp, retransmission count...)
400     retData->timeStamp = OICGetCurrentTime(TIME_IN_US);
401     retData->timeout = CAGetTimeoutValue();
402     retData->triedCount = 0;
403     retData->messageId = messageId;
404     retData->endpoint = remoteEndpoint;
405     retData->pdu = pduData;
406     retData->size = size;
407     retData->dataType = dataType;
408     // mutex lock
409     oc_mutex_lock(context->threadMutex);
410
411     size_t len = u_arraylist_length(context->dataList);
412
413     // #3. add data into list
414     for (size_t i = 0; i < len; i++)
415     {
416         CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
417
418         if (NULL == currData)
419         {
420             continue;
421         }
422
423         // found index
424         if (NULL != currData->endpoint && currData->messageId == messageId
425             && (currData->endpoint->adapter == endpoint->adapter))
426         {
427             OIC_LOG(ERROR, TAG, "Duplicate message ID");
428
429             // mutex unlock
430             oc_mutex_unlock(context->threadMutex);
431
432             OICFree(retData);
433             OICFree(pduData);
434             OICFree(remoteEndpoint);
435             return CA_STATUS_FAILED;
436         }
437     }
438
439     u_arraylist_add(context->dataList, (void *) retData);
440
441     // notify the thread
442     oc_cond_signal(context->threadCond);
443
444     // mutex unlock
445     oc_mutex_unlock(context->threadMutex);
446
447     return CA_STATUS_OK;
448 }
449
450 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
451                                         const CAEndpoint_t *endpoint, const void *pdu,
452                                         uint32_t size, void **retransmissionPdu)
453 {
454     OIC_LOG(DEBUG, TAG, "IN");
455     if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
456     {
457         OIC_LOG(ERROR, TAG, "invalid parameter");
458         return CA_STATUS_INVALID_PARAM;
459     }
460
461     // #0. check support transport type
462     if (!(context->config.supportType & endpoint->adapter))
463     {
464         OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
465         return CA_STATUS_OK;
466     }
467
468     // #1. check PDU method type and get message id.
469     // ACK, RST --> remove the CON data
470     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
471     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
472     CAResponseResult_t code = CAGetCodeFromPduBinaryData(pdu, size);
473
474     OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d, code=%d",
475               type, messageId, code);
476
477     if (((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
478         || (CA_MSG_RESET == type && CA_EMPTY != code))
479     {
480         return CA_STATUS_OK;
481     }
482
483     // mutex lock
484     oc_mutex_lock(context->threadMutex);
485     size_t len = u_arraylist_length(context->dataList);
486
487     for (size_t i = 0; i < len; i++)
488     {
489         CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
490                 context->dataList, i);
491
492         if (NULL == retData)
493         {
494             continue;
495         }
496
497         // found index
498         if (NULL != retData->endpoint && retData->messageId == messageId
499             && (retData->endpoint->adapter == endpoint->adapter))
500         {
501             // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
502             // if retransmission was finish..token will be unavailable.
503             if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
504             {
505                 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
506
507                 if (NULL == retData->pdu)
508                 {
509                     OIC_LOG(ERROR, TAG, "retData->pdu is null");
510                     OICFree(retData);
511                     // mutex unlock
512                     oc_mutex_unlock(context->threadMutex);
513
514                     return CA_STATUS_FAILED;
515                 }
516
517                 // copy PDU data
518                 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
519                 if ((*retransmissionPdu) == NULL)
520                 {
521                     OICFree(retData);
522                     OIC_LOG(ERROR, TAG, "memory error");
523
524                     // mutex unlock
525                     oc_mutex_unlock(context->threadMutex);
526
527                     return CA_MEMORY_ALLOC_FAILED;
528                 }
529                 memcpy((*retransmissionPdu), retData->pdu, retData->size);
530             }
531
532             // #2. remove data from list
533             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
534             if (NULL == removedData)
535             {
536                 OIC_LOG(ERROR, TAG, "Removed data is NULL");
537
538                 // mutex unlock
539                 oc_mutex_unlock(context->threadMutex);
540
541                 return CA_STATUS_FAILED;
542             }
543
544             OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
545
546             CAFreeEndpoint(removedData->endpoint);
547             OICFree(removedData->pdu);
548             OICFree(removedData);
549
550             break;
551         }
552     }
553
554     // mutex unlock
555     oc_mutex_unlock(context->threadMutex);
556
557     OIC_LOG(DEBUG, TAG, "OUT");
558     return CA_STATUS_OK;
559 }
560
561 CAResult_t CARetransmissionStop(CARetransmission_t *context)
562 {
563     if (NULL == context)
564     {
565         OIC_LOG(ERROR, TAG, "context is empty..");
566         return CA_STATUS_INVALID_PARAM;
567     }
568
569     OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
570
571     // mutex lock
572     oc_mutex_lock(context->threadMutex);
573
574     // set stop flag
575     context->isStop = true;
576
577     // notify the thread
578     oc_cond_signal(context->threadCond);
579
580     oc_cond_wait(context->threadCond, context->threadMutex);
581
582     // mutex unlock
583     oc_mutex_unlock(context->threadMutex);
584
585     return CA_STATUS_OK;
586 }
587
588 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
589 {
590     if (NULL == context)
591     {
592         OIC_LOG(ERROR, TAG, "context is empty..");
593         return CA_STATUS_INVALID_PARAM;
594     }
595
596     OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
597
598     oc_mutex_lock(context->threadMutex);
599     size_t len = u_arraylist_length(context->dataList);
600     for (size_t i = 0; i < len; i++)
601     {
602         CARetransmissionData_t *data = u_arraylist_get(context->dataList, i);
603         if (NULL == data)
604         {
605             continue;
606         }
607         CAFreeEndpoint(data->endpoint);
608         OICFree(data->pdu);
609         OICFree(data);
610     }
611     oc_mutex_unlock(context->threadMutex);
612
613     oc_mutex_free(context->threadMutex);
614     context->threadMutex = NULL;
615     oc_cond_free(context->threadCond);
616     u_arraylist_free(&context->dataList);
617
618     return CA_STATUS_OK;
619 }