Pregunta

Dudo que se pueda hacer de forma portátil, pero ¿existe alguna solución?Creo que podría hacerse creando una pila alternativa y restableciendo SP, BP e IP al ingresar la función, y guardando IP y restaurando SP + BP.Los destructores y la seguridad excepcional parecen complicados pero tienen solución.

¿Se ha hecho?¿Es imposible?

¿Fue útil?

Solución

sí eso puede hacerse sin problema.Todo lo que necesita es un pequeño código ensamblador para mover la pila de llamadas a una pila recién asignada en el montón.

me gustaría mira el impulso::corrutina biblioteca.

Lo único que debes tener en cuenta es el desbordamiento de la pila.En la mayoría de los sistemas operativos, el desbordamiento de la pila provocará un error de segmentación porque la página de memoria virtual no está asignada.Sin embargo, si asigna la pila en el montón, no obtiene ninguna garantía.Solo ten eso en cuenta.

Otros consejos

En POSIX, puede utilizar las rutinas makecontext()/swapcontext() para cambiar de forma portátil los contextos de ejecución.En Windows, puede utilizar la API de fibra.De lo contrario, todo lo que necesita es un poco de código ensamblador adhesivo que cambie el contexto de la máquina.He implementado corrutinas tanto con ASM (para AMD64) como con swapcontext();ninguno es muy difícil.

Para la posteridad,

Dmitri Vyukov maravilloso sitio web tiene un truco inteligente usando ucontext y setjump para corrutinas simuladas en c++.

Además, la biblioteca de contexto de Oliver Kowalke fue recientemente aceptado en Boost, por lo que esperamos ver pronto una versión actualizada de boost.coroutine que funcione en x86_64.

No existe una manera fácil de implementar la corrutina.Porque la corrutina en sí está fuera de la abstracción de la pila de C/C++ al igual que el hilo.Por lo tanto, no se puede admitir sin cambios en el nivel de idioma.

Actualmente (C++ 11), todas las implementaciones de rutinas de C++ existentes se basan en piratería a nivel de ensamblado, lo cual es difícil de ser seguro y confiable al cruzar plataformas.Para ser confiable, debe ser estándar y manejado por compiladores en lugar de piratería.

Hay una propuesta estándar - N3708 para esto.Compruébalo si estás interesado.

Si es posible, podría ser mejor con un iterador que con una corrutina.Así podrás seguir llamando next() para obtener el siguiente valor, pero puede mantener su estado como variables miembro en lugar de variables locales.

Podría hacer que las cosas sean más fáciles de mantener.Es posible que otro desarrollador de C++ no comprenda inmediatamente la rutina, mientras que podría estar más familiarizado con un iterador.

Para aquellos que quieran saber cómo pueden aprovechar Coroutines de forma portátil en C++ y̶o̶u̶ ̶w̶i̶l̶l̶ ̶h̶a̶v̶e̶ ̶t̶o̶ ̶w̶a̶i̶t̶ ̶f̶o̶r̶ ̶C̶+̶+̶1̶7̶, ¡la espera ha terminado (ver más abajo)!El comité de estándares está trabajando en la característica ver el papel N3722.Para resumir el borrador actual del documento, en lugar de Async y Await, las palabras clave serán reanudables y await.

Eche un vistazo a la implementación experimental en Visual Studio 2015 para jugar con la implementación experimental de Microsoft.No parece que clang tenga una implementación todavía.

Hay una buena charla del Cppcon Corrutinas una abstracción general negativa. Describa los beneficios de usar Coroutines en C++ y cómo afecta la simplicidad y el rendimiento del código.

En la actualidad todavía tenemos que usar implementaciones de bibliotecas, pero en un futuro cercano tendremos corrutinas como una característica central de C++.

Actualizar:Parece que la implementación de la rutina está programada para C++ 20, pero se lanzó como una especificación técnica con C++ 17 (p0057r2).Visual C++, clang y gcc le permiten optar por utilizar un indicador de tiempo de compilación.

Hace COROUTINE una biblioteca portátil de C++ para secuenciación de corrutinas ¿Te indica la dirección correcta?Parece una solución elegante que ha resistido el paso del tiempo... ¡tiene 9 años!

En la carpeta DOC hay un pdf del artículo A Portable C++ Library for Coroutine Sequencing de Keld Helsgaun que describe la biblioteca y proporciona breves ejemplos de su uso.

[actualización] De hecho, yo mismo lo estoy utilizando con éxito.La curiosidad se apoderó de mí, así que investigué esta solución y descubrí que encajaba bien con un problema en el que he estado trabajando durante algún tiempo.

No creo que haya muchas implementaciones limpias y completas en C++.Un intento que me gusta es Biblioteca de protohilos de Adam Dunkels.

Ver también Protohilos:Simplificación de la programación basada en eventos de sistemas integrados con memoria limitada. en la Biblioteca Digital ACM y tema de discusión en Wikipedia Protohilo,

Una nueva biblioteca, Impulso.Contexto, se lanzó hoy con funciones portátiles para implementar corrutinas.

Este es un hilo antiguo, pero me gustaría sugerir un truco usando el dispositivo de Duff que no depende del sistema operativo (que yo recuerde):

Corrutinas C usando el dispositivo de Duff

Y como ejemplo, aquí hay una biblioteca de telnet que modifiqué para usar corrutinas en lugar de fork/threads:Biblioteca Telnet CLI usando corrutinas

Y dado que el C estándar anterior a C99 es esencialmente un verdadero subconjunto de C++, esto también funciona bien en C++.

Se basa en macros (vergonzosas), pero el siguiente sitio proporciona una implementación de generador fácil de usar: http://www.codeproject.com/KB/cpp/cpp_generators.aspx

Se me ocurrió una implementación sin conjunto código.La idea es utilizar la función de creación de subprocesos del sistema para inicializar la pila y el contexto, y utilizar setjmp/longjmp para cambiar de contexto.Pero no es portátil, consulte la versión complicada de pthread si estás interesado.

https://github.com/tonbit/coroutine es una implementación de rutina asimétrica única de C++11 que admite primitivas de reanudación/rendimiento/espera y el modelo de canal.Se implementa a través de ucontext/fibre, no depende de boost y se ejecuta en linux/windows/macOS.Es un buen punto de partida para aprender a implementar corrutinas en C++.

Mira mi implementación, ilustra el punto de pirateo de ASM y es simple:

https://github.com/user1095108/generic/blob/master/coroutine.hpp

Siempre deberías considerar usar hilos en su lugar;especialmente en hardware moderno.Si tiene trabajo que se puede separar lógicamente en co-rutinas, usar subprocesos significa que el trabajo en realidad podría realizarse al mismo tiempo, mediante unidades de ejecución separadas (núcleos de procesador).

Pero tal vez desee utilizar corrutinas, tal vez porque tiene un algoritmo bien probado que ya ha sido escrito y probado de esa manera, o porque está transfiriendo código escrito de esa manera.

Si trabaja en Windows, debería echar un vistazo a fibras.Fibers le brindará un marco similar a una rutina con soporte del sistema operativo.

No estoy familiarizado con otros sistemas operativos para recomendar alternativas.

WvCont. es una parte de WvStreams que implementa las llamadas semi-corrutinas.Son un poco más fáciles de manejar que las corrutinas completas:lo llamas y regresa a la persona que lo llamó.

Se implementa utilizando WvTask, más flexible, que admite rutinas completas;Puedes encontrarlo en la misma biblioteca.

Funciona al menos en win32 y Linux, y probablemente en cualquier otro sistema Unix.

Intenté implementar corrutinas yo mismo usando C++ 11 e hilos:

#include <iostream>
#include <thread>

class InterruptedException : public std::exception {
};

class AsyncThread {
public:
    AsyncThread() {
        std::unique_lock<std::mutex> lock(mutex);
        thread.reset(new std::thread(std::bind(&AsyncThread::run, this)));
        conditionVar.wait(lock); // wait for the thread to start
    }
    ~AsyncThread() {
        {
            std::lock_guard<std::mutex> _(mutex);
            quit = true;
        }
        conditionVar.notify_all();
        thread->join();
    }
    void run() {
        try {
            yield();
            for (int i = 0; i < 7; ++i) {
                std::cout << i << std::endl;
                yield();
            }
        } catch (InterruptedException& e) {
            return;
        }
        std::lock_guard<std::mutex> lock(mutex);
        quit = true;
        conditionVar.notify_all();
    }
    void yield() {
        std::unique_lock<std::mutex> lock(mutex);
        conditionVar.notify_all();
        conditionVar.wait(lock);
        if (quit) {
            throw InterruptedException();
        }
    }
    void step() {
        std::unique_lock<std::mutex> lock(mutex);
        if (!quit) {
            conditionVar.notify_all();
            conditionVar.wait(lock);
        }
    }
private:
    std::unique_ptr<std::thread> thread;
    std::condition_variable conditionVar;
    std::mutex mutex;
    bool quit = false;
};

int main() {
    AsyncThread asyncThread;
    for (int i = 0; i < 3; ++i) {
        std::cout << "main: " << i << std::endl;
        asyncThread.step();
    }
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top