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
33 // Defining _POSIX_C_SOURCE macro with 199309L (or greater) as value
34 // causes header files to expose definitions
35 // corresponding to the POSIX.1b, Real-time extensions
36 // (IEEE Std 1003.1b-1993) specification
38 // For this specific file, see use of clock_gettime,
39 // Refer to http://pubs.opengroup.org/stage7tc1/functions/clock_gettime.html
40 // and to http://man7.org/linux/man-pages/man2/clock_gettime.2.html
41 #ifndef _POSIX_C_SOURCE
42 #define _POSIX_C_SOURCE 200809L
55 #if defined(__ANDROID__)
56 #include <linux/time.h>
59 #include "caretransmission.h"
60 #include "caremotehandler.h"
61 #include "caprotocolmessage.h"
62 #include "oic_malloc.h"
65 #define TAG "CA_RETRANS"
69 uint64_t timeStamp; /**< last sent time. microseconds */
71 uint64_t timeout; /**< timeout value. microseconds */
73 uint8_t triedCount; /**< retransmission count */
74 uint16_t messageId; /**< coap PDU message id */
75 CAEndpoint_t *endpoint; /**< remote endpoint */
76 void *pdu; /**< coap PDU */
77 uint32_t size; /**< coap PDU size */
78 } CARetransmissionData_t;
80 static const uint64_t USECS_PER_SEC = 1000000;
83 * @brief getCurrent monotonic time
84 * @return current time in microseconds
86 uint64_t getCurrentTimeInMicroSeconds();
90 * @brief timeout value is
91 * between DEFAULT_ACK_TIMEOUT_SEC and
92 * (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
93 * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
94 * @return microseconds.
96 static uint64_t CAGetTimeoutValue()
98 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (random() & 0xFF)) >> 8)) *
102 CAResult_t CARetransmissionStart(CARetransmission_t *context)
106 OIC_LOG(ERROR, TAG, "context is empty");
107 return CA_STATUS_INVALID_PARAM;
110 if (NULL == context->threadPool)
112 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
113 return CA_STATUS_INVALID_PARAM;
116 CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
119 if (CA_STATUS_OK != res)
121 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
130 * @brief check timeout routine
131 * @param currentTime [IN]microseconds
132 * @param retData [IN]retransmission data
133 * @return true if the timeout period has elapsed, false otherwise
135 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
137 #ifndef SINGLE_THREAD
138 // #1. calculate timeout
139 uint32_t milliTimeoutValue = retData->timeout * 0.001;
140 uint64_t timeout = (milliTimeoutValue << retData->triedCount) * (uint64_t) 1000;
142 if (currentTime >= retData->timeStamp + timeout)
144 OIC_LOG_V(DEBUG, TAG, "%d microseconds time out!!, tried count(%ld)",
145 timeout, retData->triedCount);
149 // #1. calculate timeout
150 uint64_t timeOut = (2 << retData->triedCount) * 1000000;
152 if (currentTime >= retData->timeStamp + timeOut)
154 OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d",
155 (2 << retData->triedCount), retData->triedCount);
162 static void CACheckRetransmissionList(CARetransmission_t *context)
166 OIC_LOG(ERROR, TAG, "context is null");
171 ca_mutex_lock(context->threadMutex);
174 uint32_t len = u_arraylist_length(context->dataList);
176 for (i = 0; i < len; i++)
178 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
185 uint64_t currentTime = getCurrentTimeInMicroSeconds();
187 if (CACheckTimeout(currentTime, retData))
189 // #2. if time's up, send the data.
190 if (NULL != context->dataSendMethod)
192 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
194 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
197 // #3. increase the retransmission count and update timestamp.
198 retData->timeStamp = currentTime;
199 retData->triedCount++;
202 // #4. if tried count is max, remove the retransmission data from list.
203 if (retData->triedCount >= context->config.tryingCount)
205 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
206 if (NULL == removedData)
208 OIC_LOG(ERROR, TAG, "Removed data is NULL");
210 ca_mutex_unlock(context->threadMutex);
213 OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
214 "msgid=%d", removedData->messageId);
216 // callback for retransmit timeout
217 if (NULL != context->timeoutCallback)
219 context->timeoutCallback(removedData->endpoint, removedData->pdu,
223 CAFreeEndpoint(removedData->endpoint);
224 OICFree(removedData->pdu);
226 OICFree(removedData);
228 // modify loop value.
229 len = u_arraylist_length(context->dataList);
235 ca_mutex_unlock(context->threadMutex);
238 void CARetransmissionBaseRoutine(void *threadValue)
240 OIC_LOG(DEBUG, TAG, "retransmission main thread start");
242 CARetransmission_t *context = (CARetransmission_t *) threadValue;
246 OIC_LOG(ERROR, TAG, "thread data passing error");
252 if (true == context->isStop)
254 OIC_LOG(DEBUG, TAG, "thread stopped");
257 CACheckRetransmissionList(context);
260 while (!context->isStop)
263 ca_mutex_lock(context->threadMutex);
265 if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
267 // if list is empty, thread will wait
268 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
271 ca_cond_wait(context->threadCond, context->threadMutex);
273 OIC_LOG(DEBUG, TAG, "wake up..");
275 else if (!context->isStop)
277 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
278 OIC_LOG_V(DEBUG, TAG, "wait..(%ld)microseconds",
279 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
282 uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
283 ca_cond_wait_for(context->threadCond, context->threadMutex, absTime );
287 // we are stopping, so we want to unlock and finish stopping
291 ca_mutex_unlock(context->threadMutex);
299 CACheckRetransmissionList(context);
302 ca_mutex_lock(context->threadMutex);
303 ca_cond_signal(context->threadCond);
304 ca_mutex_unlock(context->threadMutex);
307 OIC_LOG(DEBUG, TAG, "retransmission main thread end");
311 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
312 ca_thread_pool_t handle,
313 CADataSendMethod_t retransmissionSendMethod,
314 CATimeoutCallback_t timeoutCallback,
315 CARetransmissionConfig_t* config)
319 OIC_LOG(ERROR, TAG, "thread instance is empty");
320 return CA_STATUS_INVALID_PARAM;
322 #ifndef SINGLE_THREAD
325 OIC_LOG(ERROR, TAG, "thread pool handle is empty");
326 return CA_STATUS_INVALID_PARAM;
329 OIC_LOG(DEBUG, TAG, "thread initialize");
331 memset(context, 0, sizeof(CARetransmission_t));
333 CARetransmissionConfig_t cfg = { 0 };
338 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
339 cfg.tryingCount = DEFAULT_RETRANSMISSION_COUNT;
346 // set send thread data
347 context->threadPool = handle;
348 context->threadMutex = ca_mutex_new();
349 context->threadCond = ca_cond_new();
350 context->dataSendMethod = retransmissionSendMethod;
351 context->timeoutCallback = timeoutCallback;
352 context->config = cfg;
353 context->isStop = false;
354 context->dataList = u_arraylist_create();
359 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
360 const CAEndpoint_t *endpoint,
361 const void *pdu, uint32_t size)
363 if (NULL == context || NULL == endpoint || NULL == pdu)
365 OIC_LOG(ERROR, TAG, "invalid parameter");
366 return CA_STATUS_INVALID_PARAM;
369 // #0. check support transport type
370 if (!(context->config.supportType & endpoint->adapter))
372 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
373 return CA_NOT_SUPPORTED;
376 // #1. check PDU method type and get message id.
377 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
378 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
380 OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
382 if (CA_MSG_CONFIRM != type)
384 OIC_LOG(DEBUG, TAG, "not supported message type");
385 return CA_NOT_SUPPORTED;
388 // create retransmission data
389 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
390 1, sizeof(CARetransmissionData_t));
394 OIC_LOG(ERROR, TAG, "memory error");
395 return CA_MEMORY_ALLOC_FAILED;
399 void *pduData = (void *) OICMalloc(size);
403 OIC_LOG(ERROR, TAG, "memory error");
404 return CA_MEMORY_ALLOC_FAILED;
406 memcpy(pduData, pdu, size);
408 // clone remote endpoint
409 CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
410 if (NULL == remoteEndpoint)
414 OIC_LOG(ERROR, TAG, "memory error");
415 return CA_MEMORY_ALLOC_FAILED;
418 // #2. add additional information. (time stamp, retransmission count...)
419 retData->timeStamp = getCurrentTimeInMicroSeconds();
420 #ifndef SINGLE_THREAD
421 retData->timeout = CAGetTimeoutValue();
423 retData->triedCount = 0;
424 retData->messageId = messageId;
425 retData->endpoint = remoteEndpoint;
426 retData->pdu = pduData;
427 retData->size = size;
428 #ifndef SINGLE_THREAD
430 ca_mutex_lock(context->threadMutex);
433 uint32_t len = u_arraylist_length(context->dataList);
435 // #3. add data into list
436 for (i = 0; i < len; i++)
438 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
440 if (NULL == currData)
446 if (NULL != currData->endpoint && currData->messageId == messageId
447 && (currData->endpoint->adapter == endpoint->adapter))
449 OIC_LOG(ERROR, TAG, "Duplicate message ID");
452 ca_mutex_unlock(context->threadMutex);
456 OICFree(remoteEndpoint);
457 return CA_STATUS_FAILED;
461 u_arraylist_add(context->dataList, (void *) retData);
464 ca_cond_signal(context->threadCond);
467 ca_mutex_unlock(context->threadMutex);
470 u_arraylist_add(context->dataList, (void *) retData);
472 CACheckRetransmissionList(context);
477 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
478 const CAEndpoint_t *endpoint, const void *pdu,
479 uint32_t size, void **retransmissionPdu)
481 OIC_LOG(DEBUG, TAG, "IN");
482 if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
484 OIC_LOG(ERROR, TAG, "invalid parameter");
485 return CA_STATUS_INVALID_PARAM;
488 // #0. check support transport type
489 if (!(context->config.supportType & endpoint->adapter))
491 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
495 // #1. check PDU method type and get message id.
496 // ACK, RST --> remove the CON data
497 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
498 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
500 OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d", type, messageId);
502 if ((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
508 ca_mutex_lock(context->threadMutex);
509 uint32_t len = u_arraylist_length(context->dataList);
513 for (i = 0; i < len; i++)
515 CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
516 context->dataList, i);
524 if (NULL != retData->endpoint && retData->messageId == messageId
525 && (retData->endpoint->adapter == endpoint->adapter))
527 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
528 // if retransmission was finish..token will be unavailable.
529 if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
531 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
533 if (NULL == retData->pdu)
535 OIC_LOG(ERROR, TAG, "retData->pdu is null");
538 ca_mutex_unlock(context->threadMutex);
540 return CA_STATUS_FAILED;
544 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
545 if ((*retransmissionPdu) == NULL)
548 OIC_LOG(ERROR, TAG, "memory error");
551 ca_mutex_unlock(context->threadMutex);
553 return CA_MEMORY_ALLOC_FAILED;
555 memcpy((*retransmissionPdu), retData->pdu, retData->size);
558 // #2. remove data from list
559 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
560 if (NULL == removedData)
562 OIC_LOG(ERROR, TAG, "Removed data is NULL");
565 ca_mutex_unlock(context->threadMutex);
567 return CA_STATUS_FAILED;
570 OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
572 CAFreeEndpoint(removedData->endpoint);
573 OICFree(removedData->pdu);
574 OICFree(removedData);
581 ca_mutex_unlock(context->threadMutex);
583 OIC_LOG(DEBUG, TAG, "OUT");
587 CAResult_t CARetransmissionStop(CARetransmission_t *context)
591 OIC_LOG(ERROR, TAG, "context is empty..");
592 return CA_STATUS_INVALID_PARAM;
595 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
598 ca_mutex_lock(context->threadMutex);
601 context->isStop = true;
604 ca_cond_signal(context->threadCond);
606 ca_cond_wait(context->threadCond, context->threadMutex);
609 ca_mutex_unlock(context->threadMutex);
614 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
618 OIC_LOG(ERROR, TAG, "context is empty..");
619 return CA_STATUS_INVALID_PARAM;
622 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
624 ca_mutex_free(context->threadMutex);
625 context->threadMutex = NULL;
626 ca_cond_free(context->threadCond);
627 u_arraylist_free(&context->dataList);
632 uint64_t getCurrentTimeInMicroSeconds()
634 OIC_LOG(DEBUG, TAG, "IN");
635 uint64_t currentTime = 0;
638 struct timespec getTs;
640 clock_gettime(CLOCK_MONOTONIC, &getTs);
642 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
643 OIC_LOG_V(DEBUG, TAG, "current time = %ld", currentTime);
644 #elif defined __ARDUINO__
645 currentTime = millis() * 1000;
646 OIC_LOG_V(DEBUG, TAG, "currtime=%lu", currentTime);
648 #if _POSIX_TIMERS > 0
650 clock_gettime(CLOCK_MONOTONIC, &ts);
651 currentTime = ts.tv_sec * USECS_PER_SEC + ts.tv_nsec / 1000;
654 gettimeofday(&tv, NULL);
655 currentTime = tv.tv_sec * USECS_PER_SEC + tv.tv_usec;
659 OIC_LOG(DEBUG, TAG, "OUT");