1 /******************************************************************
3 * Copyright 2014 Samsung Electronics All Rights Reserved.
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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.
19 ******************************************************************/
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
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
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
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
52 #ifdef HAVE_SYS_TIME_H
56 #include <sys/timeb.h>
63 #if defined(__ANDROID__)
64 #include <linux/time.h>
67 #include "caretransmission.h"
68 #include "caremotehandler.h"
69 #include "caprotocolmessage.h"
70 #include "oic_malloc.h"
73 #define TAG "OIC_CA_RETRANS"
77 uint64_t timeStamp; /**< last sent time. microseconds */
79 uint64_t timeout; /**< timeout value. microseconds */
81 uint8_t triedCount; /**< retransmission count */
82 uint16_t messageId; /**< coap PDU message id */
83 CAEndpoint_t *endpoint; /**< remote endpoint */
84 void *pdu; /**< coap PDU */
85 uint32_t size; /**< coap PDU size */
86 } CARetransmissionData_t;
88 static const uint64_t USECS_PER_SEC = 1000000;
89 static const uint64_t MSECS_PER_SEC = 1000;
92 * @brief getCurrent monotonic time
93 * @return current time in microseconds
95 uint64_t getCurrentTimeInMicroSeconds();
99 * @brief timeout value is
100 * between DEFAULT_ACK_TIMEOUT_SEC and
101 * (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
102 * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
103 * @return microseconds.
105 static uint64_t CAGetTimeoutValue()
108 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (random() & 0xFF)) >> 8)) *
111 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (rand() & 0xFF)) >> 8)) *
116 CAResult_t CARetransmissionStart(CARetransmission_t *context)
120 OIC_LOG(ERROR, TAG, "context is empty");
121 return CA_STATUS_INVALID_PARAM;
124 if (NULL == context->threadPool)
126 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
127 return CA_STATUS_INVALID_PARAM;
130 CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
133 if (CA_STATUS_OK != res)
135 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
144 * @brief check timeout routine
145 * @param currentTime [IN]microseconds
146 * @param retData [IN]retransmission data
147 * @return true if the timeout period has elapsed, false otherwise
149 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
151 #ifndef SINGLE_THREAD
152 // #1. calculate timeout
153 uint32_t milliTimeoutValue = retData->timeout * 0.001;
154 uint64_t timeout = (milliTimeoutValue << retData->triedCount) * (uint64_t) 1000;
156 if (currentTime >= retData->timeStamp + timeout)
158 OIC_LOG_V(DEBUG, TAG, "%llu microseconds time out!!, tried count(%d)",
159 timeout, retData->triedCount);
163 // #1. calculate timeout
164 uint64_t timeOut = (2 << retData->triedCount) * 1000000;
166 if (currentTime >= retData->timeStamp + timeOut)
168 OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d",
169 (2 << retData->triedCount), retData->triedCount);
176 static void CACheckRetransmissionList(CARetransmission_t *context)
180 OIC_LOG(ERROR, TAG, "context is null");
185 ca_mutex_lock(context->threadMutex);
188 uint32_t len = u_arraylist_length(context->dataList);
190 for (i = 0; i < len; i++)
192 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
199 uint64_t currentTime = getCurrentTimeInMicroSeconds();
201 if (CACheckTimeout(currentTime, retData))
203 // #2. if time's up, send the data.
204 if (NULL != context->dataSendMethod)
206 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
208 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
211 // #3. increase the retransmission count and update timestamp.
212 retData->timeStamp = currentTime;
213 retData->triedCount++;
216 // #4. if tried count is max, remove the retransmission data from list.
217 if (retData->triedCount >= context->config.tryingCount)
219 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
220 if (NULL == removedData)
222 OIC_LOG(ERROR, TAG, "Removed data is NULL");
224 ca_mutex_unlock(context->threadMutex);
227 OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
228 "msgid=%d", removedData->messageId);
230 // callback for retransmit timeout
231 if (NULL != context->timeoutCallback)
233 context->timeoutCallback(removedData->endpoint, removedData->pdu,
237 CAFreeEndpoint(removedData->endpoint);
238 OICFree(removedData->pdu);
240 OICFree(removedData);
242 // modify loop value.
243 len = u_arraylist_length(context->dataList);
249 ca_mutex_unlock(context->threadMutex);
252 void CARetransmissionBaseRoutine(void *threadValue)
254 OIC_LOG(DEBUG, TAG, "retransmission main thread start");
256 CARetransmission_t *context = (CARetransmission_t *) threadValue;
260 OIC_LOG(ERROR, TAG, "thread data passing error");
266 if (true == context->isStop)
268 OIC_LOG(DEBUG, TAG, "thread stopped");
271 CACheckRetransmissionList(context);
274 while (!context->isStop)
277 ca_mutex_lock(context->threadMutex);
279 if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
281 // if list is empty, thread will wait
282 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
285 ca_cond_wait(context->threadCond, context->threadMutex);
287 OIC_LOG(DEBUG, TAG, "wake up..");
289 else if (!context->isStop)
291 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
292 OIC_LOG_V(DEBUG, TAG, "wait..(%lld)microseconds",
293 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
296 uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
297 ca_cond_wait_for(context->threadCond, context->threadMutex, absTime );
301 // we are stopping, so we want to unlock and finish stopping
305 ca_mutex_unlock(context->threadMutex);
313 CACheckRetransmissionList(context);
316 ca_mutex_lock(context->threadMutex);
317 ca_cond_signal(context->threadCond);
318 ca_mutex_unlock(context->threadMutex);
321 OIC_LOG(DEBUG, TAG, "retransmission main thread end");
325 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
326 ca_thread_pool_t handle,
327 CADataSendMethod_t retransmissionSendMethod,
328 CATimeoutCallback_t timeoutCallback,
329 CARetransmissionConfig_t* config)
333 OIC_LOG(ERROR, TAG, "thread instance is empty");
334 return CA_STATUS_INVALID_PARAM;
336 #ifndef SINGLE_THREAD
339 OIC_LOG(ERROR, TAG, "thread pool handle is empty");
340 return CA_STATUS_INVALID_PARAM;
343 OIC_LOG(DEBUG, TAG, "thread initialize");
345 memset(context, 0, sizeof(CARetransmission_t));
347 CARetransmissionConfig_t cfg = { .supportType = DEFAULT_RETRANSMISSION_TYPE,
348 .tryingCount = DEFAULT_RETRANSMISSION_COUNT };
355 // set send thread data
356 context->threadPool = handle;
357 context->threadMutex = ca_mutex_new();
358 context->threadCond = ca_cond_new();
359 context->dataSendMethod = retransmissionSendMethod;
360 context->timeoutCallback = timeoutCallback;
361 context->config = cfg;
362 context->isStop = false;
363 context->dataList = u_arraylist_create();
368 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
369 const CAEndpoint_t *endpoint,
370 const void *pdu, uint32_t size)
372 if (NULL == context || NULL == endpoint || NULL == pdu)
374 OIC_LOG(ERROR, TAG, "invalid parameter");
375 return CA_STATUS_INVALID_PARAM;
378 // #0. check support transport type
379 if (!(context->config.supportType & endpoint->adapter))
381 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
382 return CA_NOT_SUPPORTED;
385 // #1. check PDU method type and get message id.
386 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
387 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
389 OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
391 if (CA_MSG_CONFIRM != type)
393 OIC_LOG(DEBUG, TAG, "not supported message type");
394 return CA_NOT_SUPPORTED;
397 // create retransmission data
398 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
399 1, sizeof(CARetransmissionData_t));
403 OIC_LOG(ERROR, TAG, "memory error");
404 return CA_MEMORY_ALLOC_FAILED;
408 void *pduData = (void *) OICMalloc(size);
412 OIC_LOG(ERROR, TAG, "memory error");
413 return CA_MEMORY_ALLOC_FAILED;
415 memcpy(pduData, pdu, size);
417 // clone remote endpoint
418 CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
419 if (NULL == remoteEndpoint)
423 OIC_LOG(ERROR, TAG, "memory error");
424 return CA_MEMORY_ALLOC_FAILED;
427 // #2. add additional information. (time stamp, retransmission count...)
428 retData->timeStamp = getCurrentTimeInMicroSeconds();
429 #ifndef SINGLE_THREAD
430 retData->timeout = CAGetTimeoutValue();
432 retData->triedCount = 0;
433 retData->messageId = messageId;
434 retData->endpoint = remoteEndpoint;
435 retData->pdu = pduData;
436 retData->size = size;
437 #ifndef SINGLE_THREAD
439 ca_mutex_lock(context->threadMutex);
442 uint32_t len = u_arraylist_length(context->dataList);
444 // #3. add data into list
445 for (i = 0; i < len; i++)
447 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
449 if (NULL == currData)
455 if (NULL != currData->endpoint && currData->messageId == messageId
456 && (currData->endpoint->adapter == endpoint->adapter))
458 OIC_LOG(ERROR, TAG, "Duplicate message ID");
461 ca_mutex_unlock(context->threadMutex);
465 OICFree(remoteEndpoint);
466 return CA_STATUS_FAILED;
470 u_arraylist_add(context->dataList, (void *) retData);
473 ca_cond_signal(context->threadCond);
476 ca_mutex_unlock(context->threadMutex);
479 u_arraylist_add(context->dataList, (void *) retData);
481 CACheckRetransmissionList(context);
486 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
487 const CAEndpoint_t *endpoint, const void *pdu,
488 uint32_t size, void **retransmissionPdu)
490 OIC_LOG(DEBUG, TAG, "IN");
491 if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
493 OIC_LOG(ERROR, TAG, "invalid parameter");
494 return CA_STATUS_INVALID_PARAM;
497 // #0. check support transport type
498 if (!(context->config.supportType & endpoint->adapter))
500 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
504 // #1. check PDU method type and get message id.
505 // ACK, RST --> remove the CON data
506 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
507 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
508 CAResponseResult_t code = CAGetCodeFromPduBinaryData(pdu, size);
510 OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d, code=%d",
511 type, messageId, code);
513 if (((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
514 || (CA_MSG_RESET == type && CA_EMPTY != code))
520 ca_mutex_lock(context->threadMutex);
521 uint32_t len = u_arraylist_length(context->dataList);
525 for (i = 0; i < len; i++)
527 CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
528 context->dataList, i);
536 if (NULL != retData->endpoint && retData->messageId == messageId
537 && (retData->endpoint->adapter == endpoint->adapter))
539 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
540 // if retransmission was finish..token will be unavailable.
541 if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
543 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
545 if (NULL == retData->pdu)
547 OIC_LOG(ERROR, TAG, "retData->pdu is null");
550 ca_mutex_unlock(context->threadMutex);
552 return CA_STATUS_FAILED;
556 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
557 if ((*retransmissionPdu) == NULL)
560 OIC_LOG(ERROR, TAG, "memory error");
563 ca_mutex_unlock(context->threadMutex);
565 return CA_MEMORY_ALLOC_FAILED;
567 memcpy((*retransmissionPdu), retData->pdu, retData->size);
570 // #2. remove data from list
571 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
572 if (NULL == removedData)
574 OIC_LOG(ERROR, TAG, "Removed data is NULL");
577 ca_mutex_unlock(context->threadMutex);
579 return CA_STATUS_FAILED;
582 OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
584 CAFreeEndpoint(removedData->endpoint);
585 OICFree(removedData->pdu);
586 OICFree(removedData);
593 ca_mutex_unlock(context->threadMutex);
595 OIC_LOG(DEBUG, TAG, "OUT");
599 CAResult_t CARetransmissionStop(CARetransmission_t *context)
603 OIC_LOG(ERROR, TAG, "context is empty..");
604 return CA_STATUS_INVALID_PARAM;
607 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
610 ca_mutex_lock(context->threadMutex);
613 context->isStop = true;
616 ca_cond_signal(context->threadCond);
618 ca_cond_wait(context->threadCond, context->threadMutex);
621 ca_mutex_unlock(context->threadMutex);
626 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
630 OIC_LOG(ERROR, TAG, "context is empty..");
631 return CA_STATUS_INVALID_PARAM;
634 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
636 ca_mutex_free(context->threadMutex);
637 context->threadMutex = NULL;
638 ca_cond_free(context->threadCond);
639 u_arraylist_free(&context->dataList);
644 uint64_t getCurrentTimeInMicroSeconds()
646 OIC_LOG(DEBUG, TAG, "IN");
647 uint64_t currentTime = 0;
650 struct timespec getTs;
652 clock_gettime(CLOCK_MONOTONIC, &getTs);
654 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
655 OIC_LOG_V(DEBUG, TAG, "current time = %lld", currentTime);
656 #elif defined __ARDUINO__
657 currentTime = millis() * 1000;
658 OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
660 #if _POSIX_TIMERS > 0
662 clock_gettime(CLOCK_MONOTONIC, &ts);
663 currentTime = ts.tv_sec * USECS_PER_SEC + ts.tv_nsec / 1000;
664 #elif defined(_WIN32)
667 currentTime = tb.time * USECS_PER_SEC + tb.millitm * MSECS_PER_SEC;
670 gettimeofday(&tv, NULL);
671 currentTime = tv.tv_sec * USECS_PER_SEC + tv.tv_usec;
675 OIC_LOG(DEBUG, TAG, "OUT");