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"
49 #define TAG PCF("RET")
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]
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)
108 u_mutex_lock(context->threadMutex);
111 uint32_t len = u_arraylist_length(context->dataList);
113 for (i = 0; i < len; i++)
115 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
122 uint64_t currentTime = getCurrentTimeInMicroSeconds();
124 if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
126 // #2. if time's up, send the data.
127 if (context->dataSendMethod != NULL)
129 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
131 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
134 // #3. increase the retransmission count and update timestamp.
135 retData->timeStamp = currentTime;
136 retData->triedCount++;
139 // #4. if tried count is max, remove the retransmission data from list.
140 if (retData->triedCount >= context->config.tryingCount)
142 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
144 if (removedData != NULL)
146 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
147 message id(%d)", removedData->messageId);
149 // callback for retransmit timeout
150 if (context->timeoutCallback != NULL)
152 context->timeoutCallback(removedData->endpoint, removedData->pdu,
156 CADestroyRemoteEndpointInternal(removedData->endpoint);
157 OICFree(removedData->pdu);
159 OICFree(removedData);
161 // modify loop value.
162 len = u_arraylist_length(context->dataList);
168 OIC_LOG(ERROR, TAG, "arraylist remove error");
175 u_mutex_unlock(context->threadMutex);
178 static void CARetransmissionBaseRoutine(void *threadValue)
180 OIC_LOG(DEBUG, TAG, "retransmission main thread start..");
182 CARetransmission_t *context = (CARetransmission_t *) threadValue;
186 OIC_LOG(ERROR, TAG, "thread data passing error!!");
191 while (!context->isStop)
194 u_mutex_lock(context->threadMutex);
196 if (u_arraylist_length(context->dataList) <= 0)
198 // if list is empty, thread will wait
199 OIC_LOG(DEBUG, TAG, "wait..there is no retransmission data.");
202 u_cond_wait(context->threadCond, context->threadMutex);
204 OIC_LOG(DEBUG, TAG, "wake up..");
208 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
209 OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds",
210 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
213 u_cond_wait_until(context->threadCond, context->threadMutex,
214 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
218 u_mutex_unlock(context->threadMutex);
226 CACheckRetransmissionList(context);
229 u_cond_signal(context->threadCond);
231 OIC_LOG(DEBUG, TAG, "retransmission main thread end..");
235 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
236 CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
237 CARetransmissionConfig_t* config)
241 OIC_LOG(ERROR, TAG, "thread instance is empty..");
242 return CA_STATUS_FAILED;
247 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
248 return CA_STATUS_FAILED;
251 OIC_LOG(DEBUG, TAG, "thread initialize..");
253 memset(context, 0, sizeof(CARetransmission_t));
255 CARetransmissionConfig_t cfg = { 0 };
260 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
261 cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
268 // set send thread data
269 context->threadPool = handle;
270 context->threadMutex = u_mutex_new();
271 context->threadCond = u_cond_new();
272 context->dataSendMethod = retransmissionSendMethod;
273 context->timeoutCallback = timeoutCallback;
274 context->config = cfg;
275 context->isStop = false;
276 context->dataList = u_arraylist_create();
281 CAResult_t CARetransmissionStart(CARetransmission_t *context)
285 OIC_LOG(ERROR, TAG, "context is empty..");
286 return CA_STATUS_FAILED;
289 if (context->threadPool == NULL)
291 OIC_LOG(ERROR, TAG, "thread pool handle is empty..");
292 return CA_STATUS_FAILED;
295 CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
298 if (res != CA_STATUS_OK)
300 OIC_LOG(ERROR, TAG, "thread pool add task error(send thread).");
307 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
308 const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
310 if (context == NULL || endpoint == NULL || pdu == NULL)
312 OIC_LOG(ERROR, TAG, "invalid parameter..");
313 return CA_STATUS_INVALID_PARAM;
316 // #0. check support connectivity type
317 if (!(context->config.supportType & endpoint->connectivityType))
319 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
320 endpoint->connectivityType);
324 // #1. check PDU method type and get message id.
325 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
326 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
328 OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
330 if (type != CA_MSG_CONFIRM)
335 // create retransmission data
336 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
337 1, sizeof(CARetransmissionData_t));
341 OIC_LOG(ERROR, TAG, "memory error!!");
342 return CA_MEMORY_ALLOC_FAILED;
346 void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
350 OIC_LOG(ERROR, TAG, "memory error!!");
351 return CA_MEMORY_ALLOC_FAILED;
353 memcpy(pduData, pdu, sizeof(int8_t) * size);
355 // clone remote endpoint
356 CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
357 if (remoteEndpoint == NULL)
361 OIC_LOG(ERROR, TAG, "memory error!!");
362 return CA_MEMORY_ALLOC_FAILED;
365 // #2. add additional information. (time stamp, retransmission count...)
366 retData->timeStamp = getCurrentTimeInMicroSeconds();
367 retData->timeout = CAGetTimeoutValue();
368 retData->triedCount = 0;
369 retData->messageId = messageId;
370 retData->endpoint = remoteEndpoint;
371 retData->pdu = pduData;
372 retData->size = size;
375 u_mutex_lock(context->threadMutex);
378 uint32_t len = u_arraylist_length(context->dataList);
380 // #3. add data into list
381 for (i = 0; i < len; i++)
383 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
385 if (currData == NULL)
391 if (retData != NULL && (currData->endpoint->connectivityType == endpoint->connectivityType)
392 && currData->messageId == messageId)
394 OIC_LOG(ERROR, TAG, "Duplicate message ID");
397 u_mutex_unlock(context->threadMutex);
401 OICFree(remoteEndpoint);
402 return CA_STATUS_FAILED;
406 u_arraylist_add(context->dataList, (void *) retData);
409 u_cond_signal(context->threadCond);
412 u_mutex_unlock(context->threadMutex);
417 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
418 const CARemoteEndpoint_t *endpoint, const void *pdu,
421 if (context == NULL || endpoint == NULL || pdu == NULL)
423 OIC_LOG(ERROR, TAG, "invalid parameter..");
424 return CA_STATUS_INVALID_PARAM;
427 // #0. check support connectivity type
428 if (!(context->config.supportType & endpoint->connectivityType))
430 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
431 endpoint->connectivityType);
435 // #1. check PDU method type and get message id.
436 // ACK, RST --> remove the CON data
437 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
438 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
440 OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
442 if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
448 u_mutex_lock(context->threadMutex);
451 uint32_t len = u_arraylist_length(context->dataList);
454 for (i = 0; i < len; i++)
456 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
459 if (retData != NULL && (retData->endpoint->connectivityType == endpoint->connectivityType)
460 && retData->messageId == messageId)
462 // #2. remove data from list
463 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
465 if (removedData != NULL)
467 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)",
470 CADestroyRemoteEndpointInternal(removedData->endpoint);
471 OICFree(removedData->pdu);
473 OICFree(removedData);
480 u_mutex_unlock(context->threadMutex);
485 CAResult_t CARetransmissionStop(CARetransmission_t *context)
489 OIC_LOG(ERROR, TAG, "context is empty..");
490 return CA_STATUS_FAILED;
493 OIC_LOG(DEBUG, TAG, "retransmission stop request!!");
496 u_mutex_lock(context->threadMutex);
499 context->isStop = true;
502 u_cond_signal(context->threadCond);
504 u_cond_wait(context->threadCond, context->threadMutex);
507 u_mutex_unlock(context->threadMutex);
512 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
516 OIC_LOG(ERROR, TAG, "context is empty..");
517 return CA_STATUS_FAILED;
520 OIC_LOG(DEBUG, TAG, "retransmission context destroy..");
522 u_mutex_free(context->threadMutex);
523 context->threadMutex = NULL;
524 u_cond_free(context->threadCond);
525 u_arraylist_free(&context->dataList);
530 uint64_t getCurrentTimeInMicroSeconds()
532 uint64_t currentTime = 0;
535 struct timespec getTs;
537 clock_gettime(CLOCK_MONOTONIC, &getTs);
539 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
540 OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
542 currentTime = g_get_monotonic_time();