Is this function perfectly thread-safe?
EDIT3: It spawns in a new thread each time it is needed, "input" is a copy of a char* which is freed inside it. Assume cURL functions are thread safe.
EDIT4: Assume any non-visible functions are thread safe.
static void *Com_GoogleTranslate(void* input) {
CURL *easy_handle;
char *pos1, *pos2, url[1024], final[1024], inlang[8], outlang[8], *encoded;
const int const_strlen = strlen("\"translatedText\":\"");
struct GoogleMem chunk;
pthread_mutex_lock( &GoogleMessage_mutex );
// 'auto' is really empty in google API:
if (!strcmp(clu.translateIn->string, "auto"))
strcpy(inlang, "");
else
strcpy(inlang, clu.translateIn->string);
if (!strcmp(clu.translateOut->string, "auto"))
strcpy(outlang, "");
else
strcpy(outlang, clu.translateOut->string);
pthread_mutex_unlock( &GoogleMessage_mutex );
// Build the URL
url[0] = '\0';
strcat(url, "http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q=");
// Encode input into URL formatting
encoded = url_encode((char*)input);
if (!encoded) return 0;
strcat(url, encoded);
strcat(url, "&langpair=");
strcat(url, inlang);
strcat(url, "|");
strcat(url, outlang);
chunk.memory = malloc(1); // realloc grows it at Com_GoogleTranslateMem()
if (!chunk.memory) return 0;
chunk.size = 0; // no data yet
// cURL initialization for this sub-session:
easy_handle = qcurl_easy_init();
// ioq3-urt: was needed on https:// (v2 API) attempts 开发者_如何学运维when using GnuTLS
//qcurl_easy_setopt(curl_handle, CURLOPT_SSLVERSION, 3);
// set URL:
qcurl_easy_setopt(easy_handle, CURLOPT_URL, url);
// ioq3-urt: required for multithreading according to cURL doc.
qcurl_easy_setopt(easy_handle, CURLOPT_NOSIGNAL, 1);
// ioq3-urt: skip peer verification; required for google translate when SSL was used
//qcurl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L);
// send all data to this function
qcurl_easy_setopt(easy_handle, CURLOPT_WRITEFUNCTION, Com_GoogleTranslateMem);
// we pass our 'chunk' struct to the callback function:
qcurl_easy_setopt(easy_handle, CURLOPT_WRITEDATA, (void *)&chunk);
// some servers don't like requests that are made without a user-agent field, so we provide one:
qcurl_easy_setopt(easy_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0");
// ioq3-urt: Required by Google Translate terms:
qcurl_easy_setopt(easy_handle, CURLOPT_REFERER, "ioq3-urt");
// fetch it
qcurl_easy_perform(easy_handle);
// cleanup curl stuff
qcurl_easy_cleanup(easy_handle);
/*
Now chunk.memory points to a memory block that is chunk.size
bytes big and contains the remote file.
Nothing has yet deallocated that data, hence free() is used at the end.
*/
if (!chunk.size) {
pthread_mutex_lock( &GoogleMessage_mutex );
sprintf(GoogleMessage.message, "Translation: no data received from Google\n");
GoogleMessage.new_message = qtrue;
pthread_mutex_unlock( &GoogleMessage_mutex );
// Free up memory (same with the end)
if(chunk.memory) free(chunk.memory);
free(encoded);
free(input);
return 0;
}
if ( ( pos1 = strstr(chunk.memory, "\"translatedText\":\"") ) ) { // Make sure we use a valid file:
pos2 = strstr(pos1 + const_strlen, "\""); // position translated text ends
// Build the translated text:
final[0] = '\0';
strncat(final, pos1 + const_strlen, strlen(pos1) - ( strlen(pos2) + const_strlen ) );
// Final printing of the translated text:
pthread_mutex_lock( &GoogleMessage_mutex );
sprintf(GoogleMessage.message, "^2Translated^7: ^3%s\n", final);
GoogleMessage.new_message = qtrue;
pthread_mutex_unlock( &GoogleMessage_mutex );
#ifdef BUILD_FREETYPE
TTF_Find_Slot(final, clu.TTF_MessageMaxTime->integer);
#endif
} else {
pthread_mutex_lock( &GoogleMessage_mutex );
sprintf(GoogleMessage.message, "Translation: no valid translation file received from Google\n");
GoogleMessage.new_message = qtrue;
pthread_mutex_unlock( &GoogleMessage_mutex );
}
// Free allocated memory
if(chunk.memory) free(chunk.memory);
free(encoded);
free(input);
return 0;
}
I'm getting unstable behavior on certain system only and while I suspect its questionable hardware (Intel OpenGL?), I'd like to find out if I miss something.
EDIT: Assume cURL is thread safe in itself.
EDIT2: "input" is a new copy that is freed into this thread.
It looks thread safe to me as long as all the functions it calls outside of the mutex protected sections are themselves thread safe.
There is, however, a question over the thread safety of malloc. See this question.
This is not a thread-safety issue, but there is one thing that stands out as a potential problem. The following line of code has no check for a successful search:
pos2 = strstr(pos1 + const_strlen, "\""); // position translated text ends
If the chunk.memory
value did not contain another double quote following the "translatedText":"
value, then it would be null and the subsequent strlen()
on that value inside the strncat
would likely cause an access violation.
At a minimum, it might not hurt to add an assert
there to verify that it is indeed always succeeding. But I don't know what the other functions are doing. If they guarantee the double quote is in the string, then this is a non-issue.
Also, from a "I don't know anything about the data involved" issue is the blind assumptions about string lengths all fitting in the 1024 buffers. From a defensive coding standpoint, additional checks on those calls might not hurt.
Otherwise, though, I agree (+1) with JeremyP that it looks thread safe based on the given code.
精彩评论