Domanda

Dubito che possa essere fatto in modo portabile, ma ci sono soluzioni là fuori? Penso che potrebbe essere fatto creando uno stack alternativo e reimpostando SP, BP e IP all'ingresso della funzione e avendo la resa salva IP e ripristina SP + BP. I distruttori e la sicurezza delle eccezioni sembrano difficili ma risolvibili.

È stato fatto? È impossibile?

È stato utile?

Soluzione

può essere eseguito senza problemi. È sufficiente un piccolo codice assembly per spostare lo stack di chiamate in uno stack appena allocato nell'heap.

Vorrei consultare boost :: coroutine libreria .

L'unica cosa a cui dovresti fare attenzione è un overflow dello stack. Nella maggior parte dei sistemi operativi che traboccano lo stack causerà un segfault perché la pagina di memoria virtuale non è mappata. Tuttavia, se si alloca lo stack sull'heap, non si ottiene alcuna garanzia. Tienilo a mente.

Altri suggerimenti

Su POSIX, è possibile utilizzare le routine makecontext () / swapcontext () per cambiare in modo portabile i contesti di esecuzione. Su Windows, puoi usare l'API in fibra. Altrimenti, tutto ciò che serve è un po 'di codice di assemblaggio colla che cambia il contesto della macchina. Ho implementato coroutine sia con ASM (per AMD64) che con swapcontext (); nessuno dei due è molto difficile.

Per i posteri,

Il sito web meraviglioso di Dmitry Vyukov ha un trucco intelligente usando ucontext e setjump a coroutine simulate in c ++.

Inoltre, la libreria di contesto di Oliver Kowalke è stata recentemente accettata in Potenzia, quindi speriamo di vedere presto una versione aggiornata di boost.coroutine che funzioni su x86_64.

Non esiste un modo semplice per implementare la coroutine. Perché lo stesso coroutine è fuori dall'astrazione dello stack di C / C ++ proprio come thread. Quindi non può essere supportato senza modifiche a livello di lingua per supportare.

Attualmente (C ++ 11), tutte le implementazioni coroutine C ++ esistenti sono tutte basate su hacking a livello di assembly che è difficile essere sicuri e affidabili attraverso le piattaforme. Per essere affidabile, deve essere standard e gestito dai compilatori anziché dagli hacker.

Esiste una proposta standard - N3708 per questo. Dai un'occhiata se sei interessato.

Se possibile, potresti stare meglio con un iteratore che con un coroutine. In questo modo puoi continuare a chiamare next () per ottenere il valore successivo, ma puoi mantenere il tuo stato come variabili membro anziché variabili locali.

Potrebbe rendere le cose più gestibili. Un altro sviluppatore di C ++ potrebbe non comprendere immediatamente il coroutine mentre potrebbe avere più familiarità con un iteratore.

Per coloro che vogliono sapere come possono sfruttare le Coroutine in modo portatile in C ++ y?o?u? ?w?i?l?l? ?h?a?v?e? ?t?o? ?w?a?i?t? ?f?o?r? ?C? + ? + ?1?7? l'attesa è finita! Il comitato per gli standard sta lavorando alla funzione, vedi N3722 paper . Per riassumere l'attuale bozza del documento, anziché Async e Await, le parole chiave saranno ripristinabili e attenderanno.

Dai un'occhiata all'implementazione sperimentale in Visual Studio 2015 per giocare con l'implementazione sperimentale di Microsoft. Non sembra che Clang abbia ancora un'implementazione.

C'è un buon discorso da parte di Cppcon Coroutine un'astrazione ambientale negativa delinea i vantaggi dell'utilizzo di Coroutine in C ++ e come influenza la semplicità e le prestazioni del codice.

Attualmente dobbiamo ancora usare le implementazioni delle librerie, ma nel prossimo futuro avremo le coroutine come funzionalità C ++ di base.

Aggiornamento: Sembra che l'implementazione della coroutine sia prevista per C ++ 20, ma è stata rilasciata come specifica tecnica con C ++ 17 ( p0057r2 ). Visual C ++, clang e gcc ti consentono di optare per l'utilizzo di un flag di compilazione.

COROUTINE una libreria C ++ portatile per il sequenziamento coroutine ti indica nella giusta direzione? Sembra una soluzione elegante che è durata la prova del tempo ..... ha 9 anni!

Nella cartella DOC è presente un pdf dell'articolo A Portable C ++ Library for Coroutine Sequencing di Keld Helsgaun che descrive la libreria e fornisce brevi esempi che la utilizzano.

[aggiorna] In realtà lo sto usando con successo. La curiosità ha avuto la meglio su di me, quindi ho esaminato questa soluzione e ho scoperto che si adattava bene a un problema su cui sto lavorando da un po 'di tempo!

Non credo che ci siano molte implementazioni complete e pulite in C ++. Un tentativo che mi piace è la biblioteca protothread di Adam Dunkels .

Vedi anche Protothreads: semplificazione della programmazione basata su eventi di sistemi incorporati con memoria limitata nella Biblioteca digitale ACM e discussione nell'argomento Wikipedia Protothread ,

Una nuova libreria, Boost .Context , è stato rilasciato oggi con funzionalità portatili per l'implementazione di coroutine.

Questo è un vecchio thread, ma vorrei suggerire un hack utilizzando il dispositivo di Duff che non dipende dal sistema operativo (per quanto mi ricordo):

Coroutine C usando il dispositivo di Duff

E come esempio, ecco una libreria telnet che ho modificato per usare coroutine invece di fork / thread: Libreria cli di Telnet che utilizza coroutine

E poiché lo standard C precedente a C99 è essenzialmente un sottoinsieme vero di C ++, questo funziona bene anche in C ++.

Si basa su macro (cringe), ma il seguente sito fornisce un'implementazione del generatore di facile utilizzo: http://www.codeproject.com/KB/cpp/cpp_generators.aspx

Ho trovato un'implementazione senza codice asm . L'idea è di utilizzare la funzione di creazione del thread del sistema per inizializzare lo stack e il contesto e utilizzare setjmp / longjmp per cambiare contesto. Ma non è portatile, vedi la versione complicata di pthread se sei interessato.

https://github.com/tonbit/coroutine è C ++ 11 singolo .h implementazione asimmetrica della coroutine a supporto del riassunto / rendimento / attesa primitivi e modello di canale. Si sta implementando tramite ucontext / fibre, non dipende da boost, in esecuzione su Linux / Windows / MacOS. È un buon punto di partenza per imparare a implementare la coroutine in c ++.

Dai un'occhiata alla mia implementazione, illustra il punto di hacking asm ed è semplice:

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

Dovresti sempre prendere in considerazione l'utilizzo dei thread; soprattutto nell'hardware moderno. Se hai lavori che possono essere logicamente separati in Co-routine, l'uso dei thread significa che il lavoro potrebbe effettivamente essere fatto contemporaneamente, da unità di esecuzione separate (core del processore).

Ma forse vuoi usare le coroutine, forse perché hai un algoritmo ben collaudato che è già stato scritto e testato in quel modo, o perché stai eseguendo il porting del codice scritto in quel modo.

Se lavori in Windows, dai un'occhiata a fibre . Fibre ti fornirà un framework simile a una routine con il supporto del sistema operativo.

Non ho familiarità con altri sistemi operativi per raccomandare alternative lì.

WvCont fa parte di WvStreams che implementa i cosiddetti semi-coroutine . Questi sono un po 'più facili da gestire rispetto alle coroutine complete: tu ci chiami e cede alla persona che l'ha chiamato.

È implementato usando il più flessibile WvTask, che supporta coroutine full-on; puoi trovarlo nella stessa libreria.

Funziona su win32 e Linux, almeno, e probabilmente su qualsiasi altro sistema Unix.

Ho provato a implementare le coroutine usando C ++ 11 e thread:

#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();
    }
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top