2010-12-15 15 views
6

Estoy usando libcurl en C++, y llamo a curl_easy_perform en un hilo separado de mi UI usando Boost.Thread.¿Cómo puedo cancelar de inmediato una operación de curvatura?

La interfaz de usuario principal tiene un botón cancelar que me gustaría responder perfectamente (es decir, cuando un usuario hace clic en él, debe reaccionar de inmediato). He leído, escribir, y el progreso devoluciones de llamada configurado para leer una should_cancel variables atómica (como en this pregunta), pero hay dos problemas:

  1. a menudo hay una muy pequeña (pero perceptible) retardo desde que cancelar se presiona para cuando finaliza la operación de curvatura.

  2. Ocasionalmente, hay un retraso muy largo (a veces interminable). En este caso, ya sea:

    a. las devoluciones de llamadas de progreso, lectura y escritura simplemente no se invocan durante mucho tiempo, o

    b. la devolución de llamada de progreso es llamada, devuelvo un valor distinto de cero (lo que significa que debe terminar), pero la operación de curvatura no se completa por un tiempo más (de hecho, la función de progreso se llama nuevamente mientras tanto!)

Así:

  1. ¿Por qué suceden las largas demoras (especialmente sin llamar a la función de progreso)?
  2. ¿Qué debo hacer para permitir que el botón cancelar reaccione correctamente?

Una posibilidad es decirle a la IU que la operación de cancelación se realizó correctamente, pero siga ejecutando el hilo de curvatura en el fondo hasta que se cancele. El problema con esto (creo) es que obliga a que la variable should_cancel sea global, en lugar de restringirla al cuadro de diálogo donde comenzó la operación.

+0

Cómo sobre el uso del curl_multi_perform no bloqueante http: –

Respuesta

3

Su idea básica es correcta. Debe separar la operación de curl de la IU. Sin embargo, la implementación debería cambiarse un poco. No deberías estar usando un should_cancel global. En su lugar, debe tener un puntero global current_request, a un objeto de tipo Request. Este tipo debe tener un indicador interno cancel y una función pública Cancel(). En respuesta al botón de cancelación, llama al Cancel en el current_request y luego lo anula. Una solicitud cancelada es entonces responsable de su propia limpieza en un momento posterior (es un hilo, después de todo).

Tendrá que tener cuidado con sus mutexes para evitar objetos zombis. Existe una condición de carrera inherente entre una cancelación y una finalización de la solicitud.

1

La demora puede ocurrir porque curl_easy_perform está ocupado con la escritura o lectura de devolución de llamada, intente devolver 0 desde la devolución de llamada de escritura o CURL_READFUNC_ABORT desde la devolución de llamada leída. De acuerdo con la documentación, si el valor devuelto por la devolución de llamada por escritura difiere del valor recibido por la función, la transferencia se interrumpirá, y si la devolución de llamada leída retorna a CURL_READFUNC_ABORT, la transferencia también se cancelará (válido desde la versión 7.12.1 del biblioteca).

+0

//curl.haxx.se/libcurl/c/curl_multi_perform.html soy! Estoy pasando el código de retorno "cancelar" a las tres devoluciones de llamada (lectura, escritura y progreso), pero no se llama a ninguna de las devoluciones de llamada en el primer caso de "larga demora". –

9

Tuve el mismo problema con curl 7.21.6. Cuando se intenta abortar el protocolo smtp. Devolver CURL_READFUNC_ABORT desde la devolución de llamada leída detiene la transferencia, pero curl_easy_perform no vuelve en los próximos 5 minutos. Probablemente espera el tiempo de espera de tcp.

Para recorrerlo almaceno el zócalo usado por curl (reemplace curl_opensocket_callback), y cierre este zócalo directamente cuando sea necesario.

curl_socket_t storeCurlSocket(SOCKET *data, curlsocktype purpose, struct curl_sockaddr *addr) { 
    SOCKET sock = socket(addr->family, addr->socktype, addr->protocol); 
    *data = sock; 
    return sock; 
} 

size_t abort_payload(void *ptr, size_t size, size_t nmemb, SOCKET *curl_socket) { 
    SOCKET l_socket = INVALID_SOCKET; 
    swap(l_socket, *curl_socket); 
    if (l_socket != INVALID_SOCKET) { 
     shutdown(l_socket, SD_BOTH); 
     closesocket(l_socket); 
    } 
    return CURL_READFUNC_ABORT; 
} 

...calling perform... 
     SOCKET curlSocket; 
     curet = curl_easy_setopt(curl, CURLOPT_OPENSOCKETFUNCTION, storeCurlSocket); 
     curet = curl_easy_setopt(curl, CURLOPT_OPENSOCKETDATA, &curlSocket); 
     curet = curl_easy_setopt(curl, CURLOPT_READFUNCTION, abort_payload); 
     curet = curl_easy_setopt(curl, CURLOPT_READDATA, &curlSocket); 
     curet = curl_easy_perform(curl); 
     if (curet == CURLE_ABORTED_BY_CALLBACK) { 
      //expected abort 
     } 
... 
Cuestiones relacionadas