Code portability cleanup.
[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("RET")
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]
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     // mutex lock
108     u_mutex_lock(context->threadMutex);
109
110     int32_t i = 0;
111     uint32_t len = u_arraylist_length(context->dataList);
112
113     for (i = 0; i < len; i++)
114     {
115         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
116
117         if (retData == NULL)
118         {
119             continue;
120         }
121
122         uint64_t currentTime = getCurrentTimeInMicroSeconds();
123
124         if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
125         {
126             // #2. if time's up, send the data.
127             if (context->dataSendMethod != NULL)
128             {
129                 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
130                           retData->messageId);
131                 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
132             }
133
134             // #3. increase the retransmission count and update timestamp.
135             retData->timeStamp = currentTime;
136             retData->triedCount++;
137         }
138
139         // #4. if tried count is max, remove the retransmission data from list.
140         if (retData->triedCount >= context->config.tryingCount)
141         {
142             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
143
144             if (removedData != NULL)
145             {
146                 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
147                         message id(%d)", removedData->messageId);
148
149                 // callback for retransmit timeout
150                 if (context->timeoutCallback != NULL)
151                 {
152                     context->timeoutCallback(removedData->endpoint, removedData->pdu,
153                             removedData->size);
154                 }
155
156                 CADestroyRemoteEndpointInternal(removedData->endpoint);
157                 OICFree(removedData->pdu);
158
159                 OICFree(removedData);
160
161                 // modify loop value.
162                 len = u_arraylist_length(context->dataList);
163
164                 --i;
165             }
166             else
167             {
168                 OIC_LOG(ERROR, TAG, "arraylist remove error");
169             }
170
171         }
172     }
173
174     // mutex unlock
175     u_mutex_unlock(context->threadMutex);
176 }
177
178 static void CARetransmissionBaseRoutine(void *threadValue)
179 {
180     OIC_LOG(DEBUG, TAG, "retransmission main thread start..");
181
182     CARetransmission_t *context = (CARetransmission_t *) threadValue;
183
184     if (context == NULL)
185     {
186         OIC_LOG(ERROR, TAG, "thread data passing error!!");
187
188         return;
189     }
190
191     while (!context->isStop)
192     {
193         // mutex lock
194         u_mutex_lock(context->threadMutex);
195
196         if (u_arraylist_length(context->dataList) <= 0)
197         {
198             // if list is empty, thread will wait
199             OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
200
201             // wait
202             u_cond_wait(context->threadCond, context->threadMutex);
203
204             OIC_LOG(DEBUG, TAG, "wake up..");
205         }
206         else
207         {
208             // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
209             OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds",
210                         RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
211
212             // wait
213             u_cond_wait_until(context->threadCond, context->threadMutex,
214                               RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
215         }
216
217         // mutex unlock
218         u_mutex_unlock(context->threadMutex);
219
220         // check stop flag
221         if (context->isStop)
222         {
223             continue;
224         }
225
226         CACheckRetransmissionList(context);
227     }
228
229     u_cond_signal(context->threadCond);
230
231     OIC_LOG(DEBUG, TAG, "retransmission main thread end..");
232
233 }
234
235 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
236         CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
237         CARetransmissionConfig_t* config)
238 {
239     if (context == NULL)
240     {
241         OIC_LOG(ERROR, TAG, "thread instance is empty..");
242         return CA_STATUS_FAILED;
243     }
244
245     if (handle == NULL)
246     {
247         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
248         return CA_STATUS_FAILED;
249     }
250
251     OIC_LOG(DEBUG, TAG, "thread initialize..");
252
253     memset(context, 0, sizeof(CARetransmission_t));
254
255     CARetransmissionConfig_t cfg = { 0 };
256
257     if (config == NULL)
258     {
259         // setDefault
260         cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
261         cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
262     }
263     else
264     {
265         cfg = *config;
266     }
267
268     // set send thread data
269     context->threadPool = handle;
270     context->threadMutex = u_mutex_new();
271     context->threadCond = u_cond_new();
272     context->dataSendMethod = retransmissionSendMethod;
273     context->timeoutCallback = timeoutCallback;
274     context->config = cfg;
275     context->isStop = false;
276     context->dataList = u_arraylist_create();
277
278     return CA_STATUS_OK;
279 }
280
281 CAResult_t CARetransmissionStart(CARetransmission_t *context)
282 {
283     if (context == NULL)
284     {
285         OIC_LOG(ERROR, TAG, "context is empty..");
286         return CA_STATUS_FAILED;
287     }
288
289     if (context->threadPool == NULL)
290     {
291         OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
292         return CA_STATUS_FAILED;
293     }
294
295     CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
296                                             context);
297
298     if (res != CA_STATUS_OK)
299     {
300         OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
301         return res;
302     }
303
304     return res;
305 }
306
307 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
308     const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
309 {
310     if (context == NULL || endpoint == NULL || pdu == NULL)
311     {
312         OIC_LOG(ERROR, TAG, "invalid parameter..");
313         return CA_STATUS_INVALID_PARAM;
314     }
315
316     // #0. check support connectivity type
317     if (!(context->config.supportType & endpoint->connectivityType))
318     {
319         OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
320                   endpoint->connectivityType);
321         return CA_STATUS_OK;
322     }
323
324     // #1. check PDU method type and get message id.
325     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
326     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
327
328     OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
329
330     if (type != CA_MSG_CONFIRM)
331     {
332         return CA_STATUS_OK;
333     }
334
335     // create retransmission data
336     CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
337                                           1, sizeof(CARetransmissionData_t));
338
339     if (retData == NULL)
340     {
341         OIC_LOG(ERROR, TAG, "memory error!!");
342         return CA_MEMORY_ALLOC_FAILED;
343     }
344
345     // copy PDU data
346     void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
347     if (pduData == NULL)
348     {
349         OICFree(retData);
350         OIC_LOG(ERROR, TAG, "memory error!!");
351         return CA_MEMORY_ALLOC_FAILED;
352     }
353     memcpy(pduData, pdu, sizeof(int8_t) * size);
354
355     // clone remote endpoint
356     CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
357     if (remoteEndpoint == NULL)
358     {
359         OICFree(retData);
360         OICFree(pduData);
361         OIC_LOG(ERROR, TAG, "memory error!!");
362         return CA_MEMORY_ALLOC_FAILED;
363     }
364
365     // #2. add additional information. (time stamp, retransmission count...)
366     retData->timeStamp = getCurrentTimeInMicroSeconds();
367     retData->timeout = CAGetTimeoutValue();
368     retData->triedCount = 0;
369     retData->messageId = messageId;
370     retData->endpoint = remoteEndpoint;
371     retData->pdu = pduData;
372     retData->size = size;
373
374     // mutex lock
375     u_mutex_lock(context->threadMutex);
376
377     uint32_t i = 0;
378     uint32_t len = u_arraylist_length(context->dataList);
379
380     // #3. add data into list
381     for (i = 0; i < len; i++)
382     {
383         CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
384
385         if (currData == NULL)
386         {
387             continue;
388         }
389
390         // found index
391         if (retData != NULL && (currData->endpoint->connectivityType == endpoint->connectivityType)
392             && currData->messageId == messageId)
393         {
394             OIC_LOG(ERROR, TAG, "Duplicate message ID");
395
396             // mutex unlock
397             u_mutex_unlock(context->threadMutex);
398
399             OICFree(retData);
400             OICFree(pduData);
401             OICFree(remoteEndpoint);
402             return CA_STATUS_FAILED;
403         }
404     }
405
406     u_arraylist_add(context->dataList, (void *) retData);
407
408     // notity the thread
409     u_cond_signal(context->threadCond);
410
411     // mutex unlock
412     u_mutex_unlock(context->threadMutex);
413
414     return CA_STATUS_OK;
415 }
416
417 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
418                                         const CARemoteEndpoint_t *endpoint, const void *pdu,
419                                         uint32_t size)
420 {
421     if (context == NULL || endpoint == NULL || pdu == NULL)
422     {
423         OIC_LOG(ERROR, TAG, "invalid parameter..");
424         return CA_STATUS_INVALID_PARAM;
425     }
426
427     // #0. check support connectivity type
428     if (!(context->config.supportType & endpoint->connectivityType))
429     {
430         OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
431                   endpoint->connectivityType);
432         return CA_STATUS_OK;
433     }
434
435     // #1. check PDU method type and get message id.
436     // ACK, RST --> remove the CON data
437     CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
438     uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
439
440     OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
441
442     if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
443     {
444         return CA_STATUS_OK;
445     }
446
447     // mutex lock
448     u_mutex_lock(context->threadMutex);
449
450     uint32_t i = 0;
451     uint32_t len = u_arraylist_length(context->dataList);
452
453     // find index
454     for (i = 0; i < len; i++)
455     {
456         CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
457
458         // found index
459         if (retData != NULL && (retData->endpoint->connectivityType == endpoint->connectivityType)
460             && retData->messageId == messageId)
461         {
462             // #2. remove data from list
463             CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
464
465             if (removedData != NULL)
466             {
467                 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)",
468                         messageId);
469
470                 CADestroyRemoteEndpointInternal(removedData->endpoint);
471                 OICFree(removedData->pdu);
472
473                 OICFree(removedData);
474             }
475             break;
476         }
477     }
478
479     // mutex unlock
480     u_mutex_unlock(context->threadMutex);
481
482     return CA_STATUS_OK;
483 }
484
485 CAResult_t CARetransmissionStop(CARetransmission_t *context)
486 {
487     if (context == NULL)
488     {
489         OIC_LOG(ERROR, TAG, "context is empty..");
490         return CA_STATUS_FAILED;
491     }
492
493     OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
494
495     // mutex lock
496     u_mutex_lock(context->threadMutex);
497
498     // set stop flag
499     context->isStop = true;
500
501     // notity the thread
502     u_cond_signal(context->threadCond);
503
504     u_cond_wait(context->threadCond, context->threadMutex);
505
506     // mutex unlock
507     u_mutex_unlock(context->threadMutex);
508
509     return CA_STATUS_OK;
510 }
511
512 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
513 {
514     if (context == NULL)
515     {
516         OIC_LOG(ERROR, TAG, "context is empty..");
517         return CA_STATUS_FAILED;
518     }
519
520     OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
521
522     u_mutex_free(context->threadMutex);
523     context->threadMutex = NULL;
524     u_cond_free(context->threadCond);
525     u_arraylist_free(&context->dataList);
526
527     return CA_STATUS_OK;
528 }
529
530 uint64_t getCurrentTimeInMicroSeconds()
531 {
532     uint64_t currentTime = 0;
533
534 #ifdef __ANDROID__
535     struct timespec getTs;
536
537     clock_gettime(CLOCK_MONOTONIC, &getTs);
538
539     currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
540     OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
541 #else
542     currentTime = g_get_monotonic_time();
543 #endif
544     return currentTime;
545 }
546