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
55 #ifdef HAVE_SYS_TIME_H
59 #include <sys/timeb.h>
66 #if defined(__ANDROID__)
67 #include <linux/time.h>
70 #include "caretransmission.h"
71 #include "caremotehandler.h"
72 #include "caprotocolmessage.h"
73 #include "oic_malloc.h"
78 #define TAG "OIC_CA_RETRANS"
82 uint64_t timeStamp; /**< last sent time. microseconds */
84 uint64_t timeout; /**< timeout value. microseconds */
86 uint8_t triedCount; /**< retransmission count */
87 uint16_t messageId; /**< coap PDU message id */
88 CADataType_t dataType; /**< data Type (Request/Response) */
89 CAEndpoint_t *endpoint; /**< remote endpoint */
90 void *pdu; /**< coap PDU */
91 uint32_t size; /**< coap PDU size */
92 } CARetransmissionData_t;
94 static const uint64_t USECS_PER_SEC = 1000000;
95 static const uint64_t MSECS_PER_SEC = 1000;
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()
107 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * OCGetRandomByte()) >> 8)) *
111 CAResult_t CARetransmissionStart(CARetransmission_t *context)
115 OIC_LOG(ERROR, TAG, "context is empty");
116 return CA_STATUS_INVALID_PARAM;
119 if (NULL == context->threadPool)
121 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
122 return CA_STATUS_INVALID_PARAM;
125 CAResult_t res = ca_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
128 if (CA_STATUS_OK != res)
130 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
139 * @brief check timeout routine
140 * @param currentTime [IN]microseconds
141 * @param retData [IN]retransmission data
142 * @return true if the timeout period has elapsed, false otherwise
144 static bool CACheckTimeout(uint64_t currentTime, CARetransmissionData_t *retData)
146 #ifndef SINGLE_THREAD
147 // #1. calculate timeout
148 uint32_t milliTimeoutValue = retData->timeout * 0.001;
149 uint64_t timeout = (milliTimeoutValue << retData->triedCount) * (uint64_t) 1000;
151 if (currentTime >= retData->timeStamp + timeout)
153 OIC_LOG_V(DEBUG, TAG, "%" PRIu64 " microseconds time out!!, tried count(%d)",
154 timeout, retData->triedCount);
158 // #1. calculate timeout
159 uint64_t timeOut = (2 << retData->triedCount) * 1000000;
161 if (currentTime >= retData->timeStamp + timeOut)
163 OIC_LOG_V(DEBUG, TAG, "timeout=%d, tried cnt=%d",
164 (2 << retData->triedCount), retData->triedCount);
171 static void CACheckRetransmissionList(CARetransmission_t *context)
175 OIC_LOG(ERROR, TAG, "context is null");
180 ca_mutex_lock(context->threadMutex);
183 uint32_t len = u_arraylist_length(context->dataList);
185 for (i = 0; i < len; i++)
187 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
194 uint64_t currentTime = OICGetCurrentTime(TIME_IN_US);
196 if (CACheckTimeout(currentTime, retData))
198 // #2. if time's up, send the data.
199 if (NULL != context->dataSendMethod)
201 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, msgid=%d",
203 context->dataSendMethod(retData->endpoint, retData->pdu,
204 retData->size, retData->dataType);
207 // #3. increase the retransmission count and update timestamp.
208 retData->timeStamp = currentTime;
209 retData->triedCount++;
212 // #4. if tried count is max, remove the retransmission data from list.
213 if (retData->triedCount >= context->config.tryingCount)
215 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
216 if (NULL == removedData)
218 OIC_LOG(ERROR, TAG, "Removed data is NULL");
220 ca_mutex_unlock(context->threadMutex);
223 OIC_LOG_V(DEBUG, TAG, "max trying count, remove RTCON data,"
224 "msgid=%d", removedData->messageId);
226 // callback for retransmit timeout
227 if (NULL != context->timeoutCallback)
229 context->timeoutCallback(removedData->endpoint, removedData->pdu,
233 CAFreeEndpoint(removedData->endpoint);
234 OICFree(removedData->pdu);
236 OICFree(removedData);
238 // modify loop value.
239 len = u_arraylist_length(context->dataList);
245 ca_mutex_unlock(context->threadMutex);
248 void CARetransmissionBaseRoutine(void *threadValue)
250 OIC_LOG(DEBUG, TAG, "retransmission main thread start");
252 CARetransmission_t *context = (CARetransmission_t *) threadValue;
256 OIC_LOG(ERROR, TAG, "thread data passing error");
262 if (true == context->isStop)
264 OIC_LOG(DEBUG, TAG, "thread stopped");
267 CACheckRetransmissionList(context);
270 while (!context->isStop)
273 ca_mutex_lock(context->threadMutex);
275 if (!context->isStop && u_arraylist_length(context->dataList) <= 0)
277 // if list is empty, thread will wait
278 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
281 ca_cond_wait(context->threadCond, context->threadMutex);
283 OIC_LOG(DEBUG, TAG, "wake up..");
285 else if (!context->isStop)
287 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
288 OIC_LOG_V(DEBUG, TAG, "wait..(%" PRIu64 ")microseconds",
289 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC);
292 uint64_t absTime = RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) USECS_PER_SEC;
293 ca_cond_wait_for(context->threadCond, context->threadMutex, absTime );
297 // we are stopping, so we want to unlock and finish stopping
301 ca_mutex_unlock(context->threadMutex);
309 CACheckRetransmissionList(context);
312 ca_mutex_lock(context->threadMutex);
313 ca_cond_signal(context->threadCond);
314 ca_mutex_unlock(context->threadMutex);
317 OIC_LOG(DEBUG, TAG, "retransmission main thread end");
321 CAResult_t CARetransmissionInitialize(CARetransmission_t *context,
322 ca_thread_pool_t handle,
323 CADataSendMethod_t retransmissionSendMethod,
324 CATimeoutCallback_t timeoutCallback,
325 CARetransmissionConfig_t* config)
329 OIC_LOG(ERROR, TAG, "thread instance is empty");
330 return CA_STATUS_INVALID_PARAM;
332 #ifndef SINGLE_THREAD
335 OIC_LOG(ERROR, TAG, "thread pool handle is empty");
336 return CA_STATUS_INVALID_PARAM;
339 OIC_LOG(DEBUG, TAG, "thread initialize");
341 memset(context, 0, sizeof(CARetransmission_t));
343 CARetransmissionConfig_t cfg = { .supportType = DEFAULT_RETRANSMISSION_TYPE,
344 .tryingCount = DEFAULT_RETRANSMISSION_COUNT };
351 // set send thread data
352 context->threadPool = handle;
353 context->threadMutex = ca_mutex_new();
354 context->threadCond = ca_cond_new();
355 context->dataSendMethod = retransmissionSendMethod;
356 context->timeoutCallback = timeoutCallback;
357 context->config = cfg;
358 context->isStop = false;
359 context->dataList = u_arraylist_create();
364 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
365 const CAEndpoint_t *endpoint,
366 CADataType_t dataType,
367 const void *pdu, uint32_t size)
369 if (NULL == context || NULL == endpoint || NULL == pdu)
371 OIC_LOG(ERROR, TAG, "invalid parameter");
372 return CA_STATUS_INVALID_PARAM;
375 // #0. check support transport type
376 if (!(context->config.supportType & endpoint->adapter))
378 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
379 return CA_NOT_SUPPORTED;
382 // #1. check PDU method type and get message id.
383 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
384 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
386 OIC_LOG_V(DEBUG, TAG, "sent pdu, msgtype=%d, msgid=%d", type, messageId);
388 if (CA_MSG_CONFIRM != type)
390 OIC_LOG(DEBUG, TAG, "not supported message type");
391 return CA_NOT_SUPPORTED;
394 // create retransmission data
395 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
396 1, sizeof(CARetransmissionData_t));
400 OIC_LOG(ERROR, TAG, "memory error");
401 return CA_MEMORY_ALLOC_FAILED;
405 void *pduData = (void *) OICMalloc(size);
409 OIC_LOG(ERROR, TAG, "memory error");
410 return CA_MEMORY_ALLOC_FAILED;
412 memcpy(pduData, pdu, size);
414 // clone remote endpoint
415 CAEndpoint_t *remoteEndpoint = CACloneEndpoint(endpoint);
416 if (NULL == remoteEndpoint)
420 OIC_LOG(ERROR, TAG, "memory error");
421 return CA_MEMORY_ALLOC_FAILED;
424 // #2. add additional information. (time stamp, retransmission count...)
425 retData->timeStamp = OICGetCurrentTime(TIME_IN_US);
426 #ifndef SINGLE_THREAD
427 retData->timeout = CAGetTimeoutValue();
429 retData->triedCount = 0;
430 retData->messageId = messageId;
431 retData->endpoint = remoteEndpoint;
432 retData->pdu = pduData;
433 retData->size = size;
434 retData->dataType = dataType;
435 #ifndef SINGLE_THREAD
437 ca_mutex_lock(context->threadMutex);
440 uint32_t len = u_arraylist_length(context->dataList);
442 // #3. add data into list
443 for (i = 0; i < len; i++)
445 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
447 if (NULL == currData)
453 if (NULL != currData->endpoint && currData->messageId == messageId
454 && (currData->endpoint->adapter == endpoint->adapter))
456 OIC_LOG(ERROR, TAG, "Duplicate message ID");
459 ca_mutex_unlock(context->threadMutex);
463 OICFree(remoteEndpoint);
464 return CA_STATUS_FAILED;
468 u_arraylist_add(context->dataList, (void *) retData);
471 ca_cond_signal(context->threadCond);
474 ca_mutex_unlock(context->threadMutex);
477 u_arraylist_add(context->dataList, (void *) retData);
479 CACheckRetransmissionList(context);
484 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
485 const CAEndpoint_t *endpoint, const void *pdu,
486 uint32_t size, void **retransmissionPdu)
488 OIC_LOG(DEBUG, TAG, "IN");
489 if (NULL == context || NULL == endpoint || NULL == pdu || NULL == retransmissionPdu)
491 OIC_LOG(ERROR, TAG, "invalid parameter");
492 return CA_STATUS_INVALID_PARAM;
495 // #0. check support transport type
496 if (!(context->config.supportType & endpoint->adapter))
498 OIC_LOG_V(DEBUG, TAG, "not supported transport type=%d", endpoint->adapter);
502 // #1. check PDU method type and get message id.
503 // ACK, RST --> remove the CON data
504 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
505 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
506 CAResponseResult_t code = CAGetCodeFromPduBinaryData(pdu, size);
508 OIC_LOG_V(DEBUG, TAG, "received pdu, msgtype=%d, msgid=%d, code=%d",
509 type, messageId, code);
511 if (((CA_MSG_ACKNOWLEDGE != type) && (CA_MSG_RESET != type))
512 || (CA_MSG_RESET == type && CA_EMPTY != code))
518 ca_mutex_lock(context->threadMutex);
519 uint32_t len = u_arraylist_length(context->dataList);
523 for (i = 0; i < len; i++)
525 CARetransmissionData_t *retData = (CARetransmissionData_t *) u_arraylist_get(
526 context->dataList, i);
534 if (NULL != retData->endpoint && retData->messageId == messageId
535 && (retData->endpoint->adapter == endpoint->adapter))
537 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
538 // if retransmission was finish..token will be unavailable.
539 if (CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
541 OIC_LOG(DEBUG, TAG, "code is CA_EMPTY");
543 if (NULL == retData->pdu)
545 OIC_LOG(ERROR, TAG, "retData->pdu is null");
548 ca_mutex_unlock(context->threadMutex);
550 return CA_STATUS_FAILED;
554 (*retransmissionPdu) = (void *) OICCalloc(1, retData->size);
555 if ((*retransmissionPdu) == NULL)
558 OIC_LOG(ERROR, TAG, "memory error");
561 ca_mutex_unlock(context->threadMutex);
563 return CA_MEMORY_ALLOC_FAILED;
565 memcpy((*retransmissionPdu), retData->pdu, retData->size);
568 // #2. remove data from list
569 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
570 if (NULL == removedData)
572 OIC_LOG(ERROR, TAG, "Removed data is NULL");
575 ca_mutex_unlock(context->threadMutex);
577 return CA_STATUS_FAILED;
580 OIC_LOG_V(DEBUG, TAG, "remove RTCON data!!, msgid=%d", messageId);
582 CAFreeEndpoint(removedData->endpoint);
583 OICFree(removedData->pdu);
584 OICFree(removedData);
591 ca_mutex_unlock(context->threadMutex);
593 OIC_LOG(DEBUG, TAG, "OUT");
597 CAResult_t CARetransmissionStop(CARetransmission_t *context)
601 OIC_LOG(ERROR, TAG, "context is empty..");
602 return CA_STATUS_INVALID_PARAM;
605 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
608 ca_mutex_lock(context->threadMutex);
611 context->isStop = true;
614 ca_cond_signal(context->threadCond);
616 ca_cond_wait(context->threadCond, context->threadMutex);
619 ca_mutex_unlock(context->threadMutex);
624 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
628 OIC_LOG(ERROR, TAG, "context is empty..");
629 return CA_STATUS_INVALID_PARAM;
632 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
634 ca_mutex_free(context->threadMutex);
635 context->threadMutex = NULL;
636 ca_cond_free(context->threadCond);
637 u_arraylist_free(&context->dataList);