/** * Copyright (2017) Baidu Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ /** * File: lightduer_engine.c * Auth: Su Hao (suhao@baidu.com) * Desc: Light duer IoT CA engine. */ #include "duerapp_config.h" #include "lightduer_connagent.h" #include "lightduer_engine.h" #include "lightduer_log.h" #include "lightduer_timers.h" #include "lightduer_queue_cache.h" #include "lightduer_ca.h" #include "lightduer_memory.h" #include "lightduer_mutex.h" #include "lightduer_ds_report.h" #include "lightduer_event_emitter.h" #include "lightduer_timestamp.h" #include "lightduer_ds_log_ca.h" #include "lightduer_ds_report_ca.h" #include "lightduer_data_cache.h" #define DUER_MOST_BIT_MASK (0x80000000) extern int duer_data_send(void); static void duer_engine_notify(int event, int status, int what, void *object); static void duer_engine_timer_handler(int what, void *data); static void duer_transport_notify_handler(int what, void *data); void duer_engine_send_directly(int what, void *object); #define DUER_NOTIFY_ONLY(_evt, _status) duer_engine_notify(_evt, _status, 0, NULL) static duer_handler g_handler = NULL; static duer_engine_notify_func g_notify_func = NULL; static duer_timer_handler g_timer = NULL; static volatile duer_qcache_handler g_qcache_handler = NULL; static duer_mutex_t g_qcache_mutex = NULL; #ifdef ENABLE_RECV_TIMEOUT_THRESHOLD static duer_u32_t g_recv_timestamp = 0; #endif #ifndef DUER_KEEPALIVE_INTERVAL #define DUER_KEEPALIVE_INTERVAL (55 * 1000) #endif #define DUER_START_TIMEOUT (1 * 3000) // 8s(notify DUER_ERR_TRANS_TIMEOUT after about 16s) #define DUER_START_INITIAL_RETRY_INTERVAL (1 * 500) // retry on 500ms, 1s, 2s, 4s, 8s #define DUER_RECV_TIMEOUT_THRESHOLD (DUER_KEEPALIVE_INTERVAL + 3000) #define DUER_COAP_EXEC_INTERVAL (5 * 1000) #ifdef DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE #define DUER_PRINT_BJSON_ESTIMATED_SIZE_FOR_REPORT_DATA (1024) #endif #define DUER_MAX_CACHED_MESSAGE_BEFORE_TRIGGER_REAL_SEND (10) typedef struct _duer_context_index_item { duer_u32_t index; duer_context_t *context; char payload[0]; } duer_context_index_item; static void duer_timer_expired(void *param) { duer_emitter_emit(duer_engine_timer_handler, 0, NULL); } static void duer_transport_notify(duer_transevt_e event) { duer_emitter_emit(duer_transport_notify_handler, event, NULL); } static void duer_engine_notify(int event, int status, int what, void *object) { if (g_notify_func) { g_notify_func(event, status, what, object); } else { DUER_LOGI("g_notify_func is not set"); } } static void duer_transport_notify_handler(int what, void *data) { duer_transevt_e event = (duer_transevt_e) what; switch (event) { case DUER_TEVT_RECV_RDY: duer_data_available(); break; case DUER_TEVT_SEND_RDY: { duer_bool is_started = baidu_ca_is_started(g_handler); if (is_started) { duer_emitter_emit(duer_engine_send_directly, 0, NULL); // duer_data_send(); } else if (baidu_ca_is_stopped(g_handler) == DUER_FALSE) { DUER_NOTIFY_ONLY(DUER_EVT_START, DUER_ERR_CONNECT_TIMEOUT);// trigger reconnection } break; } case DUER_TEVT_SEND_TIMEOUT: DUER_LOGI("duer_transport_notify send timeout!"); DUER_NOTIFY_ONLY(DUER_EVT_SEND_DATA, DUER_ERR_TRANS_TIMEOUT); break; default: DUER_LOGI("duer_transport_notify unknow event:%d", event); break; } } static void duer_engine_timer_handler(int what, void *data) { duer_bool is_started = baidu_ca_is_started(g_handler); if (is_started) { baidu_json *alive = baidu_json_CreateObject(); if (alive) { baidu_json_AddNumberToObject(alive, "alive", 0); int ret = duer_data_report(alive); if (ret != DUER_OK) { DUER_LOGW("report alive fail!"); } baidu_json_Delete(alive); } else { DUER_LOGW("create alive fail(maybe memory not available now)!"); } } else if (baidu_ca_is_stopped(g_handler) == DUER_FALSE) { DUER_NOTIFY_ONLY(DUER_EVT_START, DUER_ERR_CONNECT_TIMEOUT);// trigger reconnection } } static duer_status_t duer_engine_transmit_handler(duer_trans_handler hdlr, const void *data, duer_size_t size, duer_addr_t *addr) { // s_transmit_index to record the real write_to_socket time for every transmit data static duer_u32_t s_transmit_index = 0; // use temporary struct to accept content in parm:data and add a head into struct; // after duer_dcache_push just free the struct at once duer_context_index_item *push_item = (duer_context_index_item *) DUER_MALLOC(sizeof(duer_context_index_item) + size); if (!push_item) { DUER_LOGI("Allocate memory for push_item fail."); return DUER_ERR_FAILED; } push_item->index = s_transmit_index; push_item->context = NULL; DUER_MEMCPY(push_item->payload, data, size); duer_status_t rs = duer_dcache_push( push_item, sizeof(duer_context_index_item) + size, DUER_FALSE); push_item = NULL; if (rs < DUER_OK) { return rs; } if (rs == 1) { #ifdef DUER_SEND_WITHOUT_FIRST_CACHE duer_engine_send_directly(0, NULL); #else duer_emitter_emit(duer_engine_send_directly, 0, NULL); #endif // DUER_SEND_WITHOUT_FIRST_CACHE } #ifdef DUER_SEND_WITHOUT_FIRST_CACHE if (rs > DUER_MAX_CACHED_MESSAGE_BEFORE_TRIGGER_REAL_SEND) { // if too much cached > DUER_MAX_CACHED_MESSAGE_BEFORE_TRIGGER_REAL_SEND, // trigger send immediately. duer_engine_send_directly(0, NULL); } #endif // DUER_SEND_WITHOUT_FIRST_CACHE return s_transmit_index++; } void duer_engine_register_notify(duer_engine_notify_func func) { if (g_notify_func != func) { g_notify_func = func; } else { DUER_LOGI("g_notify_func is update to func:%p", func); } } void duer_engine_create(int what, void *object) { if (g_handler == NULL) { g_handler = baidu_ca_acquire(duer_transport_notify); } if (g_qcache_handler == NULL) { g_qcache_handler = duer_qcache_create(); } if (g_qcache_mutex == NULL) { if ((g_qcache_mutex = duer_mutex_create()) == NULL) { DUER_LOGW("Create mutex failed!!"); } } if (g_timer == NULL) { g_timer = duer_timer_acquire(duer_timer_expired, NULL, DUER_TIMER_ONCE); } duer_dcache_initialize(); DUER_NOTIFY_ONLY(DUER_EVT_CREATE, DUER_OK); } void duer_engine_set_response_callback(int what, void *object) { if (g_handler && object) { DUER_LOGD("set the coap response callback %p", object); baidu_ca_report_set_response_callback(g_handler, (duer_notify_f)object, NULL); } } void duer_engine_start(int what, void *object) { static int start_timeout; duer_status_t rs = DUER_OK; int status = DUER_OK; duer_bool is_started = baidu_ca_is_started(g_handler); do { if (is_started) { DUER_LOGI("already started, what:%d, object:%p", what, object); status = DUER_ERR_HAS_STARTED; break; } DUER_LOGI("duer_engine_start, g_handler:%p, length:%d, profile:%p", g_handler, what, object); // add this check. so duer_engine_start become re-enterable without the profile param. if (what > 0 && object != NULL) { start_timeout = DUER_START_INITIAL_RETRY_INTERVAL; // object contains the profile content, and what is the length of the profile. //DUER_LOGI("the length of profile: %p is %d", (const char *)object, (size_t)what); rs = baidu_ca_load_configuration(g_handler, (const void *)object, (size_t)what); if (rs < DUER_OK) { status = DUER_ERR_PROFILE_INVALID; duer_ds_log_ca_cache_error(DUER_DS_LOG_CA_START_PROFILE_ERR); break; } rs = baidu_ca_report_set_data_tx_callback(g_handler, duer_engine_transmit_handler); if (rs < DUER_OK) { status = DUER_ERR_FAILED; duer_ds_log_ca_cache_error(DUER_DS_LOG_CA_ERR); break; } } rs = baidu_ca_start(g_handler); if (rs == DUER_ERR_TRANS_WOULD_BLOCK) { status = DUER_ERR_TRANS_WOULD_BLOCK; } else if (rs != DUER_OK) { DUER_LOGI("start fail, status:%d, rs:%d", status, rs); status = DUER_ERR_FAILED; } else { // record the registered timestamp #ifdef ENABLE_RECV_TIMEOUT_THRESHOLD g_recv_timestamp = duer_timestamp(); DUER_LOGD("Started recv_timestamp = %u", g_recv_timestamp); #endif } } while (0); if (g_timer != NULL) { if (status == DUER_OK) { duer_timer_start(g_timer, DUER_KEEPALIVE_INTERVAL); } else if (status == DUER_ERR_TRANS_WOULD_BLOCK) { if (start_timeout > DUER_START_TIMEOUT) { status = DUER_ERR_TRANS_TIMEOUT;// start timeout start_timeout = DUER_START_INITIAL_RETRY_INTERVAL; } else { duer_timer_start(g_timer, start_timeout); start_timeout <<= 1; } } else { // do nothing } } else { DUER_LOGW("g_timer is NULL"); } duer_engine_notify(DUER_EVT_START, status, 0, object); } void duer_engine_add_resources(int what, void *object) { duer_status_t rs = baidu_ca_add_resources(g_handler, (const duer_res_t *)object, (size_t)what); duer_engine_notify(DUER_EVT_ADD_RESOURCES, rs < DUER_OK ? DUER_ERR_FAILED : DUER_OK, what, object); } const char *duer_engine_get_uuid(void) { return baidu_ca_get_uuid(g_handler); } const char *duer_engine_get_client_id(void) { return baidu_ca_get_client_id(g_handler); } const char *duer_engine_get_bduss(void) { return baidu_ca_get_bduss(g_handler); } const char *duer_engine_get_user_agent(void) { return baidu_ca_get_user_agent(g_handler); } const char *duer_engine_get_bind_token(void) { return baidu_ca_get_bind_token(g_handler); } const char *duer_engine_get_rsa_cacrt(void) { return baidu_ca_get_rsa_cacrt(g_handler); } void duer_engine_data_available(int what, void *object) { duer_status_t rs = DUER_OK; int status = DUER_OK; duer_bool is_started; DUER_LOGD("duer_engine_data_available"); do { is_started = baidu_ca_is_stopped(g_handler); if (is_started == DUER_TRUE) { DUER_LOGW("duer_engine_data_available has stopped"); status = DUER_ERR_HAS_STOPPED; break; } duer_ds_report_ca_inc_recv_count(); is_started = baidu_ca_is_started(g_handler); rs = baidu_ca_data_available(g_handler, NULL); if (rs == DUER_ERR_TRANS_WOULD_BLOCK) { status = DUER_ERR_TRANS_WOULD_BLOCK; DUER_LOGD("receive WOULDBLOCK!!"); } else if (rs < DUER_OK) { status = DUER_ERR_FAILED; } else { #ifdef ENABLE_RECV_TIMEOUT_THRESHOLD g_recv_timestamp = duer_timestamp(); DUER_LOGD("Received recv_timestamp = %u", g_recv_timestamp); #endif duer_ds_report_ca_inc_valid_recv(); } } while (0); if (is_started == DUER_TRUE) { DUER_NOTIFY_ONLY(DUER_EVT_DATA_AVAILABLE, status); } else { DUER_LOGD("status == %d && g_timer != %p", status, g_timer); if (status == DUER_OK && g_timer != NULL) { // maybe started to handle the connect timeout //duer_timer_stop(g_timer); // stop the timer before start duer_timer_start(g_timer, DUER_KEEPALIVE_INTERVAL); } DUER_NOTIFY_ONLY(DUER_EVT_START, status); } } void duer_engine_release_data(duer_msg_t *msg) { if (msg) { if (msg->payload != NULL) { if (DUER_MESSAGE_IS_RESPONSE(msg->msg_code)) { DUER_FREE(msg->payload); } else { baidu_json_release(msg->payload); } msg->payload = NULL; } if (msg->context != NULL) { if (msg->context->_on_report_finish) { msg->context->_on_report_finish(msg->context); msg->context->_on_report_finish = NULL; } DUER_FREE(msg->context); msg->context = NULL; } if (g_handler == NULL) { DUER_LOGE("release data failed!"); } else { baidu_ca_release_message(g_handler, msg); } } } void duer_engine_clear_data(int what, void *object) { duer_msg_t *msg = NULL; duer_mutex_lock(g_qcache_mutex); while ((msg = duer_qcache_top(g_qcache_handler)) != NULL) { if (what == 0 || (msg->create_timestamp < (duer_u32_t)what) || (// timestamp overflow (msg->create_timestamp & DUER_MOST_BIT_MASK) != 0 && (((duer_u32_t)what) & DUER_MOST_BIT_MASK) == 0 ) ) { duer_qcache_pop(g_qcache_handler); duer_engine_release_data(msg); } else { break; } } duer_mutex_unlock(g_qcache_mutex); } /** return the cache length if success, or error code */ int duer_engine_enqueue_report_data(duer_context_t *context, const baidu_json *data) { int rs = DUER_ERR_INVALID_PARAMETER; char *content = NULL; baidu_json *payload = NULL; duer_msg_t *msg = NULL; do { if (baidu_ca_is_started(g_handler) == DUER_FALSE) { DUER_LOGW("duer_engine_send not started"); rs = DUER_ERR_CA_NOT_CONNECTED; break; } if (data == NULL || g_qcache_handler == NULL || g_handler == NULL) { break; } payload = baidu_json_CreateObject(); if (payload == NULL) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } baidu_json_AddItemReferenceToObject(payload, "data", (baidu_json *)data); #ifdef DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE content = baidu_json_PrintBuffered( payload, DUER_PRINT_BJSON_ESTIMATED_SIZE_FOR_REPORT_DATA, 0); #else content = baidu_json_PrintUnformatted(payload); #endif // DUER_BJSON_PRINT_WITH_ESTIMATED_SIZE DUER_LOGD("enqueue content:%s", content); baidu_json_Delete(payload); msg = baidu_ca_build_report_message(g_handler, DUER_TRUE); if (msg == NULL) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } msg->payload = (duer_u8_t *)content; msg->payload_len = DUER_STRLEN(content); if (context != NULL) { msg->context = DUER_MALLOC(sizeof(duer_context_t)); // ~= 16 if (msg->context) { DUER_MEMCPY(msg->context, context, sizeof(duer_context_t)); } } #ifdef DUER_SEND_WITHOUT_FIRST_CACHE duer_engine_send(0, msg); rs = DUER_OK; #else duer_mutex_lock(g_qcache_mutex); rs = duer_qcache_push(g_qcache_handler, msg); if (rs == DUER_OK) { rs = duer_qcache_length(g_qcache_handler); } duer_mutex_unlock(g_qcache_mutex); #endif // DUER_SEND_WITHOUT_FIRST_CACHE } while (0); if (rs < 0) { DUER_LOGE("Report failed: rs = %d", rs); if (msg) { duer_engine_release_data(msg); } else if (content) { baidu_json_release(content); } } return rs; } /** return the cache length if success, or error code */ int duer_engine_enqueue_response(const duer_msg_t *req, int msg_code, const void *data, duer_size_t size) { int rs = DUER_ERR_INVALID_PARAMETER; char *content = NULL; duer_msg_t *msg = NULL; do { if (baidu_ca_is_stopped(g_handler)) { DUER_LOGW("duer_engine_send has stopped"); rs = DUER_ERR_CA_NOT_CONNECTED; break; } if (req == NULL || g_handler == NULL) { break; } msg = baidu_ca_build_response_message(g_handler, req, msg_code); if (msg == NULL) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } if (size > 0) { content = (char *)DUER_MALLOC(size); if (content == NULL) { rs = DUER_ERR_MEMORY_OVERFLOW; DUER_DS_LOG_CA_MEMORY_OVERFLOW(); break; } DUER_MEMCPY(content, data, size); } msg->payload = (duer_u8_t *)content; msg->payload_len = size; #ifdef DUER_SEND_WITHOUT_FIRST_CACHE duer_engine_send(0, msg); rs = DUER_OK; #else duer_mutex_lock(g_qcache_mutex); rs = duer_qcache_push(g_qcache_handler, msg); if (rs == DUER_OK) { rs = duer_qcache_length(g_qcache_handler); } duer_mutex_unlock(g_qcache_mutex); #endif // DUER_SEND_WITHOUT_FIRST_CACHE } while (0); if (rs < 0) { DUER_LOGE("Response failed: rs = %d", rs); if (msg) { duer_engine_release_data(msg); } else if (content) { DUER_FREE(content); } } return rs; } /** return the cache length if success, or error code */ int duer_engine_enqueue_seperate_response(const char *token, duer_size_t token_len, int msg_code, const baidu_json *data) { int rs = DUER_ERR_INVALID_PARAMETER; char *content = NULL; duer_msg_t *msg = NULL; char *data_str = NULL; duer_u32_t str_size = 0; do { if (baidu_ca_is_stopped(g_handler)) { DUER_LOGW("duer_engine_send has stopped"); rs = DUER_ERR_CA_NOT_CONNECTED; break; } if (g_handler == NULL) { break; } msg = baidu_ca_build_seperate_response_message(g_handler, token, token_len, msg_code, DUER_TRUE); if (msg == NULL) { rs = DUER_ERR_MEMORY_OVERFLOW; break; } if (data) { data_str = baidu_json_PrintUnformatted(data); DUER_LOGD("enqueue content:%s", data_str); str_size = DUER_STRLEN(data_str); content = (char *)DUER_MALLOC(str_size);// response message see duer_engine_release_data if (content == NULL) { rs = DUER_ERR_MEMORY_OVERFLOW; DUER_DS_LOG_CA_MEMORY_OVERFLOW(); break; } DUER_MEMCPY(content, data_str, str_size); } msg->payload = (duer_u8_t *)content; msg->payload_len = str_size; #ifdef DUER_SEND_WITHOUT_FIRST_CACHE duer_engine_send(0, msg); rs = DUER_OK; #else duer_mutex_lock(g_qcache_mutex); rs = duer_qcache_push(g_qcache_handler, msg); if (rs == DUER_OK) { rs = duer_qcache_length(g_qcache_handler); } duer_mutex_unlock(g_qcache_mutex); #endif // DUER_SEND_WITHOUT_FIRST_CACHE } while (0); if (rs < 0) { DUER_LOGE("Response failed: rs = %d", rs); if (msg) { duer_engine_release_data(msg); msg = NULL; } if (content) { DUER_FREE(content); content = NULL; } } if (data_str) { baidu_json_release(data_str); } return rs; } static duer_context_index_item *duer_get_dcache_item_by_index(duer_u32_t index) { duer_context_index_item *context_index_item = NULL; duer_dcache_item *cache = NULL; void *dcache_iterator = duer_dcache_create_iterator(); while (dcache_iterator) { cache = duer_dcache_get_data(dcache_iterator); if (!cache) { DUER_LOGI("cache is NULL, break"); break; } duer_context_index_item *item_tmp = (duer_context_index_item *)(cache->data); if (item_tmp && item_tmp->index == index) { context_index_item = item_tmp; break; } else { dcache_iterator = duer_dcache_get_next(dcache_iterator); } } if (!context_index_item) { DUER_LOGI("No item found with same index, abnormal!"); } return context_index_item; } void duer_engine_send(int what, void *object) { duer_status_t rs = DUER_OK; int status = DUER_OK; duer_msg_t *msg = NULL; duer_u32_t timestamp; static duer_u32_t last_coap_exec_time = 0; do { if (baidu_ca_is_stopped(g_handler)) { DUER_LOGW("duer_engine_send has stopped"); status = DUER_ERR_HAS_STOPPED; break; } #ifdef DUER_SEND_WITHOUT_FIRST_CACHE if (duer_dcache_length() > 0) { if (duer_qcache_length(g_qcache_handler) > 0) { DUER_LOGW("data cache has not sent, pending..., dcache_len:%d, qcache_len:%d", duer_dcache_length(), duer_qcache_length(g_qcache_handler)); } } #else if (duer_dcache_length() > 0) { if (duer_qcache_length(g_qcache_handler) > 0) { DUER_LOGW("data cache has not sent, pending..., dcache_len:%d, qcache_len:%d", duer_dcache_length(), duer_qcache_length(g_qcache_handler)); status = DUER_ERR_TRANS_WOULD_BLOCK; } break; } #endif // DUER_SEND_WITHOUT_FIRST_CACHE timestamp = duer_timestamp(); if (timestamp - last_coap_exec_time > DUER_COAP_EXEC_INTERVAL) { baidu_ca_exec(g_handler); last_coap_exec_time = timestamp; } #ifdef DUER_SEND_WITHOUT_FIRST_CACHE msg = object; #else duer_mutex_lock(g_qcache_mutex); msg = duer_qcache_top(g_qcache_handler); duer_mutex_unlock(g_qcache_mutex); #endif // DUER_SEND_WITHOUT_FIRST_CACHE if (msg == NULL) { break; } if (msg->context && msg->context->_on_report_start) { msg->context->_on_report_start(msg->context); msg->context->_on_report_start = NULL; } rs = baidu_ca_send_data(g_handler, msg, NULL); // always success now if (msg->context) { duer_context_index_item *item = duer_get_dcache_item_by_index(rs); msg->context->_status = rs; if (item) { item->context = msg->context; //avoid release_data to free context msg->context = NULL; } } if (rs == DUER_ERR_TRANS_WOULD_BLOCK) { status = DUER_ERR_TRANS_WOULD_BLOCK; } else if (rs < DUER_OK) { status = DUER_ERR_FAILED; } else { int latency = 0; DUER_LOGV("old:timestamp:%u,cts:%u", timestamp, msg->create_timestamp); timestamp = duer_timestamp(); if (timestamp > msg->create_timestamp) { latency = timestamp - msg->create_timestamp; duer_ds_report_ca_update_avg_latency(latency); } DUER_LOGV("new_timestamp:%u, latency:%u", timestamp, latency); // #ifdef DUER_DEBUG_AFTER_SEND DUER_LOGI("data sent: %.*s", msg->payload_len, DUER_STRING_OUTPUT((const char *)msg->payload)); // #endif duer_engine_release_data(msg); msg = NULL; #ifdef DUER_SEND_WITHOUT_FIRST_CACHE // do nothing #else duer_mutex_lock(g_qcache_mutex); duer_qcache_pop(g_qcache_handler); duer_mutex_unlock(g_qcache_mutex); #endif // DUER_SEND_WITHOUT_FIRST_CACHE } if (rs != DUER_ERR_TRANS_WOULD_BLOCK && msg && msg->context && msg->context->_on_report_finish) { msg->context->_on_report_finish(msg->context); //Note: message not send out really. msg->context->_on_report_finish = NULL; } } while (0); //duer_engine_notify(DUER_EVT_SEND_DATA, status, what, object); } void duer_engine_send_directly(int what, void *object) { duer_status_t rs = DUER_OK; int status = DUER_OK; duer_dcache_item *cache = NULL; #ifdef ENABLE_RECV_TIMEOUT_THRESHOLD duer_u32_t timestamp; #endif do { if (baidu_ca_is_stopped(g_handler)) { DUER_LOGW("duer_engine_send has stopped"); status = DUER_ERR_HAS_STOPPED; break; } #ifdef ENABLE_RECV_TIMEOUT_THRESHOLD timestamp = duer_timestamp(); if (g_recv_timestamp) { if (timestamp > g_recv_timestamp) { if (timestamp - g_recv_timestamp > DUER_RECV_TIMEOUT_THRESHOLD) { DUER_LOGE("duer has stopped! timestamp = %u, recv_timestamp = %u", timestamp, g_recv_timestamp); status = DUER_ERR_TRANS_TIMEOUT; break; } } else { g_recv_timestamp = 0; } } #endif cache = duer_dcache_top(); if (cache == NULL) { break; } if (g_timer != NULL) { duer_timer_stop(g_timer); } duer_context_index_item *sendout_item = (duer_context_index_item *)cache->data; rs = baidu_ca_send_data_directly(g_handler, sendout_item->payload, cache->size - sizeof(duer_context_index_item), NULL); if (rs == DUER_ERR_TRANS_WOULD_BLOCK) { status = DUER_ERR_TRANS_WOULD_BLOCK; } else if (rs < DUER_OK) { status = DUER_ERR_FAILED; } else { if (g_timer != NULL) { duer_timer_start(g_timer, DUER_KEEPALIVE_INTERVAL); } duer_context_index_item *context_index_item = (duer_context_index_item *)(cache->data); if (context_index_item && context_index_item->context) { if (context_index_item->context->_on_report_finish) { context_index_item->context->_on_report_finish(context_index_item->context); context_index_item->context->_on_report_finish = NULL; } DUER_FREE(context_index_item->context); context_index_item->context = NULL; } duer_dcache_pop(); if (duer_dcache_length() > 0) { // can not enter here duer_emitter_emit(duer_engine_send_directly, 0, NULL); } } } while (0); duer_engine_notify(DUER_EVT_SEND_DATA, status, what, object); } static void duer_free_context_in_dcache(void) { duer_context_index_item *context_index_item = NULL; duer_dcache_item *cache = NULL; void *dcache_iterator = duer_dcache_create_iterator(); while (dcache_iterator) { cache = duer_dcache_get_data(dcache_iterator); if (!cache) { DUER_LOGI("cache is NULL, break"); break; } context_index_item = (duer_context_index_item *)(cache->data); dcache_iterator = duer_dcache_get_next(dcache_iterator); if (context_index_item && context_index_item->context) { context_index_item->context->_status = DUER_CANCEL; if (context_index_item->context->_on_report_finish) { context_index_item->context->_on_report_finish(context_index_item->context); context_index_item->context->_on_report_finish = NULL; } DUER_FREE(context_index_item->context); context_index_item->context = NULL; } } } void duer_engine_stop(int what, void *object) { duer_status_t rs = DUER_OK; do { if (baidu_ca_is_stopped(g_handler)) { DUER_LOGW("duer_engine_stop has stopped"); rs = DUER_ERR_HAS_STOPPED; break; } #ifdef ENABLE_RECV_TIMEOUT_THRESHOLD g_recv_timestamp = 0; #endif rs = baidu_ca_stop(g_handler); if (rs == DUER_ERR_TRANS_WOULD_BLOCK) { DUER_LOGW("baidu_ca_stop DUER_ERR_TRANS_WOULD_BLOCK"); } else if (rs < DUER_OK) { DUER_LOGW("baidu_ca_stop rs:%d", rs); } else { DUER_LOGI("no action"); // do nothing } } while (0); //free context data in dcache duer_free_context_in_dcache(); duer_dcache_clear(); DUER_NOTIFY_ONLY(DUER_EVT_STOP, what); } void duer_engine_destroy(int what, void *object) { if (g_timer != NULL) { duer_timer_release(g_timer); g_timer = NULL; } if (g_qcache_handler != NULL) { duer_engine_clear_data(0, NULL); duer_mutex_lock(g_qcache_mutex); duer_qcache_destroy(g_qcache_handler); g_qcache_handler = NULL; duer_mutex_unlock(g_qcache_mutex); } if (g_qcache_mutex != NULL) { duer_mutex_destroy(g_qcache_mutex); g_qcache_mutex = NULL; } if (g_handler != NULL) { baidu_ca_release(g_handler); g_handler = NULL; } duer_dcache_finalize(); DUER_NOTIFY_ONLY(DUER_EVT_DESTROY, DUER_OK); } int duer_engine_qcache_length() { int len = 0; if (g_qcache_handler == NULL) { return 0; } duer_mutex_lock(g_qcache_mutex); len = duer_qcache_length(g_qcache_handler); duer_mutex_unlock(g_qcache_mutex); return len; } duer_bool duer_engine_is_started() { return baidu_ca_is_started(g_handler); }