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
52 #if defined(__ANDROID__)
53 #include <linux/time.h>
56 #include "caretransmission.h"
57 #include "caremotehandler.h"
58 #include "caprotocolmessage.h"
59 #include "oic_malloc.h"
66 uint64_t timeStamp; /**< last sent time. microseconds */
67 uint64_t timeout; /**< timeout value. microseconds */
68 uint8_t triedCount; /**< retransmission count */
69 uint16_t messageId; /**< coap PDU message id */
70 CARemoteEndpoint_t *endpoint; /**< remote endpoint */
71 void *pdu; /**< coap PDU */
72 uint32_t size; /**< coap PDU size */
73 } CARetransmissionData_t;
75 static const uint64_t USECS_PER_SEC = 1000000;
78 * @brief getCurrent monotonic time
79 * @return current time in microseconds
81 uint64_t getCurrentTimeInMicroSeconds();
84 * @brief check timeout routine
85 * @param currentTime [IN]microseconds
86 * @param timeStamp [IN]microseconds
87 * @param timeoutValue [IN]microseconds
88 * @param triedCount [IN]Number of retransmission tried
89 * @return true if the timeout period has elapsed, false otherwise
91 static bool CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint64_t timeoutValue,
94 // #1. calculate timeout
95 uint32_t milliTimeoutValue = timeoutValue * 0.001;
96 uint64_t timeout = (milliTimeoutValue << triedCount) * (uint64_t) 1000;
98 if (currentTime >= timeStamp + timeout)
100 OIC_LOG_V(DEBUG, TAG, "%d microseconds time out!!, tried count(%d)", timeout, triedCount);
108 * @brief timeout value is
109 * between DEFAULT_ACK_TIMEOUT_SEC and
110 * (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
111 * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
112 * @return microseconds.
114 static uint64_t CAGetTimeoutValue()
116 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (random() & 0xFF)) >> 8)) *
120 static void CACheckRetransmissionList(CARetransmission_t *context)
124 OIC_LOG(ERROR, TAG, "context is null..");
129 ca_mutex_lock(context->threadMutex);
132 uint32_t len = u_arraylist_length(context->dataList);
134 for (i = 0; i < len; i++)
136 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
143 uint64_t currentTime = getCurrentTimeInMicroSeconds();
145 if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
147 // #2. if time's up, send the data.
148 if (NULL != context->dataSendMethod)
150 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
152 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
155 // #3. increase the retransmission count and update timestamp.
156 retData->timeStamp = currentTime;
157 retData->triedCount++;
160 // #4. if tried count is max, remove the retransmission data from list.
161 if (retData->triedCount >= context->config.tryingCount)
163 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
165 if (NULL != removedData)
167 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
168 message id(%d)", removedData->messageId);
170 // callback for retransmit timeout
171 if (NULL != context->timeoutCallback)
173 context->timeoutCallback(removedData->endpoint, removedData->pdu,
177 CADestroyRemoteEndpointInternal(removedData->endpoint);
178 OICFree(removedData->pdu);
180 OICFree(removedData);
182 // modify loop value.
183 len = u_arraylist_length(context->dataList);
189 OIC_LOG(ERROR, TAG, "arraylist remove error");
196 ca_mutex_unlock(context->threadMutex);
199 static void CARetransmissionBaseRoutine(void *threadValue)
201 OIC_LOG(DEBUG, TAG, "retransmission main thread start..");
203 CARetransmission_t *context = (CARetransmission_t *) threadValue;
207 OIC_LOG(ERROR, TAG, "thread data passing error!!");
212 while (!context->isStop)
215 ca_mutex_lock(context->threadMutex);
217 if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
219 // if list is empty, thread will wait
220 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
223 ca_cond_wait(context->threadCond, context->threadMutex);
225 OIC_LOG(DEBUG, TAG, "wake up..");
227 else if (!context->isStop)
229 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
230 OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds",
231 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
234 uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
235 ca_cond_wait_for(context->threadCond, context->threadMutex, absTime );
239 // we are stopping, so we want to unlock and finish stopping
243 ca_mutex_unlock(context->threadMutex);
251 CACheckRetransmissionList(context);
254 ca_mutex_lock(context->threadMutex);
255 ca_cond_signal(context->threadCond);
256 ca_mutex_unlock(context->threadMutex);
258 OIC_LOG(DEBUG, TAG, "retransmission main thread end..");
262 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, ca_thread_pool_t handle,
263 CADataSendMethod_t retransmissionSendMethod,
264 CATimeoutCallback_t timeoutCallback,
265 CARetransmissionConfig_t* config)
269 OIC_LOG(ERROR, TAG, "thread instance is empty..");
270 return CA_STATUS_INVALID_PARAM;
275 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
276 return CA_STATUS_INVALID_PARAM;
279 OIC_LOG(DEBUG, TAG, "thread initialize..");
281 memset(context, 0, sizeof(CARetransmission_t));
283 CARetransmissionConfig_t cfg = { 0 };
288 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
289 cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
296 // set send thread data
297 context->threadPool = handle;
298 context->threadMutex = ca_mutex_new();
299 context->threadCond = ca_cond_new();
300 context->dataSendMethod = retransmissionSendMethod;
301 context->timeoutCallback = timeoutCallback;
302 context->config = cfg;
303 context->isStop = false;
304 context->dataList = u_arraylist_create();
309 CAResult_t CARetransmissionStart(CARetransmission_t *context)
313 OIC_LOG(ERROR, TAG, "context is empty..");
314 return CA_STATUS_INVALID_PARAM;
317 if (NULL == context->threadPool)
319 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
320 return CA_STATUS_INVALID_PARAM;
323 CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
326 if (CA_STATUS_OK != res)
328 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
335 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
336 const CARemoteEndpoint_t* endpoint, const void* pdu,
339 if (NULL == context || NULL == endpoint || NULL == pdu)
341 OIC_LOG(ERROR, TAG, "invalid parameter..");
342 return CA_STATUS_INVALID_PARAM;
345 // #0. check support connectivity type
346 if (!(context->config.supportType & endpoint->connectivityType))
348 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
349 endpoint->connectivityType);
350 return CA_NOT_SUPPORTED;
353 // #1. check PDU method type and get message id.
354 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
355 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
357 OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
359 if (CA_MSG_CONFIRM != type)
361 OIC_LOG(DEBUG, TAG, "not supported message type for retransmission..");
362 return CA_NOT_SUPPORTED;
365 // create retransmission data
366 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
367 1, sizeof(CARetransmissionData_t));
371 OIC_LOG(ERROR, TAG, "memory error!!");
372 return CA_MEMORY_ALLOC_FAILED;
376 void *pduData = (void *) OICMalloc(size);
380 OIC_LOG(ERROR, TAG, "memory error!!");
381 return CA_MEMORY_ALLOC_FAILED;
383 memcpy(pduData, pdu, size);
385 // clone remote endpoint
386 CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
387 if (NULL == remoteEndpoint)
391 OIC_LOG(ERROR, TAG, "memory error!!");
392 return CA_MEMORY_ALLOC_FAILED;
395 // #2. add additional information. (time stamp, retransmission count...)
396 retData->timeStamp = getCurrentTimeInMicroSeconds();
397 retData->timeout = CAGetTimeoutValue();
398 retData->triedCount = 0;
399 retData->messageId = messageId;
400 retData->endpoint = remoteEndpoint;
401 retData->pdu = pduData;
402 retData->size = size;
405 ca_mutex_lock(context->threadMutex);
408 uint32_t len = u_arraylist_length(context->dataList);
410 // #3. add data into list
411 for (i = 0; i < len; i++)
413 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
415 if (NULL == currData)
421 if (NULL != currData->endpoint && currData->messageId == messageId
422 && (currData->endpoint->connectivityType == endpoint->connectivityType))
424 OIC_LOG(ERROR, TAG, "Duplicate message ID");
427 ca_mutex_unlock(context->threadMutex);
431 OICFree(remoteEndpoint);
432 return CA_STATUS_FAILED;
436 u_arraylist_add(context->dataList, (void *) retData);
439 ca_cond_signal(context->threadCond);
442 ca_mutex_unlock(context->threadMutex);
447 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
448 const CARemoteEndpoint_t *endpoint, const void *pdu,
449 uint32_t size, void **retransmissionPdu)
451 OIC_LOG(DEBUG, TAG, "IN - CARetransmissionReceivedData");
452 if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
454 OIC_LOG(ERROR, TAG, "invalid parameter..");
455 return CA_STATUS_INVALID_PARAM;
458 // #0. check support connectivity type
459 if (!(context->config.supportType & endpoint->connectivityType))
461 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
462 endpoint->connectivityType);
466 // #1. check PDU method type and get message id.
467 // ACK, RST --> remove the CON data
468 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
469 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
471 OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
473 if ((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
479 ca_mutex_lock(context->threadMutex);
480 uint32_t len = u_arraylist_length(context->dataList);
483 for (uint32_t i = 0; i < len; i++)
485 CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
486 context->dataList, i);
494 if (NULL != retData->endpoint && retData->messageId == messageId
495 && (retData->endpoint->connectivityType == endpoint->connectivityType))
497 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
498 // if retransmission was finish..token will be unavailable.
499 if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
501 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY..");
503 if (NULL == retData->pdu)
505 OIC_LOG(ERROR, TAG, "retData->pdu is null");
508 ca_mutex_unlock(context->threadMutex);
510 return CA_STATUS_FAILED;
514 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
515 if ((*retransmissionPdu) == NULL)
518 OIC_LOG(ERROR, TAG, "memory error!!");
521 ca_mutex_unlock(context->threadMutex);
523 return CA_MEMORY_ALLOC_FAILED;
525 memcpy((*retransmissionPdu), retData->pdu, retData->size);
528 // #2. remove data from list
529 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
530 if (NULL == removedData)
532 OIC_LOG(ERROR, TAG, "Removed data is NULL");
535 ca_mutex_unlock(context->threadMutex);
537 return CA_STATUS_FAILED;
540 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)",
543 CADestroyRemoteEndpointInternal(removedData->endpoint);
544 OICFree(removedData->pdu);
545 OICFree(removedData);
552 ca_mutex_unlock(context->threadMutex);
554 OIC_LOG(DEBUG, TAG, "OUT - CARetransmissionReceivedData");
558 CAResult_t CARetransmissionStop(CARetransmission_t *context)
562 OIC_LOG(ERROR, TAG, "context is empty..");
563 return CA_STATUS_INVALID_PARAM;
566 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
569 ca_mutex_lock(context->threadMutex);
572 context->isStop = true;
575 ca_cond_signal(context->threadCond);
577 ca_cond_wait(context->threadCond, context->threadMutex);
580 ca_mutex_unlock(context->threadMutex);
585 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
589 OIC_LOG(ERROR, TAG, "context is empty..");
590 return CA_STATUS_INVALID_PARAM;
593 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
595 ca_mutex_free(context->threadMutex);
596 context->threadMutex = NULL;
597 ca_cond_free(context->threadCond);
598 u_arraylist_free(&context->dataList);
603 uint64_t getCurrentTimeInMicroSeconds()
605 OIC_LOG(DEBUG, TAG, "IN");
606 uint64_t currentTime = 0;
609 struct timespec getTs;
611 clock_gettime(CLOCK_MONOTONIC, &getTs);
613 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
614 OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
616 #if _POSIX_TIMERS > 0
618 clock_gettime(CLOCK_MONOTONIC, &ts);
619 currentTime = ts.tv_sec * USECS_PER_SEC + ts.tv_nsec / 1000;
622 gettimeofday(&tv, NULL);
623 currentTime = tv.tv_sec * USECS_PER_SEC + tv.tv_usec;
627 OIC_LOG(DEBUG, TAG, "OUT");