Re: Вопрос по корутинам
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 28.08.25 09:59
Оценка:
Здравствуйте, LaptevVV, Вы писали:

LVV>А в каких задачах корутины вот прям супер — супер?

LVV>Чего раньше приходилось делать муторно и долго ?

Вообще изначально это рекурсивный обход деревьев.
То есть после каждого MoveNext мы должны запомнить состояние для следующего MoveNext.
На этом Linq в .Net работает и async/await
и солнце б утром не вставало, когда бы не было меня
Отредактировано 28.08.2025 10:04 Serginio1 . Предыдущая версия .
Re[16]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 09:59
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>Это что за "тут играйте, тут не играйте"?. Не-не-не, давай по-чесноку. Я тебе давал полный текст программы и ты давай тоже полный. Вместо со "вспомогательными макросами" и ссылкой на запускаемую программу.


https://coliru.stacked-crooked.com/a/86f2c863b4d93396

int fn1_loop(fn1_t *my) {
    LOOP_BEGIN(my->loop)
    my->value="Idle"; LOOP_POINT
    my->value="Started"; LOOP_POINT
    my->value="Processing"; LOOP_POINT
    while (*my->param < 10 + my->machineID) {
        my->value="Waiting"; LOOP_POINT
    }
    my->value="Stopped";
    LOOP_END
}
  Скрытый текст
typedef int loop_t;

#define LOOP_RESET(loop) { loop=0; }
#if defined(__COUNTER__) && __COUNTER__!=__COUNTER__
#define LOOP_BEGIN(loop) { enum { __loop_base=__COUNTER__ }; \
    loop_t *__loop=&(loop); __loop_switch: int __loop_rv=1; \
    switch(*__loop) { default: *__loop=0; case 0: {
#define LOOP_POINT { enum { __loop_case=__COUNTER__-__loop_base }; \
    *__loop=__loop_case; goto __loop_leave; case __loop_case:{} }
#else
#define LOOP_BEGIN(loop) {loop_t*__loop=&(loop);__loop_switch:int __loop_rv=1;\
    switch(*__loop){ default: case 0: *__loop=__LINE__; case __LINE__:{
#define LOOP_POINT { *__loop=__LINE__; goto __loop_leave; case __LINE__:{} }
#endif
#define LOOP_END { __loop_end: *__loop=-1; case -1: return 0; \
    { goto __loop_end; goto __loop_switch; } } \
    }} __loop_leave: return __loop_rv; }
#define LOOP_SET_RV(rv) { __loop_rv=(rv); } /* rv must be non zero */
#define LOOP_INT(n) { __loop_rv=(n); LOOP_POINT } /* interrupt n */
/* for manual labeling: enum { L01=1,L02,L03,L04 }; ... LOOP_POINT_(L02) */
#define LOOP_POINT_(name) { *__loop=name; goto __loop_leave; case name:{} }
#define LOOP_INT_(n,name) { __loop_rv=(n); LOOP_POINT_(name) }

//------------------------------------------------------------------------------

enum { sevInit, sevDone };
typedef int (*afn_loop_fn)(void *ctx);
typedef int (*afn_setup_fn)(void *ctx,int sev);
typedef struct {
    void *ctx;
    int (*loop)(void *ctx);
    int (*setup)(void *ctx,int sev);
} afn_t;

//------------------------------------------------------------------------------

typedef struct {
    loop_t loop;
    int machineID, *param;
    const char* value;
} fn1_t;

int fn1_setup(fn1_t* my,int sev) {
    switch(sev) {
        case sevInit: {
            LOOP_RESET(my->loop);
            my->value="initialized";
        } break;
        case sevDone: {
            my->loop=-1;
        } break;
    }
    return 0;
}

int fn1_loop(fn1_t *my) {
    LOOP_BEGIN(my->loop)
    my->value="Idle"; LOOP_POINT
    my->value="Started"; LOOP_POINT
    my->value="Processing"; LOOP_POINT
    while (*my->param < 10 + my->machineID) {
        my->value="Waiting"; LOOP_POINT
    }
    my->value="Stopped";
    LOOP_END
}

//------------------------------------------------------------------------------
typedef void (*cycle_fn)(void *ctx);
typedef struct {
    afn_t *group; int current,count;
    void (*cycle)(void *ctx); void *cycle_ctx;
} fn2_t;

int fn2_setup(fn2_t* my, int sev) {
    int i,err=0;
    for(i=0;i<my->count;i++) {
        afn_t *fn=&my->group[i];
        if (fn->setup(fn->ctx,sev)) err++;
    }
    my->current=0;
    return err;
}

int fn2_loop(fn2_t* my) {
    if (my->count>0) {
        afn_t *fn=&my->group[my->current];
        if (fn->loop(fn->ctx)) my->current++; else {
            my->group[my->current]=my->group[--my->count];
        }
    }
    if (my->current>=my->count) { my->current=0;
        if (my->cycle) my->cycle(my->cycle_ctx);
    }
    return my->count>0 ? 1 : 0;
}

//------------------------------------------------------------------------------
#include <stdio.h>

typedef struct { fn1_t *list; int count, param; } show_ctx_t;

void show(show_ctx_t* my) {
    int i,n;
    n=my->count;
    for(i=0;i<n;i++) {
        fn1_t *f=&my->list[i];
        printf("| ");
        printf("Machine %d: %10s ",f->machineID, f->value);
    }
    printf("|\n");
    my->param++;
}

int main() {
    enum { N=5 }; 
    fn1_t fn[N]; afn_t afn[N];
    show_ctx_t show_ctx[1];
    fn2_t fn2[1];
    int i;

    for(i=0;i<N;i++) {
        fn[i].machineID=i+1;
        fn[i].param=&show_ctx->param;
    }
    show_ctx->param=0;
    show_ctx->list=fn;
    show_ctx->count=N;

    fn2->cycle=(cycle_fn)show;
    fn2->cycle_ctx=show_ctx;
    for(i=0;i<N;i++) {
        afn[i].ctx=&fn[i];
        afn[i].setup=(afn_setup_fn)fn1_setup;
        afn[i].loop=(afn_loop_fn)fn1_loop;
    }
    fn2->group=afn;
    fn2->count=N;

    fn2_setup(fn2,sevInit);
    while(fn2_loop(fn2)) {}
    fn2_setup(fn2,sevDone);

    return 0;
}
| Machine 1:       Idle | Machine 2:       Idle | Machine 3:       Idle | Machine 4:       Idle | Machine 5:       Idle |
| Machine 1:    Started | Machine 2:    Started | Machine 3:    Started | Machine 4:    Started | Machine 5:    Started |
| Machine 1: Processing | Machine 2: Processing | Machine 3: Processing | Machine 4: Processing | Machine 5: Processing |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Waiting | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Stopped | Machine 2:    Waiting | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Stopped | Machine 2:    Stopped | Machine 3:    Waiting | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Stopped | Machine 2:    Stopped | Machine 3:    Stopped | Machine 4:    Waiting | Machine 5:    Waiting |
| Machine 1:    Stopped | Machine 2:    Stopped | Machine 3:    Stopped | Machine 4:    Stopped | Machine 5:    Waiting |
| Machine 1:    Stopped | Machine 2:    Stopped | Machine 3:    Stopped | Machine 4:    Stopped | Machine 5:    Stopped |
Re[15]: Вопрос по корутинам
От: landerhigh Пират  
Дата: 28.08.25 10:06
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Здравствуйте, rg45, Вы писали:


  Скрытый текст
_>
/* loop-fn.h */
_>#ifndef __LOOP_FN_H__
_>#define __LOOP_FN_H__

_>typedef int loop_t;

_>#define LOOP_RESET(loop) { loop=0; }
_>#if defined(__COUNTER__) && __COUNTER__!=__COUNTER__
_>#define LOOP_BEGIN(loop) { enum { __loop_base=__COUNTER__ }; \
_>    loop_t *__loop=&(loop); __loop_switch: int __loop_rv=1; \
_>    switch(*__loop) { default: *__loop=0; case 0: {
_>#define LOOP_POINT { enum { __loop_case=__COUNTER__-__loop_base }; \
_>    *__loop=__loop_case; goto __loop_leave; case __loop_case:{} }
_>#else
_>#define LOOP_BEGIN(loop) {loop_t*__loop=&(loop);__loop_switch:int __loop_rv=1;\
_>    switch(*__loop){ default: case 0: *__loop=__LINE__; case __LINE__:{
_>#define LOOP_POINT { *__loop=__LINE__; goto __loop_leave; case __LINE__:{} }
_>#endif
_>#define LOOP_END { __loop_end: *__loop=-1; case -1: return 0; \
_>    { goto __loop_end; goto __loop_switch; } } \
_>    }} __loop_leave: return __loop_rv; }
_>#define LOOP_SET_RV(rv) { __loop_rv=(rv); } /* rv must be non zero */
_>#define LOOP_INT(n) { __loop_rv=(n); LOOP_POINT } /* interrupt n */
_>/* for manual labeling: enum { L01=1,L02,L03,L04 }; ... LOOP_POINT_(L02) */
_>#define LOOP_POINT_(name) { *__loop=name; goto __loop_leave; case name:{} }
_>#define LOOP_INT_(n,name) { __loop_rv=(n); LOOP_POINT_(name) }

_>#endif /* __LOOP_FN_H__ */


Где-то я это видел

На самом деле С++20 корутины имеют внутри что-то похожее, но на уровне компилятора. Но стандартные корутины также реализуют автоматическое сохранение всех переменных, используемых внутри корутины, что в случае switch..case на стероидах приходится делать вручную.
www.blinnov.com
Re[17]: Вопрос по корутинам
От: landerhigh Пират  
Дата: 28.08.25 10:07
Оценка:
Здравствуйте, so5team, Вы писали:

L>>С высоты своего опыта могу заявить, что "иерархические КА", "вложенные состояния" и прочая дичь есть не более чем словесный онанизм от теоретиков, которые даже не пытались перейти от словесного описания к реализации в коде.


S>Очередной Д`Артаньян в белом пОльто и с членом такой длины, что волочится по асфальту, детектед.


Шляпу забыл.

S>Фу таким быть


Да не, мне норм
www.blinnov.com
Re[17]: Вопрос по корутинам
От: rg45 СССР  
Дата: 28.08.25 10:08
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>https://coliru.stacked-crooked.com/a/86f2c863b4d93396


_>
int fn1_loop(fn1_t *my) {
_>    LOOP_BEGIN(my->loop)
    my->>value="Idle"; LOOP_POINT
    my->>value="Started"; LOOP_POINT
    my->>value="Processing"; LOOP_POINT
_>    while (*my->param < 10 + my->machineID) {
        my->>value="Waiting"; LOOP_POINT
_>    }
    my->>value="Stopped";
_>    LOOP_END
_>}


Жульничаешь? Изюм выковыриваешь? Ну, я помогу тебе сделать этот нелёгкий шаг. Сравни следующие два варианта:

С корутинами:

#include <generator>
#include <iostream>
#include <string>
#include <ranges>
#include <vector>

std::generator<std::string> runMachine(const int& param) {
    co_yield "Idle";
    co_yield "Started";
    co_yield "Processing";
    while (param < 10)
    {
        co_yield "Waiting";
    }
    co_yield "Stopped";
}

std::generator<std::generator<std::string>> createMachines(const int& param, size_t n) {
    for (size_t i = 0; i < n; ++i)
        co_yield runMachine(param);
}

int main() {
    int param = 0;
    std::vector machines(std::from_range, createMachines(param, 333));
    
    for(bool active = true; active;) {
        active = false;
        for (size_t i = 0; i < machines.size(); ++i) {
            if (machines[i].begin() != machines[i].end()) {
                std::cout << "Machine " << i + 1 << ": " << *machines[i].begin() << std::endl;
                active = true;
            }
        }
        ++param;
    }
}


На С со "вспомогательными макросами":

typedef int loop_t;

#define LOOP_RESET(loop) { loop=0; }
#if defined(__COUNTER__) && __COUNTER__!=__COUNTER__
#define LOOP_BEGIN(loop) { enum { __loop_base=__COUNTER__ }; \
    loop_t *__loop=&(loop); __loop_switch: int __loop_rv=1; \
    switch(*__loop) { default: *__loop=0; case 0: {
#define LOOP_POINT { enum { __loop_case=__COUNTER__-__loop_base }; \
    *__loop=__loop_case; goto __loop_leave; case __loop_case:{} }
#else
#define LOOP_BEGIN(loop) {loop_t*__loop=&(loop);__loop_switch:int __loop_rv=1;\
    switch(*__loop){ default: case 0: *__loop=__LINE__; case __LINE__:{
#define LOOP_POINT { *__loop=__LINE__; goto __loop_leave; case __LINE__:{} }
#endif
#define LOOP_END { __loop_end: *__loop=-1; case -1: return 0; \
    { goto __loop_end; goto __loop_switch; } } \
    }} __loop_leave: return __loop_rv; }
#define LOOP_SET_RV(rv) { __loop_rv=(rv); } /* rv must be non zero */
#define LOOP_INT(n) { __loop_rv=(n); LOOP_POINT } /* interrupt n */
/* for manual labeling: enum { L01=1,L02,L03,L04 }; ... LOOP_POINT_(L02) */
#define LOOP_POINT_(name) { *__loop=name; goto __loop_leave; case name:{} }
#define LOOP_INT_(n,name) { __loop_rv=(n); LOOP_POINT_(name) }

//------------------------------------------------------------------------------

enum { sevInit, sevDone };
typedef int (*afn_loop_fn)(void *ctx);
typedef int (*afn_setup_fn)(void *ctx,int sev);
typedef struct {
    void *ctx;
    int (*loop)(void *ctx);
    int (*setup)(void *ctx,int sev);
} afn_t;

//------------------------------------------------------------------------------

typedef struct {
    loop_t loop;
    int machineID, *param;
    const char* value;
} fn1_t;

int fn1_setup(fn1_t* my,int sev) {
    switch(sev) {
        case sevInit: {
            LOOP_RESET(my->loop);
            my->value="initialized";
        } break;
        case sevDone: {
            my->loop=-1;
        } break;
    }
    return 0;
}

int fn1_loop(fn1_t *my) {
    LOOP_BEGIN(my->loop)
    my->value="Idle"; LOOP_POINT
    my->value="Started"; LOOP_POINT
    my->value="Processing"; LOOP_POINT
    while (*my->param < 10 + my->machineID) {
        my->value="Waiting"; LOOP_POINT
    }
    my->value="Stopped";
    LOOP_END
}

//------------------------------------------------------------------------------
typedef void (*cycle_fn)(void *ctx);
typedef struct {
    afn_t *group; int current,count;
    void (*cycle)(void *ctx); void *cycle_ctx;
} fn2_t;

int fn2_setup(fn2_t* my, int sev) {
    int i,err=0;
    for(i=0;i<my->count;i++) {
        afn_t *fn=&my->group[i];
        if (fn->setup(fn->ctx,sev)) err++;
    }
    my->current=0;
    return err;
}

int fn2_loop(fn2_t* my) {
    if (my->count>0) {
        afn_t *fn=&my->group[my->current];
        if (fn->loop(fn->ctx)) my->current++; else {
            my->group[my->current]=my->group[--my->count];
        }
    }
    if (my->current>=my->count) { my->current=0;
        if (my->cycle) my->cycle(my->cycle_ctx);
    }
    return my->count>0 ? 1 : 0;
}

//------------------------------------------------------------------------------
#include <stdio.h>

typedef struct { fn1_t *list; int count, param; } show_ctx_t;

void show(show_ctx_t* my) {
    int i,n;
    n=my->count;
    for(i=0;i<n;i++) {
        fn1_t *f=&my->list[i];
        printf("| ");
        printf("Machine %d: %10s ",f->machineID, f->value);
    }
    printf("|\n");
    my->param++;
}

int main() {
    enum { N=5 }; 
    fn1_t fn[N]; afn_t afn[N];
    show_ctx_t show_ctx[1];
    fn2_t fn2[1];
    int i;

    for(i=0;i<N;i++) {
        fn[i].machineID=i+1;
        fn[i].param=&show_ctx->param;
    }
    show_ctx->param=0;
    show_ctx->list=fn;
    show_ctx->count=N;

    fn2->cycle=(cycle_fn)show;
    fn2->cycle_ctx=show_ctx;
    for(i=0;i<N;i++) {
        afn[i].ctx=&fn[i];
        afn[i].setup=(afn_setup_fn)fn1_setup;
        afn[i].loop=(afn_loop_fn)fn1_loop;
    }
    fn2->group=afn;
    fn2->count=N;

    fn2_setup(fn2,sevInit);
    while(fn2_loop(fn2)) {}
    fn2_setup(fn2,sevDone);

    return 0;
}


И что, тебе реально твой вариант больше нравится? Лично я бы не выбрал этот вариант из-за одних только "вспомогательных макросов". Ну и вообще...
--
Справедливость выше закона. А человечность выше справедливости.
Отредактировано 28.08.2025 10:10 rg45 . Предыдущая версия .
Re[18]: Вопрос по корутинам
От: so5team https://stiffstream.com
Дата: 28.08.25 10:11
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Да не, мне норм


В то, что вам норм даже и сомнений нет.
Re[18]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:14
Оценка:
Здравствуйте, rg45, Вы писали:

R>Здравствуйте, kov_serg, Вы писали:


R>Жульничаешь? Изюм выковыриваешь? Ну, я помогу тебе сделать этот нелёгкий шаг. Сравни следующие два варианта:

Нет. Вся обвеска делается вспомогательными функциями. Макросы нужны чтобы упростить написание.
Более того он позволяет единообразно производить декомпозицию асинхронных операций.
Кроме того он не запрещает использовать и корутины если они поддерживаются компилятором.

R>И что, тебе реально твой вариант больше нравится? Лично я бы не выбрал этот вариант из-за одних только "вспомогательных макросов". Ну и вообще...

Да. Т.к. он значительно проще, понятнее, всё под полным контролем. Никаких скрытых или недоступных состояний.
Я могу сохранять состояние на диск или передать по сети и потом возобновлять на другой машине.
И он работает даже на микроконтроллерах с десятками байт RAM где есть тольк C компилятор.
Re[18]: Вопрос по корутинам
От: landerhigh Пират  
Дата: 28.08.25 10:15
Оценка:
Здравствуйте, rg45, Вы писали:

R>И что, тебе реально твой вариант больше нравится? Лично я бы не выбрал этот вариант из-за одних только "вспомогательных макросов". Ну и вообще...


Stackless корутины из asio прячут все эти "вспомогательные макросы".
Но нужно быть очень осторожным в коде таких "корутин" — неосмотрительное применение switch..case и все разваливается с кучей спецэффектов.
Также, все переменные контекста корутины нужно где-то вручную сохранять. Боль моя дырка задница.
Мы их, впрочем, успешно использовали для реализации развесистой асинхронной логики в отсутствие 20-х корутин. Грубо говоря, пришлось применить именно их, т.к. стековые корутины не подошли из-за оверхеда на переключение и памяти для стеков.
www.blinnov.com
Re[19]: Вопрос по корутинам
От: rg45 СССР  
Дата: 28.08.25 10:15
Оценка:
Здравствуйте, kov_serg, Вы писали:

R>>Жульничаешь? Изюм выковыриваешь? Ну, я помогу тебе сделать этот нелёгкий шаг. Сравни следующие два варианта:

_>Нет. Вся обвеска делается вспомогательными функциями. Макросы нужны чтобы упростить написание.
_>Более того он позволяет единообразно производить декомпозицию асинхронных операций.
_>Кроме того он не запрещает использовать и корутины если они поддерживаются компилятором.

R>>И что, тебе реально твой вариант больше нравится? Лично я бы не выбрал этот вариант из-за одних только "вспомогательных макросов". Ну и вообще...

_>Да. Т.к. он значительно проще, понятнее, всё под полным контролем. Никаких скрытых или недоступных состояний.
_>Я могу сохранять состояние на диск или передать по сети и потом возобновлять на другой машине.
_>И он работает даже на микроконтроллерах с десятками байт RAM где есть тольк C компилятор.

Ну, мне остаётся только пожелать тебе творческих успехов.
--
Справедливость выше закона. А человечность выше справедливости.
Re[16]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:17
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Где-то я это видел


L>На самом деле С++20 корутины имеют внутри что-то похожее, но на уровне компилятора. Но стандартные корутины также реализуют автоматическое сохранение всех переменных, используемых внутри корутины, что в случае switch..case на стероидах приходится делать вручную.

Зато есть полный контроль над происходящим. Более того основная засада не в корутинах, а в их диспечеризации.
Re[19]: Вопрос по корутинам
От: rg45 СССР  
Дата: 28.08.25 10:19
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Stackless корутины из asio прячут все эти "вспомогательные макросы".

L>Но нужно быть очень осторожным в коде таких "корутин" — неосмотрительное применение switch..case и все разваливается с кучей спецэффектов.
L>Также, все переменные контекста корутины нужно где-то вручную сохранять. Боль моя дырка задница.
L>Мы их, впрочем, успешно использовали для реализации развесистой асинхронной логики в отсутствие 20-х корутин. Грубо говоря, пришлось применить именно их, т.к. стековые корутины не подошли из-за оверхеда на переключение и памяти для стеков.

Да я в курсе. По этим граблям я тоже хаживал

Более того, где-то в конце 90-х или начале 2000-х я сам мастырил свой собственный механизм stack-full корутин поверх longjmp.
--
Справедливость выше закона. А человечность выше справедливости.
Re[20]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:19
Оценка:
Здравствуйте, rg45, Вы писали:

R>Ну, мне остаётся только пожелать тебе творческих успехов.

Спасибо
Re[20]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:24
Оценка: :)
Здравствуйте, rg45, Вы писали:

R>Более того, где-то в конце 90-х или начале 2000-х я сам мастырил свой собственный механизм stack-full корутин поверх longjmp.


Типа такого:
// ptsk.h : popup multitasking
#ifndef __PTSK_H__
#define __PTSK_H__

#include <setjmp.h>
#include "ticks.h"

#ifdef __cplusplus
extern "C" {
#endif

typedef struct ptsk_tcb_tag {
    void (*task)(void*);
    void *args;
    int   stack_size;
    int  *stack_top;    
    jmp_buf ctx;
    struct ptsk_tcb_tag* next;
    struct ptsk_tcb_tag* prev;
} ptsk_tcb_t;

void ptsk_init(void);
void ptsk_done(void);
void ptsk_idle(void);
void ptsk_addtask(ptsk_tcb_t *task);
void ptsk_deltask(ptsk_tcb_t *task); // if current it'll die
void ptsk_die(void); // kill current task
ptsk_tcb_t* ptsk_getcurrent();

void ptsk_sleep(ticks_t ticks);
void ptsk_sleep_till(ticks_t time);

/*

usage:

#include <stdio.h>
#include "ptsk.h"

void task1(void* arg) {
    for(int i=0;i<=9;++i) {
        printf("task1 \ti=%d\n",i);
        ptsk_idle();
    }
}
void task2(void* arg) {
    for(int i=0;i<=7;++i) {
        printf("task2 \t\ti=%d\n",i);
        ptsk_sleep(500);
    }
}
void task3(void* arg) {
    for(int i=0;i<=5;++i) {
        printf("task3 \t\t\ti=%d\n",i);
        ptsk_sleep(1000);
    }
}

ptsk_tcb_t t1={task1,(void*)1,1024};
ptsk_tcb_t t2={task2,(void*)2,1024};
ptsk_tcb_t t3={task3,(void*)3,1024};

void main() {
    ptsk_init();
    ptsk_addtask(&t1);
    ptsk_addtask(&t2);
    ptsk_addtask(&t3);
    ptsk_idle();
    ptsk_done();
}

*/

#ifdef __cplusplus
}
#endif

#endif // __PTSK_H__
  Скрытый текст
// ptsk.c
#include "ptsk.h"

#ifdef __cplusplus
extern "C" {
#endif

enum {
    PTSK_1ST =0,
    PTSK_WAKE=1,
    PTSK_NEW =2,
    PTSK_RET =3
};

char        ptsk_active=0;
ptsk_tcb_t* ptsk_head=0;
ptsk_tcb_t* ptsk_tail=0;
ptsk_tcb_t* ptsk_curr=0;
jmp_buf     ptsk_last;
jmp_buf     ptsk_main;

void ptsk_init(void) {
    ptsk_active=0;
    ptsk_head=ptsk_tail=ptsk_curr=0;
}
void ptsk_done(void) {
    if (ptsk_active) longjmp(ptsk_main,PTSK_RET);
}
void ptsk_panic() {
    // exit(0);
    for(;;) {}
}
void ptsk_switch(void) {
    ptsk_curr=ptsk_curr->next; 
    if (!ptsk_curr) {
        ptsk_curr=ptsk_head;
        if (!ptsk_curr) ptsk_done();
    }
    longjmp(ptsk_curr->ctx,PTSK_WAKE);            
}
void ptsk_die(void) {
    if (!ptsk_curr) { ptsk_done(); ptsk_panic(); return; } // panic
    if (ptsk_curr->prev) ptsk_curr->prev->next=ptsk_curr->next; else ptsk_head=ptsk_curr->next;
    if (ptsk_curr->next) ptsk_curr->next->prev=ptsk_curr->prev; else ptsk_tail=ptsk_curr->prev;
    ptsk_switch();
}
void ptsk_deltask(ptsk_tcb_t *task) {
    if (ptsk_curr==task) ptsk_die();
    if (task->prev) task->prev->next=task->next; else ptsk_head=task->next;
    if (task->next) task->next->prev=task->prev; else ptsk_tail=task->prev;
}
void ptsk_run(ptsk_tcb_t* task) {
    task->stack_top=(int*)&task;
    task->task(task->args);
    ptsk_die();
}
int ptsk_stackalloc(int arg) {
    volatile int dummy; ptsk_tcb_t* tsk; int sz;
    static int* stk;
    if (arg==1) stk=(int*)&dummy; 
    sz=(int*)&dummy-stk; if (sz<0) sz=-sz;
    if (sz<ptsk_tail->stack_size) ptsk_stackalloc(0);
    tsk=ptsk_tail;
    if (setjmp(ptsk_last)==PTSK_NEW) ptsk_stackalloc(1);
    switch(setjmp(tsk->ctx)) {
        case PTSK_1ST:  longjmp(ptsk_main,PTSK_RET);
        case PTSK_WAKE: ptsk_run(tsk);
    }
    ptsk_die(); return dummy;
}
void ptsk_addtask(ptsk_tcb_t *task) {
    ptsk_tcb_t* last;
    last=ptsk_tail;
    task->prev=ptsk_tail;
    task->next=0;
    if (ptsk_tail) ptsk_tail->next=task; else ptsk_head=task;
    ptsk_tail=task;
    switch(setjmp(ptsk_main)) {
        case PTSK_1ST:  if (last) longjmp(ptsk_last,PTSK_NEW); else ptsk_stackalloc(1);
        //case PTSK_RET: break;
    }
}
void ptsk_idle(void) {
    if (!ptsk_curr) { 
        ptsk_curr=ptsk_head; if (!ptsk_curr) return;
        ptsk_active=1;
        switch(setjmp(ptsk_main)) {
            case PTSK_1ST: ptsk_run(ptsk_curr);
            //case PTSK_RET: break;
        }
        ptsk_active=0;
        return;
    }
    switch(setjmp(ptsk_curr->ctx)) {
        case PTSK_1ST:  ptsk_switch();
        //case PTSK_WAKE: break;
    }
}
ptsk_tcb_t* ptsk_getcurrent() {
    return ptsk_curr;
}
void ptsk_sleep_till(ticks_t time) {
    do {
        ptsk_idle();
    } while(time-getticks()>0);
}
void ptsk_sleep(ticks_t ticks) {
    ptsk_sleep_till(getticks()+ticks);
}

#ifdef __cplusplus
}
#endif

// ticks.h
#ifndef __TICKS_H__
#define __TICKS_H__

typedef long ticks_t;
ticks_t getticks();

#endif // __TICKS_H__
Re[17]: Вопрос по корутинам
От: landerhigh Пират  
Дата: 28.08.25 10:26
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Зато есть полный контроль над происходящим.


Это до тех пор, пока (не "если", а "пока") кто-то в коде такой "корутины вручную" не напишет.
typedef struct {
    int loop;
    int machineID, *param;
    const char* value;
} fn1_t;

void fn1_setup(fn1_t *self) { self->loop=0; }
int fn1_loop(fn1_t *self) {
    LOOP_BEGIN(self->loop)
    self->value="Idle"; LOOP_POINT
    self->value="Started"; LOOP_POINT
    for (unsigned int i = 0; i<10 && !connected(); ++i )
    {
       connect(); LOOP_POINT
    }
    self->value="Processing"; LOOP_POINT
    while (*self->param < 10) {
        self->value="Waiting"; LOOP_POINT
    }
    self->value="Stopped";
    LOOP_END
}


Нет, рано или поздно ошибка найдется, но все равно, happy debugging!

_>Более того основная засада не в корутинах, а в их диспечеризации.


Диспетчерезация обычно определяется собственно уровнем I/O, и как правило сводится к вызову корутины из колбека.
www.blinnov.com
Re[18]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:32
Оценка:
Здравствуйте, landerhigh, Вы писали:

L>Здравствуйте, kov_serg, Вы писали:


_>>Зато есть полный контроль над происходящим.


L>Это до тех пор, пока (не "если", а "пока") кто-то в коде такой "корутины вручную" не напишет.

L>
L>typedef struct {
L>    int loop;
L>    int machineID, *param;
L>    const char* value;
L>} fn1_t;

L>void fn1_setup(fn1_t *self) { self->loop=0; }
L>int fn1_loop(fn1_t *self) {
L>    LOOP_BEGIN(self->loop)
    self->>value="Idle"; LOOP_POINT
    self->>value="Started"; LOOP_POINT
L>    for (unsigned int i = 0; i<10 && !connected(); ++i )
L>    {
L>       connect(); LOOP_POINT
L>    }
    self->>value="Processing"; LOOP_POINT
L>    while (*self->param < 10) {
        self->>value="Waiting"; LOOP_POINT
L>    }
    self->>value="Stopped";
L>    LOOP_END
L>}
L>


L>Нет, рано или поздно ошибка найдется, но все равно, happy debugging!

Тут всё же предполагается некотарая дисциплина, для соблюдения ряда требований. Да и компилятор уматерит. Что инициализация не в своём скоупе.
typedef struct {
    int loop;
    int machineID, *param;
    const char* value;
    unsigned int i;
} fn1_t;

void fn1_setup(fn1_t *self) { self->loop=0; }
int fn1_loop(fn1_t *self) {
    LOOP_BEGIN(self->loop)
    self->value="Idle"; LOOP_POINT
    self->value="Started"; LOOP_POINT
    for ( self->i = 0; self->i<10 && !connected(); ++self->i )
    {
       connect(); LOOP_POINT
    }
    self->value="Processing"; LOOP_POINT
    while (*self->param < 10) {
        self->value="Waiting"; LOOP_POINT
    }
    self->value="Stopped";
    LOOP_END
}


L>Диспетчерезация обычно определяется собственно уровнем I/O, и как правило сводится к вызову корутины из колбека.

Вот тут одна из засад, колбэки не гарантируют их какого потока будут вызваны.
Re[21]: Вопрос по корутинам
От: rg45 СССР  
Дата: 28.08.25 10:36
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Типа такого:


Ну да, тип того. А может и ещё хуже.
--
Справедливость выше закона. А человечность выше справедливости.
Re[21]: Вопрос по корутинам
От: rg45 СССР  
Дата: 28.08.25 10:37
Оценка: :))
Здравствуйте, kov_serg, Вы писали:

R>>Ну, мне остаётся только пожелать тебе творческих успехов.

_>Спасибо

На всякий случай вдогонку:

Правило №16. Избегайте макросов

Мало ли, вдруг надумаешь С++ изучать.
--
Справедливость выше закона. А человечность выше справедливости.
Re[19]: Вопрос по корутинам
От: landerhigh Пират  
Дата: 28.08.25 10:40
Оценка:
Здравствуйте, kov_serg, Вы писали:

_>Тут всё же предполагается некотарая дисциплина, для соблюдения ряда требований.


Требуется в первую очередь понимание, что внутри этой функции много чего нельзя из того, что во всех других местах можно. Когда код уехал другом разработчику, с этим может оказаться сложно.

_>Да и компилятор уматерит. Что инициализация не в своём скоупе.


Так, надо попробовать, даже интересно стало.

L>>Диспетчерезация обычно определяется собственно уровнем I/O, и как правило сводится к вызову корутины из колбека.

_>Вот тут одна из засад, колбэки не гарантируют их какого потока будут вызваны.

С точки зрения корутины это вовсе не обязательно минус. Воможность "разбудить" корутину в другом потоке может быть большим плюсом.
Опять же, проблемы диспетчерезации ортогональны корутинам.
www.blinnov.com
Re[22]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:41
Оценка:
Здравствуйте, rg45, Вы писали:

R>На всякий случай вдогонку:

R>Правило №16. Избегайте макросов
R>Мало ли, вдруг надумаешь С++ изучать.

На всякий случай: Это лишь рекомендации. Если бы комитет не употреблял тяжелые наркотики уже бы добавили нормальные макросы в язык. А пока приходится использовать то что есть.

ps: странно что про goto правило не привели
Отредактировано 28.08.2025 10:42 kov_serg . Предыдущая версия .
Re[20]: Вопрос по корутинам
От: kov_serg Россия  
Дата: 28.08.25 10:44
Оценка:
Здравствуйте, landerhigh, Вы писали:

_>>Тут всё же предполагается некотарая дисциплина, для соблюдения ряда требований.


L>Требуется в первую очередь понимание, что внутри этой функции много чего нельзя из того, что во всех других местах можно. Когда код уехал другом разработчику, с этим может оказаться сложно.

Так во моногих местах можно делать не всё что угодно. И ничего живём как-то.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.