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
45 #include <tinyara/config.h>
48 #include "iotivity_config.h"
60 #ifdef HAVE_SYS_TIME_H
63 #ifdef HAVE_SYS_TIMEB_H
64 #include <sys/timeb.h>
71 #if defined(__ANDROID__)
72 #include <linux/time.h>
75 #include "caretransmission.h"
76 #include "caremotehandler.h"
77 #include "caprotocolmessage.h"
78 #include "oic_malloc.h"
83 #define TAG "OIC_CA_RETRANS"
87 uint64_t timeStamp; /**< last sent time. microseconds */
89 uint64_t timeout; /**< timeout value. microseconds */
91 uint8_t triedCount; /**< retransmission count */
92 uint16_t messageId; /**< coap PDU message id */
93 CADataType_t dataType; /**< data Type (Request/Response) */
94 CAEndpoint_t *endpoint; /**< remote endpoint */
95 void *pdu; /**< coap PDU */
96 uint32_t size; /**< coap PDU size */
97 } CARetransmissionData_t;
99 static const uint64_t USECS_PER_SEC = 1000000;
100 static const uint64_t MSECS_PER_SEC = 1000;
102 #ifndef SINGLE_THREAD
104 * @brief timeout value is
105 * between DEFAULT_ACK_TIMEOUT_SEC and
106 * (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
107 * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
108 * @return microseconds.
110 static uint64_t CAGetTimeoutValue()
112 return ((DEFAULT_ACK_TIMEOUT_SEC * MSECS_PER_SEC)
113 + ((MSECS_PER_SEC * OCGetRandomByte()) >> 8)) * MSECS_PER_SEC;
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 CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
134 context, NULL, "IoT_Retransmit",
135 CONFIG_IOTIVITY_RETRANSMIT_PTHREAD_STACKSIZE);
137 if (CA_STATUS_OK != res)
139 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
148 * @brief check timeout routine
149 * @param currentTime [IN]microseconds
150 * @param retData [IN]retransmission data
151 * @return true if the timeout period has elapsed, false otherwise
153 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
155 #ifndef SINGLE_THREAD
156 // #1. calculate timeout
157 uint32_t milliTimeoutValue = retData->timeout * 0.001;
158 uint64_t timeout = ((uint64_t) milliTimeoutValue << retData->triedCount) * MSECS_PER_SEC;
160 if (currentTime >= retData->timeStamp + timeout)
163 OIC_LOG_V(DEBUG, TAG, "%" PRIu64 " microseconds time out!!, tried count(%d)",
164 timeout, retData->triedCount);
169 // #1. calculate timeout
170 uint64_t timeOut = (2 << retData->triedCount) * USECS_PER_SEC;
172 if (currentTime >= retData->timeStamp + timeOut)
174 OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d",
175 (2 << retData->triedCount), retData->triedCount);
182 static void CACheckRetransmissionList(CARetransmission_t *context)
186 OIC_LOG(ERROR, TAG, "context is null");
191 oc_mutex_lock(context->threadMutex);
194 uint32_t len = u_arraylist_length(context->dataList);
196 for (i = 0; i < len; i++)
198 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
205 uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
207 if (CACheckTimeout(currentTime, retData))
209 // #2. if time's up, send the data.
210 if (NULL != context->dataSendMethod)
212 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
214 context->dataSendMethod(retData->endpoint, retData->pdu,
215 retData->size, retData->dataType);
218 // #3. increase the retransmission count and update timestamp.
219 retData->timeStamp = currentTime;
220 retData->triedCount++;
223 // #4. if tried count is max, remove the retransmission data from list.
224 if (retData->triedCount >= context->config.tryingCount)
226 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
227 if (NULL == removedData)
229 OIC_LOG(ERROR, TAG, "Removed data is NULL");
231 oc_mutex_unlock(context->threadMutex);
234 OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
235 "msgid=%d", removedData->messageId);
237 // callback for retransmit timeout
238 if (NULL != context->timeoutCallback)
240 context->timeoutCallback(removedData->endpoint, removedData->pdu,
244 CAFreeEndpoint(removedData->endpoint);
245 OICFree(removedData->pdu);
247 OICFree(removedData);
249 // modify loop value.
250 len = u_arraylist_length(context->dataList);
256 oc_mutex_unlock(context->threadMutex);
259 void CARetransmissionBaseRoutine(void *threadValue)
261 OIC_LOG(DEBUG, TAG, "retransmission main thread start");
263 CARetransmission_t *context = (CARetransmission_t *) threadValue;
267 OIC_LOG(ERROR, TAG, "thread data passing error");
273 if (true == context->isStop)
275 OIC_LOG(DEBUG, TAG, "thread stopped");
278 CACheckRetransmissionList(context);
281 while (!context->isStop)
284 oc_mutex_lock(context->threadMutex);
286 if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
288 // if list is empty, thread will wait
289 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
292 oc_cond_wait(context->threadCond, context->threadMutex);
294 OIC_LOG(DEBUG, TAG, "wake up..");
296 else if (!context->isStop)
298 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
300 OIC_LOG_V(DEBUG, TAG, "wait..(%" PRIu64 ")microseconds",
301 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
305 uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
306 oc_cond_wait_for(context->threadCond, context->threadMutex, absTime );
310 // we are stopping, so we want to unlock and finish stopping
314 oc_mutex_unlock(context->threadMutex);
322 CACheckRetransmissionList(context);
325 oc_mutex_lock(context->threadMutex);
326 oc_cond_signal(context->threadCond);
327 oc_mutex_unlock(context->threadMutex);
330 OIC_LOG(DEBUG, TAG, "retransmission main thread end");
334 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
335 ca_thread_pool_t handle,
336 CADataSendMethod_t retransmissionSendMethod,
337 CATimeoutCallback_t timeoutCallback,
338 CARetransmissionConfig_t* config)
342 OIC_LOG(ERROR, TAG, "thread instance is empty");
343 return CA_STATUS_INVALID_PARAM;
345 #ifndef SINGLE_THREAD
348 OIC_LOG(ERROR, TAG, "thread pool handle is empty");
349 return CA_STATUS_INVALID_PARAM;
352 OIC_LOG(DEBUG, TAG, "thread initialize");
354 memset(context, 0, sizeof(CARetransmission_t));
356 CARetransmissionConfig_t cfg = { .supportType = DEFAULT_RETRANSMISSION_TYPE,
357 .tryingCount = DEFAULT_RETRANSMISSION_COUNT };
364 // set send thread data
365 context->threadPool = handle;
366 context->threadMutex = oc_mutex_new();
367 context->threadCond = oc_cond_new();
368 context->dataSendMethod = retransmissionSendMethod;
369 context->timeoutCallback = timeoutCallback;
370 context->config = cfg;
371 context->isStop = false;
372 context->dataList = u_arraylist_create();
377 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
378 const CAEndpoint_t *endpoint,
379 CADataType_t dataType,
380 const void *pdu, uint32_t size)
382 if (NULL == context || NULL == endpoint || NULL == pdu)
384 OIC_LOG(ERROR, TAG, "invalid parameter");
385 return CA_STATUS_INVALID_PARAM;
388 // #0. check support transport type
389 if (!(context->config.supportType & endpoint->adapter))
391 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
392 return CA_NOT_SUPPORTED;
395 // #1. check PDU method type and get message id.
396 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
397 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
399 OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
401 if (CA_MSG_CONFIRM != type)
403 OIC_LOG(DEBUG, TAG, "not supported message type");
404 return CA_NOT_SUPPORTED;
407 // create retransmission data
408 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
409 1, sizeof(CARetransmissionData_t));
413 OIC_LOG(ERROR, TAG, "memory error");
414 return CA_MEMORY_ALLOC_FAILED;
418 void *pduData = (void *) OICMalloc(size);
422 OIC_LOG(ERROR, TAG, "memory error");
423 return CA_MEMORY_ALLOC_FAILED;
425 memcpy(pduData, pdu, size);
427 // clone remote endpoint
428 CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
429 if (NULL == remoteEndpoint)
433 OIC_LOG(ERROR, TAG, "memory error");
434 return CA_MEMORY_ALLOC_FAILED;
437 // #2. add additional information. (time stamp, retransmission count...)
438 retData->timeStamp = OICGetCurrentTime(TIME_IN_US);
439 #ifndef SINGLE_THREAD
440 retData->timeout = CAGetTimeoutValue();
442 retData->triedCount = 0;
443 retData->messageId = messageId;
444 retData->endpoint = remoteEndpoint;
445 retData->pdu = pduData;
446 retData->size = size;
447 retData->dataType = dataType;
448 #ifndef SINGLE_THREAD
450 oc_mutex_lock(context->threadMutex);
453 uint32_t len = u_arraylist_length(context->dataList);
455 // #3. add data into list
456 for (i = 0; i < len; i++)
458 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
460 if (NULL == currData)
466 if (NULL != currData->endpoint && currData->messageId == messageId
467 && (currData->endpoint->adapter == endpoint->adapter))
469 OIC_LOG(ERROR, TAG, "Duplicate message ID");
472 oc_mutex_unlock(context->threadMutex);
476 OICFree(remoteEndpoint);
477 return CA_STATUS_FAILED;
481 bool res = u_arraylist_add(context->dataList, (void *) retData);
484 OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
486 oc_mutex_unlock(context->threadMutex);
490 OICFree(remoteEndpoint);
491 return CA_MEMORY_ALLOC_FAILED;
495 oc_cond_signal(context->threadCond);
498 oc_mutex_unlock(context->threadMutex);
501 bool res = u_arraylist_add(context->dataList, (void *) retData);
504 OIC_LOG(ERROR, TAG, "u_arraylist_add failed.");
506 oc_mutex_unlock(context->threadMutex);
510 OICFree(remoteEndpoint);
511 return CA_MEMORY_ALLOC_FAILED;
514 CACheckRetransmissionList(context);
519 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
520 const CAEndpoint_t *endpoint, const void *pdu,
521 uint32_t size, void **retransmissionPdu)
523 OIC_LOG(DEBUG, TAG, "IN");
524 if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
526 OIC_LOG(ERROR, TAG, "invalid parameter");
527 return CA_STATUS_INVALID_PARAM;
530 // #0. check support transport type
531 if (!(context->config.supportType & endpoint->adapter))
533 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
537 // #1. check PDU method type and get message id.
538 // ACK, RST --> remove the CON data
539 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
540 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
541 CAResponseResult_t code = CAGetCodeFromPduBinaryData(pdu, size);
543 OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d, code=%d",
544 type, messageId, code);
546 if (((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
547 || (CA_MSG_RESET == type && CA_EMPTY != code))
553 oc_mutex_lock(context->threadMutex);
554 uint32_t len = u_arraylist_length(context->dataList);
558 for (i = 0; i < len; i++)
560 CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
561 context->dataList, i);
569 if (NULL != retData->endpoint && retData->messageId == messageId
570 && (retData->endpoint->adapter == endpoint->adapter))
572 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
573 // if retransmission was finish..token will be unavailable.
574 if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
576 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
578 if (NULL == retData->pdu)
580 OIC_LOG(ERROR, TAG, "retData->pdu is null");
583 oc_mutex_unlock(context->threadMutex);
585 return CA_STATUS_FAILED;
589 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
590 if ((*retransmissionPdu) == NULL)
593 OIC_LOG(ERROR, TAG, "memory error");
596 oc_mutex_unlock(context->threadMutex);
598 return CA_MEMORY_ALLOC_FAILED;
600 memcpy((*retransmissionPdu), retData->pdu, retData->size);
603 // #2. remove data from list
604 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
605 if (NULL == removedData)
607 OIC_LOG(ERROR, TAG, "Removed data is NULL");
610 oc_mutex_unlock(context->threadMutex);
612 return CA_STATUS_FAILED;
615 OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
617 CAFreeEndpoint(removedData->endpoint);
618 OICFree(removedData->pdu);
619 OICFree(removedData);
626 oc_mutex_unlock(context->threadMutex);
628 OIC_LOG(DEBUG, TAG, "OUT");
632 CAResult_t CARetransmissionClearAdapterData(CARetransmission_t *context, CATransportAdapter_t type)
636 OIC_LOG(ERROR, TAG, "thread instance is empty..");
637 return CA_STATUS_INVALID_PARAM;
640 OIC_LOG_V(DEBUG, TAG, "clear queue data for Adapter : %d ", type);
643 oc_mutex_lock(context->threadMutex);
645 uint32_t len = u_arraylist_length(context->dataList);
649 for (i = 0; i < len; i++)
652 CARetransmissionData_t *removedData = (CARetransmissionData_t *) u_arraylist_get(
653 context->dataList, i);
656 if (NULL != removedData && NULL != removedData->endpoint &&
657 (removedData->endpoint->adapter == type))
659 CARetransmissionData_t *remData = (CARetransmissionData_t *) u_arraylist_remove(
660 context->dataList, i);
665 CAFreeEndpoint(remData->endpoint);
666 OICFree(remData->pdu);
668 len = u_arraylist_length(context->dataList);
674 oc_mutex_unlock(context->threadMutex);
679 CAResult_t CARetransmissionStop(CARetransmission_t *context)
683 OIC_LOG(ERROR, TAG, "context is empty..");
684 return CA_STATUS_INVALID_PARAM;
687 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
690 oc_mutex_lock(context->threadMutex);
693 context->isStop = true;
696 oc_cond_signal(context->threadCond);
698 oc_cond_wait(context->threadCond, context->threadMutex);
701 oc_mutex_unlock(context->threadMutex);
706 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
710 OIC_LOG(ERROR, TAG, "context is empty..");
711 return CA_STATUS_INVALID_PARAM;
714 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
716 oc_mutex_free(context->threadMutex);
717 context->threadMutex = NULL;
718 oc_cond_free(context->threadCond);
719 u_arraylist_free(&context->dataList);