curl-library
Problem/Crash with libCurl Daily Snapshot and Option CURLSHOPT_SHARE and Parameter CURL_LOCK_DATA_CONNECT
Date: Tue, 28 Nov 2017 15:36:26 +0000
Hi all,
I tried the new option CURLSHOPT_SHARE with parameter CURL_LOCK_DATA_CONNECT today that will be available in libcurl 7.57.0 according to https://curl.haxx.se/dev/release-notes.html.
This option would be very useful for us as we want to use libcurl in a multithreaded environment. Sadly I discovered random crashes with different callstacks when using this option.
I wonder if I configured something wrong or forgot something. Or maybe I misunderstood the use case for this option completely.
I have put together a small example that leads to the same error as I encountered in our software.
#include <windows.h>
#include <iostream>
#include <tchar.h>
#include <strsafe.h>
#include <curl/curl.h>
using namespace std;
const size_t MAX_THREADS = 3;
const size_t ITERATIONS = 100;
#define CHECK_CURL(cmd) \
if ((cmd) != CURLE_OK) \
{ \
throw runtime_error( "Error executing: "#cmd ); \
}
DWORD WINAPI MyThreadFunction( LPVOID lpParam );
HANDLE ghMutex;
static void ShareLockFunc( CURL* pHandle, curl_lock_data Data, curl_lock_access Access, void* pUseptr )
{
(void)pHandle;
(void)Data;
(void)Access;
(void)pUseptr;
WaitForSingleObject(
ghMutex, // handle to mutex
INFINITE ); // no time-out interval
}
static void ShareUnlockFunc( CURL* pHandle, curl_lock_data Data, void* pUseptr )
{
(void)pHandle;
(void)Data;
(void)pUseptr;
ReleaseMutex( ghMutex );
}
typedef struct MyData {
CURLSH* pShare;
} MYDATA, *PMYDATA;
int CurlPerformGet( CURL* pHandle )
{
CHECK_CURL( curl_easy_setopt( pHandle, CURLOPT_FOLLOWLOCATION, 1L ) );
CHECK_CURL( curl_easy_setopt( pHandle, CURLOPT_URL, "http://127.0.0.1:3000" ) );
CHECK_CURL( curl_easy_perform( pHandle ) );
int StatusCode = 0;
CHECK_CURL( curl_easy_getinfo( pHandle, CURLINFO_RESPONSE_CODE, &StatusCode ) );
return StatusCode;
}
int _tmain()
{
PMYDATA pDataArray[MAX_THREADS];
DWORD dwThreadIdArray[MAX_THREADS];
HANDLE hThreadArray[MAX_THREADS];
ghMutex = CreateMutex(
NULL, // default security attributes
FALSE, // initially not owned
NULL ); // unnamed mutex
if (ghMutex == NULL)
{
printf( "CreateMutex error: %d\n", GetLastError() );
return 1;
}
(void)curl_global_init( CURL_GLOBAL_ALL );
CURLSH *pShare = curl_share_init();
curl_share_setopt( pShare, CURLSHOPT_SHARE, CURL_LOCK_DATA_CONNECT );
// Create MAX_THREADS worker threads.
for (int i = 0; i < MAX_THREADS; i++)
{
pDataArray[i] = (PMYDATA)HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY,
sizeof( MYDATA ) );
pDataArray[i]->pShare = pShare;
if (pDataArray[i] == NULL)
{
ExitProcess( 2 );
}
hThreadArray[i] = CreateThread(
NULL, // default security attributes
0, // use default stack size
MyThreadFunction, // thread function name
pDataArray[i],
0, // use default creation flags
&dwThreadIdArray[i] ); // returns the thread identifier
if (hThreadArray[i] == NULL)
{
cout << "Create thread failed" << endl;
ExitProcess( 3 );
}
}
WaitForMultipleObjects( MAX_THREADS, hThreadArray, TRUE, INFINITE );
for (int i = 0; i < MAX_THREADS; i++)
{
CloseHandle( hThreadArray[i] );
}
curl_share_cleanup( pShare );
curl_global_cleanup();
CloseHandle( ghMutex );
return 0;
}
DWORD WINAPI MyThreadFunction( LPVOID lpParam )
{
PMYDATA pData = (PMYDATA)lpParam;
CURLSH* pShare = pData->pShare;
for (size_t i = 0; i < ITERATIONS; ++i)
{
CURL* pHandle = curl_easy_init();
curl_easy_setopt( pHandle, CURLOPT_SHARE, pShare );
cout << "Status: " << CurlPerformGet( pHandle ) << endl;
curl_easy_cleanup( pHandle );
}
return 0;
}
As a backend server I used a simple nodejs express server:
const express = require('express');
const app = express();
let requestsCount = 0;
process.on('uncaughtException', function (err) {
console.log("uncaught exception");
console.log(err);
});
app.get('/', (req, res) => {
console.log(req.headers);
requestsCount += 1;
setTimeout(() => {
res.send('Hello World!');
}, 1);
});
app.use(function (err, req, res, next) {
console.error("Express error catched!", err);
res.status(500).send("Error happened");
});
const server = app.listen(3000, () => {
console.log('Example app listening on port 3000!');
});
let count = 0;
server.on("connection", (socket) => {
console.log(`GOT CONNECTION ${count++} on addr: ${JSON.stringify(socket.address())}`);
socket.setKeepAlive(true);
});
const interval = 2000;
setInterval(() => {
console.log(`got ${requestsCount} requests in ${interval/1000}s`);
requestsCount = 0;
}, interval);
-- Best regards Patrick Dawson ------------------------------------------------------------------- Unsubscribe: https://cool.haxx.se/list/listinfo/curl-library Etiquette: https://curl.haxx.se/mail/etiquette.htmlReceived on 2017-11-28