問題描述
在需要并發(fā)請求http時,需要通過多線程使用curl庫。curl的handle對象只要保證在一個線程中創(chuàng)建使用和銷毀,一般就不會有問題。
但是有一種類型的對象叫做curl_share,通過curl_share_init函數(shù)創(chuàng)建。用于給多個curl handle對象提供共享數(shù)據(jù),如為了降低請求延時,減少dns查詢時間,可以共享同一份dns緩存。
crash堆棧
#0 0x00007fe4339c9387 in raise () from /lib64/libc.so.6
#1 0x00007fe4339caa78 in abort () from /lib64/libc.so.6
#2 0x00007fe433a0bed7 in __libc_message () from /lib64/libc.so.6
#3 0x00007fe433a14299 in _int_free () from /lib64/libc.so.6
#4 0x00007fe434c82b6d in hash_element_dtor () from /lib64/libcurl.so.4
#5 0x00007fe434c8297f in Curl_llist_remove () from /lib64/libcurl.so.4
#6 0x00007fe434c8308a in Curl_hash_clean_with_criterium () from /lib64/libcurl.so.4
#7 0x00007fe434c5ebb7 in Curl_fetch_addr () from /lib64/libcurl.so.4
#8 0x00007fe434c5ed24 in Curl_resolv () from /lib64/libcurl.so.4
#9 0x00007fe434c763c5 in Curl_connect () from /lib64/libcurl.so.4
#10 0x00007fe434c857c3 in multi_runsingle () from /lib64/libcurl.so.4
#11 0x00007fe434c860f9 in curl_multi_perform () from /lib64/libcurl.so.4
#12 0x00007fe434c7e7db in curl_easy_perform () from /lib64/libcurl.so.4
解決方法:
設(shè)置加解鎖函數(shù)才可以加鎖保護
static void curl_dns_lock(CURL *h, curl_lock_data data, curl_lock_access access, void*userptr) {
dns_mutex.lock();
}
static void curl_dns_unlock(CURL *h, curl_lock_data data, curl_lock_access access, void*userptr) {
dns_mutex.unlock();
}
curl_share_setopt(dns_share_handle, CURLSHOPT_LOCKFUNC, curl_dns_lock);
curl_share_setopt(dns_share_handle, CURLSHOPT_UNLOCKFUNC, curl_dns_unlock);
分析過程分享
通過堆棧發(fā)現(xiàn)和dns有關(guān),因為有函數(shù)Curl_resolv。
查看curl源碼
git clone https://github.com/curl/curl.git
struct Curl_dns_entry *
Curl_fetch_addr(struct Curl_easy *data,
const char *hostname,
int port)
{
struct Curl_dns_entry *dns = NULL;
if(data->share)
Curl_share_lock(data, CURL_LOCK_DATA_DNS, CURL_LOCK_ACCESS_SINGLE);
dns = fetch_addr(data, hostname, port);
if(dns)
dns->inuse++; /* we use it! */
if(data->share)
Curl_share_unlock(data, CURL_LOCK_DATA_DNS);
return dns;
}
發(fā)現(xiàn)此處獲取地址,需要加鎖。但是如果加鎖了應(yīng)該不會出crash才對。
//初始化時,是這么寫的
dns_share_handle = curl_share_init();
curl_share_setopt(curl_dns_share_handle, CURLSHOPT_SHARE, CURL_LOCK_DATA_DNS);
查看share->lockfunc賦值代碼,發(fā)現(xiàn)是在curl_share_setopt中設(shè)置的:
curl_share_setopt(struct Curl_share *share, CURLSHoption option, ...) {
case CURLSHOPT_LOCKFUNC:
lockfunc = va_arg(param, curl_lock_function);
share->lockfunc = lockfunc;
break;
case CURLSHOPT_UNLOCKFUNC:
unlockfunc = va_arg(param, curl_unlock_function);
share->unlockfunc = unlockfunc;
break;
}