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
40 #include <linux/time.h>
43 #include "caretransmission.h"
44 #include "caremotehandler.h"
45 #include "caprotocolmessage.h"
46 #include "oic_malloc.h"
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;
63 * @brief getCurrent monotonic time
64 * @return current time in microseconds
66 uint64_t getCurrentTimeInMicroSeconds();
69 * @brief check timeout routine
70 * @param currentTime [IN]microseconds
71 * @param timeStamp [IN]microseconds
72 * @param timeoutValue [IN]microseconds
73 * @param triedCount [IN]Number of retransmission tried
74 * @return true if the timeout period has elapsed, false otherwise
76 static bool CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint64_t timeoutValue,
79 // #1. calculate timeout
80 uint32_t milliTimeoutValue = timeoutValue * 0.001;
81 uint64_t timeout = (milliTimeoutValue << triedCount) * (uint64_t) 1000;
83 if (currentTime >= timeStamp + timeout)
85 OIC_LOG_V(DEBUG, TAG, "%d microseconds time out!!, tried count(%d)", timeout, triedCount);
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.
99 static uint64_t CAGetTimeoutValue()
101 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (random() & 0xFF)) >> 8)) *
105 static void CACheckRetransmissionList(CARetransmission_t *context)
109 OIC_LOG(ERROR, TAG, "context is null..");
114 u_mutex_lock(context->threadMutex);
117 uint32_t len = u_arraylist_length(context->dataList);
119 for (i = 0; i < len; i++)
121 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
128 uint64_t currentTime = getCurrentTimeInMicroSeconds();
130 if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
132 // #2. if time's up, send the data.
133 if (context->dataSendMethod != NULL)
135 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
137 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
140 // #3. increase the retransmission count and update timestamp.
141 retData->timeStamp = currentTime;
142 retData->triedCount++;
145 // #4. if tried count is max, remove the retransmission data from list.
146 if (retData->triedCount >= context->config.tryingCount)
148 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
150 if (removedData != NULL)
152 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
153 message id(%d)", removedData->messageId);
155 // callback for retransmit timeout
156 if (context->timeoutCallback != NULL)
158 context->timeoutCallback(removedData->endpoint, removedData->pdu,
162 CADestroyRemoteEndpointInternal(removedData->endpoint);
163 OICFree(removedData->pdu);
165 OICFree(removedData);
167 // modify loop value.
168 len = u_arraylist_length(context->dataList);
174 OIC_LOG(ERROR, TAG, "arraylist remove error");
181 u_mutex_unlock(context->threadMutex);
184 static void CARetransmissionBaseRoutine(void *threadValue)
186 OIC_LOG(DEBUG, TAG, "retransmission main thread start..");
188 CARetransmission_t *context = (CARetransmission_t *) threadValue;
192 OIC_LOG(ERROR, TAG, "thread data passing error!!");
197 while (!context->isStop)
200 u_mutex_lock(context->threadMutex);
202 if (u_arraylist_length(context->dataList) <= 0)
204 // if list is empty, thread will wait
205 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
208 u_cond_wait(context->threadCond, context->threadMutex);
210 OIC_LOG(DEBUG, TAG, "wake up..");
214 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
215 OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds",
216 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
219 u_cond_wait_until(context->threadCond, context->threadMutex,
220 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
224 u_mutex_unlock(context->threadMutex);
232 CACheckRetransmissionList(context);
235 u_cond_signal(context->threadCond);
237 OIC_LOG(DEBUG, TAG, "retransmission main thread end..");
241 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
242 CADataSendMethod_t retransmissionSendMethod,
243 CATimeoutCallback_t timeoutCallback,
244 CARetransmissionConfig_t* config)
248 OIC_LOG(ERROR, TAG, "thread instance is empty..");
249 return CA_STATUS_INVALID_PARAM;
254 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
255 return CA_STATUS_INVALID_PARAM;
258 OIC_LOG(DEBUG, TAG, "thread initialize..");
260 memset(context, 0, sizeof(CARetransmission_t));
262 CARetransmissionConfig_t cfg = { 0 };
267 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
268 cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
275 // set send thread data
276 context->threadPool = handle;
277 context->threadMutex = u_mutex_new();
278 context->threadCond = u_cond_new();
279 context->dataSendMethod = retransmissionSendMethod;
280 context->timeoutCallback = timeoutCallback;
281 context->config = cfg;
282 context->isStop = false;
283 context->dataList = u_arraylist_create();
288 CAResult_t CARetransmissionStart(CARetransmission_t *context)
292 OIC_LOG(ERROR, TAG, "context is empty..");
293 return CA_STATUS_INVALID_PARAM;
296 if (NULL == context->threadPool)
298 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
299 return CA_STATUS_INVALID_PARAM;
302 CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
305 if (res != CA_STATUS_OK)
307 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
314 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
315 const CARemoteEndpoint_t* endpoint, const void* pdu,
318 if (NULL == context || NULL == endpoint || NULL == pdu)
320 OIC_LOG(ERROR, TAG, "invalid parameter..");
321 return CA_STATUS_INVALID_PARAM;
324 // #0. check support connectivity type
325 if (!(context->config.supportType & endpoint->connectivityType))
327 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
328 endpoint->connectivityType);
329 return CA_NOT_SUPPORTED;
332 // #1. check PDU method type and get message id.
333 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
334 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
336 OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
338 if (type != CA_MSG_CONFIRM)
340 OIC_LOG(DEBUG, TAG, "not supported message type for retransmission..");
341 return CA_NOT_SUPPORTED;
344 // create retransmission data
345 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
346 1, sizeof(CARetransmissionData_t));
350 OIC_LOG(ERROR, TAG, "memory error!!");
351 return CA_MEMORY_ALLOC_FAILED;
355 void *pduData = (void *) OICMalloc(size);
359 OIC_LOG_V(ERROR, TAG, "memory error!!");
360 return CA_MEMORY_ALLOC_FAILED;
362 memcpy(pduData, pdu, size);
364 // clone remote endpoint
365 CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
366 if (NULL == remoteEndpoint)
370 OIC_LOG(ERROR, TAG, "memory error!!");
371 return CA_MEMORY_ALLOC_FAILED;
374 // #2. add additional information. (time stamp, retransmission count...)
375 retData->timeStamp = getCurrentTimeInMicroSeconds();
376 retData->timeout = CAGetTimeoutValue();
377 retData->triedCount = 0;
378 retData->messageId = messageId;
379 retData->endpoint = remoteEndpoint;
380 retData->pdu = pduData;
381 retData->size = size;
384 u_mutex_lock(context->threadMutex);
387 uint32_t len = u_arraylist_length(context->dataList);
389 // #3. add data into list
390 for (i = 0; i < len; i++)
392 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
394 if (NULL == currData)
400 if (NULL != currData->endpoint && currData->messageId == messageId
401 && (currData->endpoint->connectivityType == endpoint->connectivityType))
403 OIC_LOG(ERROR, TAG, "Duplicate message ID");
406 u_mutex_unlock(context->threadMutex);
410 OICFree(remoteEndpoint);
411 return CA_STATUS_FAILED;
415 u_arraylist_add(context->dataList, (void *) retData);
418 u_cond_signal(context->threadCond);
421 u_mutex_unlock(context->threadMutex);
426 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
427 const CARemoteEndpoint_t *endpoint, const void *pdu,
428 uint32_t size, void **retransmissionPdu)
430 OIC_LOG_V(DEBUG, TAG, "IN - CARetransmissionReceivedData");
431 if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
433 OIC_LOG(ERROR, TAG, "invalid parameter..");
434 return CA_STATUS_INVALID_PARAM;
437 // #0. check support connectivity type
438 if (!(context->config.supportType & endpoint->connectivityType))
440 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
441 endpoint->connectivityType);
445 // #1. check PDU method type and get message id.
446 // ACK, RST --> remove the CON data
447 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
448 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
450 OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
452 if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
458 u_mutex_lock(context->threadMutex);
461 uint32_t len = u_arraylist_length(context->dataList);
464 for (i = 0; i < len; i++)
466 CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
467 context->dataList, i);
475 if (NULL != retData->endpoint && retData->messageId == messageId
476 && (retData->endpoint->connectivityType == endpoint->connectivityType))
478 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
479 // if retransmission was finish..token will be unavailable.
480 if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
482 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY..");
484 if (NULL == retData->pdu)
486 OIC_LOG(ERROR, TAG, "retData->pdu is null");
489 u_mutex_unlock(context->threadMutex);
491 return CA_STATUS_FAILED;
495 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
496 if ((*retransmissionPdu) == NULL)
499 OIC_LOG(ERROR, TAG, "memory error!!");
502 u_mutex_unlock(context->threadMutex);
504 return CA_MEMORY_ALLOC_FAILED;
506 memcpy((*retransmissionPdu), retData->pdu, retData->size);
509 // #2. remove data from list
510 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
511 if (NULL == removedData)
513 OIC_LOG(ERROR, TAG, "Removed data is NULL");
516 u_mutex_unlock(context->threadMutex);
518 return CA_STATUS_FAILED;
521 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)",
524 CADestroyRemoteEndpointInternal(removedData->endpoint);
525 OICFree(removedData->pdu);
526 OICFree(removedData);
533 u_mutex_unlock(context->threadMutex);
535 OIC_LOG(DEBUG, TAG, "OUT - CARetransmissionReceivedData");
539 CAResult_t CARetransmissionStop(CARetransmission_t *context)
543 OIC_LOG(ERROR, TAG, "context is empty..");
544 return CA_STATUS_INVALID_PARAM;
547 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
550 u_mutex_lock(context->threadMutex);
553 context->isStop = true;
556 u_cond_signal(context->threadCond);
558 u_cond_wait(context->threadCond, context->threadMutex);
561 u_mutex_unlock(context->threadMutex);
566 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
570 OIC_LOG(ERROR, TAG, "context is empty..");
571 return CA_STATUS_INVALID_PARAM;
574 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
576 u_mutex_free(context->threadMutex);
577 context->threadMutex = NULL;
578 u_cond_free(context->threadCond);
579 u_arraylist_free(&context->dataList);
584 uint64_t getCurrentTimeInMicroSeconds()
586 uint64_t currentTime = 0;
589 struct timespec getTs;
591 clock_gettime(CLOCK_MONOTONIC, &getTs);
593 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
594 OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
596 currentTime = g_get_monotonic_time();