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 ******************************************************************/
28 #include <linux/time.h>
31 #include "caretransmission.h"
32 #include "caremotehandler.h"
33 #include "caprotocolmessage.h"
34 #include "oic_malloc.h"
37 #define TAG PCF("RET")
41 uint64_t timeStamp; /**< last sent time. microseconds */
42 uint64_t timeout; /**< timeout value. microseconds */
43 uint8_t triedCount; /**< retransmission count */
44 uint16_t messageId; /**< coap PDU message id */
45 CARemoteEndpoint_t *endpoint; /**< remote endpoint */
46 void *pdu; /**< coap PDU */
47 uint32_t size; /**< coap PDU size */
48 } CARetransmissionData_t;
51 * @brief getCurrent monotonic time
52 * @return current time in microseconds
54 uint64_t getCurrentTimeInMicroSeconds();
57 * @brief check timeout routine
58 * @param currentTime [IN]microseconds
59 * @param timeStamp [IN]microseconds
60 * @param timeoutValue [IN]microseconds
61 * @param triedCount [IN]
62 * @return true if the timeout period has elapsed, false otherwise
64 static bool CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint64_t timeoutValue,
67 // #1. calculate timeout
68 uint32_t milliTimeoutValue = timeoutValue * 0.001;
69 uint64_t timeout = (milliTimeoutValue << triedCount) * (uint64_t) 1000;
71 if (currentTime >= timeStamp + timeout)
73 OIC_LOG_V(DEBUG, TAG, "%d microseconds time out!!, tried count(%d)", timeout, triedCount);
81 * @brief timeout value is
82 * between DEFAULT_ACK_TIMEOUT_SEC and
83 * (DEFAULT_ACK_TIMEOUT_SEC * DEFAULT_RANDOM_FACTOR) second.
84 * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
85 * @return microseconds.
87 static uint64_t CAGetTimeoutValue()
89 return ((DEFAULT_ACK_TIMEOUT_SEC * 1000) + ((1000 * (random() & 0xFF)) >> 8)) *
93 static void CACheckRetransmissionList(CARetransmission_t *context)
96 u_mutex_lock(context->threadMutex);
99 uint32_t len = u_arraylist_length(context->dataList);
101 for (i = 0; i < len; i++)
103 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
110 uint64_t currentTime = getCurrentTimeInMicroSeconds();
112 if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
114 // #2. if time's up, send the data.
115 if (context->dataSendMethod != NULL)
117 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
119 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
122 // #3. increase the retransmission count and update timestamp.
123 retData->timeStamp = currentTime;
124 retData->triedCount++;
127 // #4. if tried count is max, remove the retransmission data from list.
128 if (retData->triedCount >= context->config.tryingCount)
130 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
132 if (removedData != NULL)
134 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
135 message id(%d)", removedData->messageId);
137 // callback for retransmit timeout
138 if (context->timeoutCallback != NULL)
140 context->timeoutCallback(removedData->endpoint, removedData->pdu,
144 CADestroyRemoteEndpointInternal(removedData->endpoint);
145 OICFree(removedData->pdu);
147 OICFree(removedData);
149 // modify loop value.
150 len = u_arraylist_length(context->dataList);
156 OIC_LOG_V(ERROR, TAG, "arraylist remove error");
163 u_mutex_unlock(context->threadMutex);
166 static void CARetransmissionBaseRoutine(void *threadValue)
168 OIC_LOG_V(DEBUG, TAG, "retransmission main thread start..");
170 CARetransmission_t *context = (CARetransmission_t *) threadValue;
174 OIC_LOG_V(ERROR, TAG, "thread data passing error!!");
179 while (!context->isStop)
182 u_mutex_lock(context->threadMutex);
184 if (u_arraylist_length(context->dataList) <= 0)
186 // if list is empty, thread will wait
187 OIC_LOG_V(DEBUG, TAG, "wait..there is no retransmission data.");
190 u_cond_wait(context->threadCond, context->threadMutex);
192 OIC_LOG_V(DEBUG, TAG, "wake up..");
196 // check each RETRANSMISSION_CHECK_PERIOD_SEC time.
197 OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds",
198 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
201 u_cond_wait_until(context->threadCond, context->threadMutex,
202 RETRANSMISSION_CHECK_PERIOD_SEC * (uint64_t) 1000000);
206 u_mutex_unlock(context->threadMutex);
214 CACheckRetransmissionList(context);
217 u_cond_signal(context->threadCond);
219 OIC_LOG_V(DEBUG, TAG, "retransmission main thread end..");
223 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
224 CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
225 CARetransmissionConfig_t* config)
229 OIC_LOG_V(DEBUG, TAG, "thread instance is empty..");
230 return CA_STATUS_FAILED;
235 OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
236 return CA_STATUS_FAILED;
239 OIC_LOG_V(DEBUG, TAG, "thread initialize..");
241 memset(context, 0, sizeof(CARetransmission_t));
243 CARetransmissionConfig_t cfg = { 0 };
248 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
249 cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
256 // set send thread data
257 context->threadPool = handle;
258 context->threadMutex = u_mutex_new();
259 context->threadCond = u_cond_new();
260 context->dataSendMethod = retransmissionSendMethod;
261 context->timeoutCallback = timeoutCallback;
262 context->config = cfg;
263 context->isStop = false;
264 context->dataList = u_arraylist_create();
269 CAResult_t CARetransmissionStart(CARetransmission_t *context)
273 OIC_LOG_V(DEBUG, TAG, "context is empty..");
274 return CA_STATUS_FAILED;
277 if (context->threadPool == NULL)
279 OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
280 return CA_STATUS_FAILED;
283 CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
286 if (res != CA_STATUS_OK)
288 OIC_LOG_V(ERROR, TAG, "thread pool add task error(send thread).");
295 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
296 const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
298 if (context == NULL || endpoint == NULL || pdu == NULL)
300 OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
301 return CA_STATUS_INVALID_PARAM;
304 // #0. check support connectivity type
305 if (!(context->config.supportType & endpoint->connectivityType))
307 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
308 endpoint->connectivityType);
312 // #1. check PDU method type and get message id.
313 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
314 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
316 OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
318 if (type != CA_MSG_CONFIRM)
323 // create retransmission data
324 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICCalloc(
325 1, sizeof(CARetransmissionData_t));
329 OIC_LOG_V(ERROR, TAG, "memory error!!");
330 return CA_MEMORY_ALLOC_FAILED;
334 void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
338 OIC_LOG_V(ERROR, TAG, "memory error!!");
339 return CA_MEMORY_ALLOC_FAILED;
341 memcpy(pduData, pdu, sizeof(int8_t) * size);
343 // clone remote endpoint
344 CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
345 if (remoteEndpoint == NULL)
349 OIC_LOG_V(ERROR, TAG, "memory error!!");
350 return CA_MEMORY_ALLOC_FAILED;
353 // #2. add additional information. (time stamp, retransmission count...)
354 retData->timeStamp = getCurrentTimeInMicroSeconds();
355 retData->timeout = CAGetTimeoutValue();
356 retData->triedCount = 0;
357 retData->messageId = messageId;
358 retData->endpoint = remoteEndpoint;
359 retData->pdu = pduData;
360 retData->size = size;
363 u_mutex_lock(context->threadMutex);
366 uint32_t len = u_arraylist_length(context->dataList);
368 // #3. add data into list
369 for (i = 0; i < len; i++)
371 CARetransmissionData_t *currData = u_arraylist_get(context->dataList, i);
373 if (currData == NULL)
379 if (retData != NULL && (currData->endpoint->connectivityType == endpoint->connectivityType)
380 && currData->messageId == messageId)
382 OIC_LOG_V(ERROR, TAG, "Duplicate message ID");
385 u_mutex_unlock(context->threadMutex);
389 OICFree(remoteEndpoint);
390 return CA_STATUS_FAILED;
394 u_arraylist_add(context->dataList, (void *) retData);
397 u_cond_signal(context->threadCond);
400 u_mutex_unlock(context->threadMutex);
405 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
406 const CARemoteEndpoint_t *endpoint, const void *pdu,
409 if (context == NULL || endpoint == NULL || pdu == NULL)
411 OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
412 return CA_STATUS_INVALID_PARAM;
415 // #0. check support connectivity type
416 if (!(context->config.supportType & endpoint->connectivityType))
418 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
419 endpoint->connectivityType);
423 // #1. check PDU method type and get message id.
424 // ACK, RST --> remove the CON data
425 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
426 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
428 OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
430 if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
436 u_mutex_lock(context->threadMutex);
439 uint32_t len = u_arraylist_length(context->dataList);
442 for (i = 0; i < len; i++)
444 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
447 if (retData != NULL && (retData->endpoint->connectivityType == endpoint->connectivityType)
448 && retData->messageId == messageId)
450 // #2. remove data from list
451 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
453 if (removedData != NULL)
455 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)",
458 CADestroyRemoteEndpointInternal(removedData->endpoint);
459 OICFree(removedData->pdu);
461 OICFree(removedData);
468 u_mutex_unlock(context->threadMutex);
473 CAResult_t CARetransmissionStop(CARetransmission_t *context)
477 OIC_LOG_V(DEBUG, TAG, "context is empty..");
478 return CA_STATUS_FAILED;
481 OIC_LOG_V(DEBUG, TAG, "retransmission stop request!!");
484 u_mutex_lock(context->threadMutex);
487 context->isStop = true;
490 u_cond_signal(context->threadCond);
492 u_cond_wait(context->threadCond, context->threadMutex);
495 u_mutex_unlock(context->threadMutex);
500 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
504 OIC_LOG_V(DEBUG, TAG, "context is empty..");
505 return CA_STATUS_FAILED;
508 OIC_LOG_V(DEBUG, TAG, "retransmission context destroy..");
510 u_mutex_free(context->threadMutex);
511 context->threadMutex = NULL;
512 u_cond_free(context->threadCond);
513 u_arraylist_free(&context->dataList);
518 uint64_t getCurrentTimeInMicroSeconds()
520 uint64_t currentTime = 0;
523 struct timespec getTs;
525 clock_gettime(CLOCK_MONOTONIC, &getTs);
527 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
528 OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
530 currentTime = g_get_monotonic_time();