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 /** last sent time. microseconds **/
43 /** retransmission count **/
45 /** coap PDU message id **/
47 /** remote endpoint **/
48 CARemoteEndpoint_t* endpoint;
53 } CARetransmissionData_t;
56 * getCurrent monotonic time
60 uint64_t getCurrentTimeInMicroSeconds();
64 * 2sec -> 4sec -> 8sec -> 16sec
68 static CABool_t CACheckTimeout(uint64_t currentTime, uint64_t timeStamp, uint8_t triedCount)
70 // #1. calculate timeout
71 uint64_t timeOut = (2 << triedCount) * 1000000;
73 if (currentTime >= timeStamp + timeOut)
75 OIC_LOG_V(DEBUG, TAG, "%d seconds time out!!, tried count(%d)",
76 (2 << triedCount), triedCount);
83 static void CACheckRetransmissionList(CARetransmission_t *context)
86 u_mutex_lock(context->threadMutex);
87 uint64_t currentTime = 0;
90 uint32_t len = u_arraylist_length(context->dataList);
92 for (i = 0; i < len; i++)
94 CARetransmissionData_t* retData = u_arraylist_get(context->dataList, i);
99 currentTime = getCurrentTimeInMicroSeconds();
101 if (CACheckTimeout(currentTime, retData->timeStamp, retData->triedCount))
103 // #2. if time's up, send the data.
104 if (context->dataSendMethod != NULL)
106 OIC_LOG_V(DEBUG, TAG, "retransmission CON data!!, message id(%d)",
108 context->dataSendMethod(retData->endpoint, retData->pdu, retData->size);
111 // #3. increase the retransmission count and update timestamp.
112 retData->timeStamp = currentTime;
113 retData->triedCount++;
116 // #4. if tried count is max, remove the retransmission data from list.
117 if (retData->triedCount >= context->config.tryingCount)
119 CARetransmissionData_t* removedData = u_arraylist_remove(context->dataList, i);
121 OIC_LOG_V(DEBUG, TAG, "max trying count, remove retransmission CON data!!\
122 message id(%d)", removedData->messageId);
124 CADestroyRemoteEndpointInternal(removedData->endpoint);
125 OICFree(removedData->pdu);
127 OICFree(removedData);
129 // modify loop value.
130 len = u_arraylist_length(context->dataList);
136 u_mutex_unlock(context->threadMutex);
139 static void CARetransmissionBaseRoutine(void *threadValue)
141 OIC_LOG_V(DEBUG, TAG, "retransmission main thread start..");
143 CARetransmission_t *context = (CARetransmission_t *) threadValue;
147 OIC_LOG_V(DEBUG, TAG, "thread data passing error!!");
152 while (!context->isStop)
155 u_mutex_lock(context->threadMutex);
157 if (u_arraylist_length(context->dataList) <= 0)
159 // if list is empty, thread will wait
160 OIC_LOG_V(DEBUG, TAG, "wait..there is no retransmission data.");
163 u_cond_wait(context->threadCond, context->threadMutex);
165 OIC_LOG_V(DEBUG, TAG, "wake up..");
169 // check each RETRANSMISSION_CHECK_PERIOD time.
170 OIC_LOG_V(DEBUG, TAG, "wait..(%d)microseconds", RETRANSMISSION_CHECK_PERIOD);
173 u_cond_timed_wait(context->threadCond, context->threadMutex,
174 RETRANSMISSION_CHECK_PERIOD);
178 u_mutex_unlock(context->threadMutex);
184 CACheckRetransmissionList(context);
187 u_cond_signal(context->threadCond);
189 OIC_LOG_V(DEBUG, TAG, "retransmission main thread end..");
193 CAResult_t CARetransmissionInitialize(CARetransmission_t* context, u_thread_pool_t handle,
194 CADataSendMethod_t retransmissionSendMethod, CARetransmissionConfig_t* config)
198 OIC_LOG_V(DEBUG, TAG, "thread instance is empty..");
199 return CA_STATUS_FAILED;
204 OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
205 return CA_STATUS_FAILED;
208 OIC_LOG_V(DEBUG, TAG, "thread initialize..");
210 memset(context, 0, sizeof(CARetransmission_t));
212 CARetransmissionConfig_t cfg;
213 memset(&cfg, 0, sizeof(CARetransmissionConfig_t));
218 cfg.supportType = DEFAULT_RETRANSMISSION_TYPE;
219 cfg.tryingCount = DEFAULT_RETRANSMISSION_COUNT;
229 // set send thread data
230 context->threadPool = handle;
231 context->threadMutex = u_mutex_new();
232 context->threadCond = u_cond_new();
233 context->dataSendMethod = retransmissionSendMethod;
234 context->config = cfg;
235 context->isStop = CA_FALSE;
236 context->dataList = u_arraylist_create();
241 CAResult_t CARetransmissionStart(CARetransmission_t* context)
245 OIC_LOG_V(DEBUG, TAG, "context is empty..");
246 return CA_STATUS_FAILED;
249 if (context->threadPool == NULL)
251 OIC_LOG_V(DEBUG, TAG, "thread pool handle is empty..");
252 return CA_STATUS_FAILED;
255 CAResult_t res = u_thread_pool_add_task(context->threadPool, CARetransmissionBaseRoutine,
258 if (res != CA_STATUS_OK)
260 OIC_LOG_V(DEBUG, TAG, "thread pool add task error(send thread).");
267 CAResult_t CARetransmissionSentData(CARetransmission_t* context,
268 const CARemoteEndpoint_t* endpoint,
269 const void* pdu, uint32_t size)
271 if (context == NULL || endpoint == NULL || pdu == NULL)
273 OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
274 return CA_STATUS_INVALID_PARAM;
277 // #0. check support connectivity type
278 if (!(context->config.supportType & endpoint->connectivityType))
280 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
281 endpoint->connectivityType);
285 // #1. check PDU method type and get message id.
286 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
287 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
289 OIC_LOG_V(DEBUG, TAG, "sent pdu, message type(%d), message id(%d)", type, messageId);
291 if (type != CA_MSG_CONFIRM)
296 // create retransmission data
297 CARetransmissionData_t* retData = (CARetransmissionData_t*) OICMalloc(
298 sizeof(CARetransmissionData_t));
302 OIC_LOG_V(DEBUG, TAG, "memory error!!");
303 return CA_MEMORY_ALLOC_FAILED;
305 memset(retData, 0, sizeof(CARetransmissionData_t));
308 void* pduData = (void*) OICMalloc(sizeof(int8_t) * size);
312 OIC_LOG_V(DEBUG, TAG, "memory error!!");
313 return CA_MEMORY_ALLOC_FAILED;
315 memset(pduData, 0, sizeof(int8_t) * size);
316 memcpy(pduData, pdu, sizeof(int8_t) * size);
318 // clone remote endpoint
319 CARemoteEndpoint_t* remoteEndpoint = CACloneRemoteEndpoint(endpoint);
320 if (remoteEndpoint == NULL)
324 OIC_LOG_V(DEBUG, TAG, "memory error!!");
325 return CA_MEMORY_ALLOC_FAILED;
328 // #2. add additional information. (time stamp, retransmission count...)
329 retData->timeStamp = getCurrentTimeInMicroSeconds();
330 retData->triedCount = 0;
331 retData->messageId = messageId;
332 retData->endpoint = remoteEndpoint;
333 retData->pdu = pduData;
334 retData->size = size;
337 u_mutex_lock(context->threadMutex);
339 // #3. add data into list
340 u_arraylist_add(context->dataList, (void*) retData);
343 u_cond_signal(context->threadCond);
346 u_mutex_unlock(context->threadMutex);
351 CAResult_t CARetransmissionReceivedData(CARetransmission_t* context,
352 const CARemoteEndpoint_t* endpoint, const void* pdu, uint32_t size)
354 if (context == NULL || endpoint == NULL || pdu == NULL)
356 OIC_LOG_V(DEBUG, TAG, "invalid parameter..");
357 return CA_STATUS_INVALID_PARAM;
360 // #0. check support connectivity type
361 if (!(context->config.supportType & endpoint->connectivityType))
363 OIC_LOG_V(DEBUG, TAG, "not supported connectivity type for retransmission..(%d)",
364 endpoint->connectivityType);
368 // #1. check PDU method type and get message id.
369 // ACK, RST --> remove the CON data
370 CAMessageType_t type = CAGetMessageTypeFromPduBinaryData(pdu, size);
371 uint16_t messageId = CAGetMessageIdFromPduBinaryData(pdu, size);
373 OIC_LOG_V(DEBUG, TAG, "received pdu, message type(%d), message id(%d)", type, messageId);
375 if (type != CA_MSG_ACKNOWLEDGE && type != CA_MSG_RESET)
381 u_mutex_lock(context->threadMutex);
384 uint32_t len = u_arraylist_length(context->dataList);
387 for (i = 0; i < len; i++)
389 CARetransmissionData_t* retData = u_arraylist_get(context->dataList, i);
395 if ((retData->endpoint->connectivityType == endpoint->connectivityType)
396 && retData->messageId == messageId)
400 // #2. remove data from list
403 CARetransmissionData_t* removedData = u_arraylist_remove(context->dataList, i);
405 OIC_LOG_V(DEBUG, TAG, "remove retransmission CON data!!, message id(%d)", messageId);
407 CADestroyRemoteEndpointInternal(removedData->endpoint);
408 OICFree(removedData->pdu);
410 OICFree(removedData);
414 u_mutex_unlock(context->threadMutex);
419 CAResult_t CARetransmissionStop(CARetransmission_t* context)
423 OIC_LOG_V(DEBUG, TAG, "context is empty..");
424 return CA_STATUS_FAILED;
427 OIC_LOG_V(DEBUG, TAG, "retransmission stop request!!");
430 u_mutex_lock(context->threadMutex);
433 context->isStop = CA_TRUE;
436 u_cond_signal(context->threadCond);
438 u_cond_wait(context->threadCond, context->threadMutex);
441 u_mutex_unlock(context->threadMutex);
446 CAResult_t CARetransmissionDestroy(CARetransmission_t* context)
450 OIC_LOG_V(DEBUG, TAG, "context is empty..");
451 return CA_STATUS_FAILED;
454 OIC_LOG_V(DEBUG, TAG, "retransmission context destroy..");
456 u_mutex_free(context->threadMutex);
457 context->threadMutex = NULL;
458 u_cond_free(context->threadCond);
459 u_arraylist_free(context->dataList);
464 uint64_t getCurrentTimeInMicroSeconds()
466 uint64_t currentTime = 0;
469 struct timespec getTs;
471 memset(&getTs, 0, sizeof(getTs));
472 clock_gettime(CLOCK_MONOTONIC, &getTs);
474 currentTime = (getTs.tv_sec * 1000000000 + getTs.tv_nsec)/1000;
475 OIC_LOG_V(DEBUG, TAG, "current time = %d", currentTime);
477 currentTime = g_get_monotonic_time();