LINUX.ORG.RU

История изменений

Исправление dissident, (текущая версия) :

Под полингом я имел в виду не полинг состояния корутин, а полинг состояния абстракций с которыми они работают.

А если они вовсе не работают ни с какими абстракциями как пример Lua выше а просто делают что-то -> отдают управление по yield -> делают что-то -> отдают управление по yield…

Где в этом примере полинг?

Очевидно, что в такой ситуации корутины используют какую-то lock free структуру для обработки cpu bound задач. В такой ситуации не работал с корутинами, не знаю есть ли в этом случае профит.

Зачем корутинам (стэковым или безстэковым) lock-free структуры, если они выполняются в одном трэде?

В точках создания корутин компилятор должен просто сгенерировать (или программист написать, если корутины реализованы в виде внешней библиотеки) код, который создаст структуру/ucontext для корутины.

В точках yield()/resume() компилятор должен просто сгенерировать (или программист написать) код, который вызовет что-нибудь вроде swapcontext().

AFAIU так как трэд один, никакой полинг/lock-free структуры/мутексы не нужны.

PS я имею ввиду здесь корутины, которые просто делают yield()/resume() без всяких async операций вроде socket::read() «под ними». Т.е., например, тут трэд один и никакой полинг не нужен:

coroutine_type coroutine()
{
    coroutine_type cor_type;

    int a, b;
    while (...)
    {
        do_smth_a_little_bit();
        // здесь будет сгенерирован (написан) код а-ля
        //
        // longjmp(LABEL ## cor_type.resume_label);
        yield();
    }
}

// здесь будет сгенерирован (написан) код а-ля
//
// struct courotine_type {
//     int a, b
//     ucontext context;
//     program_counter pc;
//     label resume_label; <- это совсем псевдокод, я не очень силен в asm
//     ...
// }
coroutine_type cor1 = coroutine();
coroutine_type cor2 = coroutine();

while (!finished)
{
   // здесь будет сгенерирован (написан) код а-ля
   //
   // swapcontext(cor1);
   // run_starting_from(cor1.pc);
   // LABEL1:
   cor1.resume();

   // здесь будет сгенерирован (написан) код а-ля
   //
   // swapcontext(cor2);
   // run_starting_from(cor2.pc);
   // LABEL2:
   cor2.resume();
}

Выше совсем бредоватый псевдокод, но IMHO смысл какой-то такой.

Если же корутина сама вызывает какую-то async операцию, то там может быть и полинг и мутексы и все что угодно, но это уже не связано с корутиной, это часть этой async операции, как в примере вроде:

coroutine_type coroutine()
{
    data d = await read_from_tcp(); // внутри read_from_tcp() может быть что угодно
    return d;
}

Исправление dissident, :

Под полингом я имел в виду не полинг состояния корутин, а полинг состояния абстракций с которыми они работают.

А если они вовсе не работают ни с какими абстракциями как пример Lua выше а просто делают что-то -> отдают управление по yield -> делают что-то -> отдают управление по yield…

Где в этом примере полинг?

Очевидно, что в такой ситуации корутины используют какую-то lock free структуру для обработки cpu bound задач. В такой ситуации не работал с корутинами, не знаю есть ли в этом случае профит.

Зачем корутинам (стэковым или безстэковым) lock-free структуры, если они выполняются в одном трэде?

В точках создания корутин компилятор должен просто сгенерировать (или программист написать, если корутины реализованы в виде внешней библиотеки) код, который создаст структуру/ucontext для корутины.

В точках yield()/resume() компилятор должен просто сгенерировать (или программист написать) код, который вызовет что-нибудь вроде swapcontext().

AFAIU так как трэд один, никакой полинг/lock-free структуры/мутексы не нужны.

PS я имею ввиду здесь корутины, которые просто делают yield()/resume() без всяких async операций вроде socket::read() «под ними». Т.е., например, тут трэд один и никакой полинг не нужен:

coroutine_type coroutine()
{
    coroutine_type cor_type;

    int a, b;
    while (...)
    {
        do_smth_a_little_bit();
        // здесь будет сгенерирован (написан) код а-ля
        //
        // longjmp(LABEL ## cor_type.resume_label);
        yield();
    }
}

// здесь будет сгенерирован (написан) код а-ля
//
// struct courotine_type {
//     int a, b
//     ucontext context;
//     program_counter pc;
//     label resume_label; <- это совсем псевдокод, я не очень силен в asm
//     ...
// }
coroutine_type cor1 = coroutine();
coroutine_type cor2 = coroutine();

while (!finished)
{
   // здесь будет сгенерирован (написан) код а-ля
   //
   // swapcontext(cor1);
   // run_starting_from(cor1.pc);
   // LABEL1:
   cor1.resume();

   // здесь будет сгенерирован (написан) код а-ля
   //
   // swapcontext(cor2);
   // run_starting_from(cor2.pc);
   // LABEL2:
   cor2.resume();
}

Выше совсем бредоватый псевдокод, но IMHO смысл какой-то такой.

Если же корутина сама вызывает какую-то async операцию, то там может быть и полинг и мутексы и все что угодно, но это уже не связано с корутиной, это часть этой async операции, как в примере вроде:

coroutine_type coroutine()
{
    data d = await read_from_tcp(); // внутри read_from_tcp() может быть что угодно
    return d;
}

Исправление dissident, :

Под полингом я имел в виду не полинг состояния корутин, а полинг состояния абстракций с которыми они работают.

А если они вовсе не работают ни с какими абстракциями как пример Lua выше а просто делают что-то -> отдают управление по yield -> делают что-то -> отдают управление по yield…

Где в этом примере полинг?

Очевидно, что в такой ситуации корутины используют какую-то lock free структуру для обработки cpu bound задач. В такой ситуации не работал с корутинами, не знаю есть ли в этом случае профит.

Зачем корутинам (стэковым или безстэковым) lock-free структуры, если они выполняются в одном трэде?

В точках создания корутин компилятор должен просто сгенерировать (или программист написать, если корутины реализованы в виде внешней библиотеки) код, который создаст структуру/ucontext для корутины.

В точках yield()/resume() компилятор должен просто сгенерировать (или программист написать) код, который вызовет что-нибудь вроде swapcontext().

AFAIU так как трэд один, никакой полинг/lock-free структуры/мутексы не нужны.

PS я имею ввиду здесь корутины, которые просто делают yield()/resume() без всяких async операций вроде socket::read() «под ними». Т.е., например, тут трэд один и никакой полинг не нужен:

coroutine_type coroutine()
{
    coroutine_type cor_type;

    int a, b;
    while (...)
    {
        do_smth_a_little_bit();
        // здесь будет сгенерирован (написан) код а-ля
        //
        // swapcontext(cor2);
        // longjmp(LABEL ## cor_type.resume_label);
        yield();
    }
}

// здесь будет сгенерирован (написан) код а-ля
//
// struct courotine_type {
//     int a, b
//     ucontext context;
//     program_counter pc;
//     label resume_label; <- это совсем псевдокод, я не очень силен в asm
//     ...
// }
coroutine_type cor1 = coroutine();
coroutine_type cor2 = coroutine();

while (!finished)
{
   // здесь будет сгенерирован (написан) код а-ля
   //
   // swapcontext(cor1);
   // run_starting_from(cor1.pc);
   // LABEL1:
   cor1.resume();

   // здесь будет сгенерирован (написан) код а-ля
   //
   // swapcontext(cor2);
   // run_starting_from(cor2.pc);
   // LABEL2:
   cor2.resume();
}

Выше совсем бредоватый псевдокод, но IMHO смысл какой-то такой.

Если же корутина сама вызывает какую-то async операцию, то там может быть и полинг и мутексы и все что угодно, но это уже не связано с корутиной, это часть этой async операции, как в примере вроде:

coroutine_type coroutine()
{
    data d = await read_from_tcp(); // внутри read_from_tcp() может быть что угодно
    return d;
}

Исправление dissident, :

Под полингом я имел в виду не полинг состояния корутин, а полинг состояния абстракций с которыми они работают.

А если они вовсе не работают ни с какими абстракциями как пример Lua выше а просто делают что-то -> отдают управление по yield -> делают что-то -> отдают управление по yield…

Где в этом примере полинг?

Очевидно, что в такой ситуации корутины используют какую-то lock free структуру для обработки cpu bound задач. В такой ситуации не работал с корутинами, не знаю есть ли в этом случае профит.

Зачем корутинам (стэковым или безстэковым) lock-free структуры, если они выполняются в одном трэде?

В точках создания корутин компилятор должен просто сгенерировать (или программист написать, если корутины реализованы в виде внешней библиотеки) код, который создаст структуру/ucontext для корутины.

В точках yield()/resume() компилятор должен просто сгенерировать (или программист написать) код, который вызовет что-нибудь вроде swapcontext().

AFAIU так как трэд один, никакой полинг/lock-free структуры/мутексы не нужны (я имею ввиду здесь корутины, которые просто делают yield()/resume() без всяких async операций вроде socket::read() «под ними»).

Исправление dissident, :

Очевидно, что в такой ситуации корутины используют какую-то lock free структуру для обработки cpu bound задач. В такой ситуации не работал с корутинами, не знаю есть ли в этом случае профит.

Зачем корутинам (стэковым или безстэековым) lock-free структуры, если они выполняются в одном трэде?

В точках создания корутин компилятор должен просто сгенерировать (или программист написать, если корутины реализованы в виде внешней библиотеки) код, который создаст структуру/ucontext для корутины.

В точках resume()/yield() компилятор должен просто сгенерировать (или программист написать), который вызовет что-нибудь вроде swapcontext().

Трэд один, никакой полинг/lock-free структуры/мутексы не нужны (я имею ввиду здесь корутины, которые просто делают yield()/resume() без всяких async операций вроде socket::read() «под ними»).

Исходная версия dissident, :

Очевидно, что в такой ситуации корутины используют какую-то lock free структуру для обработки cpu bound задач. В такой ситуации не работал с корутинами, не знаю есть ли в этом случае профит.

Зачем корутинам (стэковым или безстэековым) lock-free структуры, если они выполняются в одном трэде?

В точках создания корутин компилятор должен просто сгенерировать (или программист написать) код, который создаст структуру/ucontext для корутины.

В точках resume()/yield() компилятор должен просто сгенерировать (или программист написать), который вызовет что-нибудь вроде swapcontext().

Трэд один, никакой полинг/lock-free структуры/мутексы не нужны (я имею ввиду здесь корутины, которые просто делают yield()/resume() без всяких async операций вроде socket::read() «под ними»).