¿Por qué el destructor de un futuro es devuelto por el bloqueo `std::async`?


Al intentar responder otra pregunta de Stackoverflow , me di cuenta de que este simple fragmento de C++11 está bloqueando implícitamente el hilo de llamada:

std::async(std::launch::async, run_async_task)

Para mí esto habría parecido la forma canónica de C++11 de lanzar una tarea de forma asíncrona sin preocuparse por el resultado. En su lugar, uno tiene que crear y separar un hilo de manera aparentemente explícita (ver respuesta a la pregunta mencionada) para lograr esto.

Así que aquí está mi pregunta: ¿Hay alguna razón en lo que respecta a a seguridad / corrección que el destructor de un std::future tiene que estar bloqueando? ¿No sería suficiente si se bloquea solo en get y de lo contrario, si no estoy interesado en el valor devuelto o la excepción, es simplemente disparar y olvidar?

Author: Community, 2014-05-04

1 answers

Bloquear destructores de futuros devueltos por std::async y de hilos: Ese es un tema controvertido. La siguiente lista de documentos en orden cronológico refleja algunos de los debates de los miembros del comité:

Aunque hay hubo mucha discusión, no hay cambios planeados para C++14 con respecto al comportamiento de bloqueo de los destructores de std::future y std::thread.

Con respecto a su pregunta, el artículo más interesante es probablemente el segundo de Hans Boehm. Cito algunas partes para responder a su pregunta.

N3679: Async () los futuros destructores deben esperar

[..] Futuros devueltos por async() con async política de lanzamiento esperar en su destructor para el estado compartido asociado para estar listo. Esto evita una situación en la que el hilo asociado continúa ejecutándose, y ya no hay un medio para esperar a que se complete porque el futuro asociado ha sido destruido. Sin esfuerzos heroicos para esperar la finalización, un hilo de "fuga" de este tipo puede continuar ejecutándose más allá de la vida útil de los objetos de los que depende.

[Ejemplo]

Es probable que el resultado final sea un "memory smash"cruzado. Este problema es por supuesto evitado si get() o wait() se llama [.. antes de que sean destruidos. Dificultad [..] es que una excepción inesperada puede hacer que ese código sea omitido. Por lo tanto, generalmente se necesita algún tipo de protector de alcance para garantizar la seguridad. Si el programador olvida agregar el scope guard, parece probable que un atacante pueda generar, por ejemplo, una excepción bad_alloc en un punto oportuno para aprovechar la supervisión, y causar que una pila sea sobrescrita. Puede ser posible también controlar los datos utilizados para sobrescribir la pila, y así obtener control sobre el proceso. Este es un error suficientemente sutil que, en nuestra experiencia, es probable que se pase por alto en el código real.

Actualización: El Informe de viaje de Michael Wong también contiene información interesante sobre los resultados de la reunión de septiembre de 2013:

The View from the C++ Standard meeting September 2013 Part 2 of 2.

Sobre la cuestión que los destructores asíncronos no deben bloquear dedicamos una gran cantidad de discusión sobre ello. [..] La única posición que recibió un apoyo considerable fue [..] dando aviso de que los destructores futuros no se bloquearán, a menos que sean devueltos desde async, convirtiéndolo en la notable excepción. [..] Después de una discusión significativa, la única parte que intentamos llevar fue N3776, un intento de aclarar la posición que ~future y ~shared_future no bloquean excepto posiblemente en presencia de async. Hubo un intento de emitir un desaprobación a lo largo de las líneas de C. Desaprobar async sin reemplazo. Esta propuesta casi fue presentada. Pero [.. murió incluso antes de llegar a la mesa de operaciones.

 27
Author: nosid,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-04 21:31:23