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"
41 /** last sent time. microseconds **/
43 /** timeout value. milliseconds **/
45 /** retransmission count **/
47 /** coap PDU message id **/
49 /** remote endpoint **/
50 CARemoteEndpoint_t *endpoint;
55 } CARetransmissionData_t;
58 * getCurrent monotonic time
62 uint64_t getCurrentTimeInMicroSeconds();
65 * @brief check timeout routine
66 * @param currentTime [IN]microseconds
67 * @param timeStamp [IN]microseconds
68 * @param timeoutValue [IN]milliseconds
69 * @param triedCount [IN]
70 * @return CA_TRUE(timeout) or CA_FALSE
74 static CABool_t CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint32_t timeoutValue,
77 // #1. calculate timeout
78 uint64_t timeout = (timeoutValue << triedCount) * (uint64_t) 1000;
80 if (currentTime >= timeStamp + timeout)
82 OIC_LOG_V(DEBUG, TAG, "%d milliseconds time out!!, tried count(%d)",
83 (timeoutValue << triedCount), triedCount);
91 * @brief timeout value is
92 * between DEFAULT_ACK_TIMEOUT and (DEFAULT_ACK_TIMEOUT * DEFAULT_RANDOM_FACTOR) second.
93 * DEFAULT_RANDOM_FACTOR 1.5 (CoAP)
94 * @return milliseconds.
96 static uint32_t CAGetTimeoutValue()
98 return (DEFAULT_ACK_TIMEOUT * 1000) + ((1000 * (rand() & 0xFF)) >> 8);
101 static void CACheckRetransmissionList(CARetransmission_t *context)
104 u_mutex_lock(context->threadMutex);
105 uint64_t currentTime = 0;
108 uint32_t len = u_arraylist_length(context->dataList);
110 for (i = 0; i < len; i++)
112 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
117 currentTime = getCurrentTimeInMicroSeconds();
119 if (CACheckTimeout(currentTime, retData->timeStamp, retData->timeout, retData->triedCount))
121 // #2. if time's up, send the data.
122 if (context->dataSendMethod != NULL)
124 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
126 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
129 // #3. increase the retransmission count and update timestamp.
130 retData->timeStamp = currentTime;
131 retData->triedCount++;
134 // #4. if tried count is max, remove the retransmission data from list.
135 if (retData->triedCount >= context->config.tryingCount)
137 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
139 if (removedData != NULL)
141 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!,\
142 message id(%d)", removedData->messageId);
144 // callback for retransmit timeout
145 if (context->timeoutCallback != NULL)
147 context->timeoutCallback(removedData->endpoint, removedData->pdu,
151 CADestroyRemoteEndpointInternal(removedData->endpoint);
152 OICFree(removedData->pdu);
154 OICFree(removedData);
156 // modify loop value.
157 len = u_arraylist_length(context->dataList);
162 OIC_LOG_V(DEBUG, TAG, "arraylist remove error");
169 u_mutex_unlock(context->threadMutex);
172 static void CARetransmissionBaseRoutine(void *threadValue)
174 OIC_LOG_V(DEBUG, TAG, "retransmission main thread start..");
176 CARetransmission_t *context = (CARetransmission_t *) threadValue;
180 OIC_LOG_V(DEBUG, TAG, "thread data passing error!!");
185 while (!context->isStop)
188 u_mutex_lock(context->threadMutex);
190 if (u_arraylist_length(context->dataList) <= 0)
192 // if list is empty, thread will wait
193 OIC_LOG_V(DEBUG, TAG, "wait..there is no retransmission data.");
196 u_cond_wait(context->threadCond, context->threadMutex);
198 OIC_LOG_V(DEBUG, TAG, "wake up..");
202 // check each RETRANSMISSION_CHECK_PERIOD time.
203 OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds", RETRANSMISSION_CHECK_PERIOD);
206 u_cond_timed_wait(context->threadCond, context->threadMutex,
207 RETRANSMISSION_CHECK_PERIOD);
211 u_mutex_unlock(context->threadMutex);
217 CACheckRetransmissionList(context);
220 u_cond_signal(context->threadCond);
222 OIC_LOG_V(DEBUG, TAG, "retransmission main thread end..");
226 CAResult_t CARetransmissionInitialize(CARetransmission_t *context, u_thread_pool_t handle,
227 CADataSendMethod_t retransmissionSendMethod, CATimeoutCallback_t timeoutCallback,
228 CARetransmissionConfig_t* config)
232 OIC_LOG_V(DEBUG, TAG, "thread instance is empty..");
233 return CA_STATUS_FAILED;
238 OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
239 return CA_STATUS_FAILED;
242 OIC_LOG_V(DEBUG, TAG, "thread initialize..");
244 memset(context, 0, sizeof(CARetransmission_t));
246 CARetransmissionConfig_t cfg;
247 memset(&cfg, 0, sizeof(CARetransmissionConfig_t));
252 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
253 cfg.tryingCount = DEFAULT_MAX_RETRANSMIT;
263 // set send thread data
264 context->threadPool = handle;
265 context->threadMutex = u_mutex_new();
266 context->threadCond = u_cond_new();
267 context->dataSendMethod = retransmissionSendMethod;
268 context->timeoutCallback = timeoutCallback;
269 context->config = cfg;
270 context->isStop = CA_FALSE;
271 context->dataList = u_arraylist_create();
276 CAResult_t CARetransmissionStart(CARetransmission_t *context)
280 OIC_LOG_V(DEBUG, TAG, "context is empty..");
281 return CA_STATUS_FAILED;
284 if (context->threadPool == NULL)
286 OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
287 return CA_STATUS_FAILED;
290 CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
293 if (res != CA_STATUS_OK)
295 OIC_LOG_V(DEBUG, TAG, "thread pool add task error(send thread).");
302 CAResult_t CARetransmissionSentData(CARetransmission_t *context,
303 const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
305 if (context == NULL || endpoint == NULL || pdu == NULL)
307 OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
308 return CA_STATUS_INVALID_PARAM;
311 // #0. check support connectivity type
312 if (!(context->config.supportType & endpoint->connectivityType))
314 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
315 endpoint->connectivityType);
319 // #1. check PDU method type and get message id.
320 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
321 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
323 OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
325 if (type != CA_MSG_CONFIRM)
330 // create retransmission data
331 CARetransmissionData_t *retData = (CARetransmissionData_t *) OICMalloc(
332 sizeof(CARetransmissionData_t));
336 OIC_LOG_V(DEBUG, TAG, "memory error!!");
337 return CA_MEMORY_ALLOC_FAILED;
339 memset(retData, 0, sizeof(CARetransmissionData_t));
342 void *pduData = (void *) OICMalloc(sizeof(int8_t) * size);
346 OIC_LOG_V(DEBUG, TAG, "memory error!!");
347 return CA_MEMORY_ALLOC_FAILED;
349 memset(pduData, 0, sizeof(int8_t) * size);
350 memcpy(pduData, pdu, sizeof(int8_t) * size);
352 // clone remote endpoint
353 CARemoteEndpoint_t *remoteEndpoint = CACloneRemoteEndpoint(endpoint);
354 if (remoteEndpoint == NULL)
358 OIC_LOG_V(DEBUG, TAG, "memory error!!");
359 return CA_MEMORY_ALLOC_FAILED;
362 // #2. add additional information. (time stamp, retransmission count...)
363 retData->timeStamp = getCurrentTimeInMicroSeconds();
364 retData->timeout = CAGetTimeoutValue();
365 retData->triedCount = 0;
366 retData->messageId = messageId;
367 retData->endpoint = remoteEndpoint;
368 retData->pdu = pduData;
369 retData->size = size;
372 u_mutex_lock(context->threadMutex);
374 // #3. add data into list
375 u_arraylist_add(context->dataList, (void *) retData);
378 u_cond_signal(context->threadCond);
381 u_mutex_unlock(context->threadMutex);
386 CAResult_t CARetransmissionReceivedData(CARetransmission_t *context,
387 const CARemoteEndpoint_t *endpoint,
388 const void *pdu, uint32_t size,
389 void **retransmissionPdu)
391 OIC_LOG_V(DEBUG, TAG, "IN - CARetransmissionReceivedData");
392 if (context == NULL || endpoint == NULL || pdu == NULL)
394 OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
395 return CA_STATUS_INVALID_PARAM;
398 // #0. check support connectivity type
399 if (!(context->config.supportType & endpoint->connectivityType))
401 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
402 endpoint->connectivityType);
406 // #1. check PDU method type and get message id.
407 // ACK, RST --> remove the CON data
408 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
409 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
411 OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
413 if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
419 u_mutex_lock(context->threadMutex);
422 uint32_t len = u_arraylist_length(context->dataList);
425 for (i = 0; i < len; i++)
427 CARetransmissionData_t *retData = u_arraylist_get(context->dataList, i);
433 if ((retData->endpoint->connectivityType == endpoint->connectivityType)
434 && retData->messageId == messageId)
436 // get pdu data for getting token when CA_EMPTY(RST/ACK) is received from remote device
437 // if retransmission was finish..token will be unavailable.
438 if(CA_EMPTY == CAGetCodeFromPduBinaryData(pdu, size))
440 OIC_LOG_V(DEBUG, TAG, "code is CA_EMPTY..");
442 if(NULL == retData->pdu)
444 OIC_LOG_V(DEBUG, TAG, "retData->pdu is null");
448 (*retransmissionPdu) = (void *) OICMalloc(sizeof(int8_t) * retData->size);
449 if ((*retransmissionPdu) == NULL)
452 OIC_LOG_V(DEBUG, TAG, "memory error!!");
453 return CA_MEMORY_ALLOC_FAILED;
455 memset((*retransmissionPdu), 0, sizeof(int8_t) * retData->size);
456 memcpy((*retransmissionPdu), retData->pdu, sizeof(int8_t) * retData->size);
463 // #2. remove data from list
466 CARetransmissionData_t *removedData = u_arraylist_remove(context->dataList, i);
468 if (removedData != NULL)
470 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)", messageId);
472 CADestroyRemoteEndpointInternal(removedData->endpoint);
473 OICFree(removedData->pdu);
475 OICFree(removedData);
480 u_mutex_unlock(context->threadMutex);
482 OIC_LOG_V(DEBUG, TAG, "OUT - CARetransmissionReceivedData");
486 CAResult_t CARetransmissionStop(CARetransmission_t *context)
490 OIC_LOG_V(DEBUG, TAG, "context is empty..");
491 return CA_STATUS_FAILED;
494 OIC_LOG_V(DEBUG, TAG, "retransmission stop request!!");
497 u_mutex_lock(context->threadMutex);
500 context->isStop = CA_TRUE;
503 u_cond_signal(context->threadCond);
505 u_cond_wait(context->threadCond, context->threadMutex);
508 u_mutex_unlock(context->threadMutex);
513 CAResult_t CARetransmissionDestroy(CARetransmission_t *context)
517 OIC_LOG_V(DEBUG, TAG, "context is empty..");
518 return CA_STATUS_FAILED;
521 OIC_LOG_V(DEBUG, TAG, "retransmission context destroy..");
523 u_mutex_free(context->threadMutex);
524 context->threadMutex = NULL;
525 u_cond_free(context->threadCond);
526 u_arraylist_free(&context->dataList);
531 uint64_t getCurrentTimeInMicroSeconds()
533 uint64_t currentTime = 0;
536 struct timespec getTs;
538 memset(&getTs, 0, sizeof(getTs));
539 clock_gettime(CLOCK_MONOTONIC, &getTs);
541 currentTime = (getTs.tv_sec * (uint64_t)1000000000 + getTs.tv_nsec)/1000;
542 OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
544 currentTime = g_get_monotonic_time();