-
Notifications
You must be signed in to change notification settings - Fork 18
/
cort_proto.h
889 lines (758 loc) · 36.5 KB
/
cort_proto.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
#ifndef CORT_PROTO_H_
#define CORT_PROTO_H_
#include <stdlib.h>
#include <iterator>
#define CO_JOIN2(x,y) x##y
#define CO_JOIN(x,y) CO_JOIN2(x,y)
#if defined(__GNUC__)
#define CO_TYPE_OF __typeof__
#else //C++ 0x or above is required
#define CO_TYPE_OF decltype
#endif
#define CO_AWAITABLE //Declare a function result is awaitable.
#define CO_THIS_AWAITABLE CO_AWAITABLE //Declare a member function result is awaitable in another coroutine member function of current class or its subclass.
struct cort_proto;
struct cort_base{
//trivial but virtual destructor
virtual ~cort_base(){}
cort_proto* cort_parent; //The parent coroutine that waits this coroutine.
void remove_parent(){
cort_parent = 0;
}
void set_parent(cort_proto *arg){
cort_parent = arg;
}
cort_proto *get_parent() const{
return cort_parent;
}
};
struct cort_proto : public cort_base{
public:
typedef cort_proto* (*run_type)(cort_proto*);
protected:
size_t cort_wait_count; //Count of the coroutines that waited by this coroutine.
union{
run_type callback_function; //The callback function executed when the waited subcoroutines are all finished.
size_t object_count; //Used to save count of the resumed waiters of cort_channel.
cort_proto* last_resumer_cort; //Used to save resumer.
}data0;
union{
run_type cort_then_function; //The callback function when this coroutine finished. It maybe zero.
size_t rest_wait_count; //Used to save wait count of cort_wait_n after it resumed its parent
size_t pinned_object_count; //Used to save count of produced and locked objects of cort_channel_proto.
size_t ref_count; //reference count.
void* pdata; //Used for p-impl pattern.
}data10;
public:
enum{is_auto_delete = false}; //When the coroutine finished, it will not "delete this".
cort_proto(){
//We do not use initialize list because we want to remove the initialize order limit for the c++ compiler.
cort_parent = 0;
data0.callback_function = 0;
cort_wait_count = 0;
data10.cort_then_function = 0;
}
//So the cort_proto coroutine will cost (4+1)*sizeof(void*) bytes, 4 data meber, 1 virtual function table pointer.
run_type get_callback_function() const {
return data0.callback_function;
}
void set_callback_function(run_type arg){
data0.callback_function = arg;
}
cort_proto* get_last_resumer() const{
return data0.last_resumer_cort;
}
void set_last_resumer(cort_proto* arg){
data0.last_resumer_cort = arg;
}
size_t get_wait_count() const{
return this->cort_wait_count;
}
void set_wait_count(size_t wait_count){
this->cort_wait_count = wait_count;
}
void incr_wait_count(size_t wait_count){
this->cort_wait_count += wait_count;
}
size_t decr_wait_count(size_t wait_count){
return this->cort_wait_count -= wait_count;
}
//await another coroutine not started.
template<typename T>
cort_proto* await(T* sub_cort){
cort_proto* __wait_result_cort = sub_cort->cort_start();
if(__wait_result_cort != 0){
__wait_result_cort->set_parent(this);
this->incr_wait_count(1);
return this;
}
return 0;
}
//await another coroutine started yet.
cort_proto* until(cort_proto* sub_cort){
if(sub_cort != 0){
sub_cort->set_parent(this);
this->incr_wait_count(1);
}
return sub_cort;
}
//on_finish will be called when this coroutine ended.
//As a coroutine is most ended in the subclass member function, it is designed not to be a virtual function.
cort_proto* on_finish(){
run_type func = then();
if(func != 0 ){
then(0);
return func(this);
}
return 0;
}
//Sometimes you need to reuse this coroutine, or add it to a "coroutine pool".
//You can call the "clear" function to clear or reset the coroutine states.
//You can rewrite the function for your subclass.
virtual void clear(){}
//If T is a local coroutine class, c++11 needed because c++03 disabled local class to be the template argument.
//corts[0].then<T>;
template<typename T>
void then(){
data10.cort_then_function = T::cort_start_static;
}
void then(run_type then_function){
data10.cort_then_function = then_function;
}
run_type then() const {
return data10.cort_then_function;
}
//When all the coroutines awaited by this coroutine finished, this coroutine will be resumed by member function "resume".
//last_resumer_cort is the coroutine that generates the calling.
void resume(cort_proto* last_resumer_cort = 0) {
//We save the cort_parent before callback_function. So you can "delete this" in your callback_function.
cort_proto* that = this;
cort_proto* cort_parent_save;
run_type callback_function;
do {
cort_parent_save = that->get_parent(); //we save it because that may be deleted in callback_function
callback_function = that->get_callback_function();
that->set_last_resumer(last_resumer_cort);
}while((callback_function == 0 || callback_function(that) == 0) &&
((last_resumer_cort = that, that = cort_parent_save) != 0) &&
(cort_parent_save->decr_wait_count(1) == 0));
}
void try_resume(cort_proto* resumer){
if(this->decr_wait_count(1) == 0){
this->resume(resumer);
}
}
};
template<typename T>
struct cort_auto_delete : public T{
enum{is_auto_delete = true};
cort_proto* on_finish(){
cort_proto* result = T::on_finish();
if(result == 0 && !(T::is_auto_delete)){
delete this;
}
return result;
}
};
typedef cort_auto_delete<cort_proto> cort_auto;
#define CO_GET_1ST_ARG(x,...) x
#define CO_GET_2ND_ARG(x,y,...) y
#define CO_PARAMS(...) __VA_ARGS__
#define CO_REMOVE_PARENTHESIS(x) CO_EXPAND(CO_PARAMS x)
#define CO_EXPAND(x) x
#define CO_EMPTY_EXPAND(...)
#define CO_COMMA_GEN(...) ,
#define CO_REMOVE_TWO_IMPL(x,y,...) __VA_ARGS__
#define CO_REMOVE_TWO(...) CO_EXPAND(CO_GET_NTH_ARG(__VA_ARGS__, \
CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL,\
CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL, CO_REMOVE_TWO_IMPL, \
CO_EMPTY_EXPAND, CO_EMPTY_EXPAND, CO_EMPTY_EXPAND))(__VA_ARGS__)
//We add a "ignored_data" because visual C++ compiler can not "perfect forward" empty __VA_ARGS__.
#define CO_FE_0(...)
#define CO_FE_1(op_sep, sep, op_data, ignored_data, x, ...) op_data(x)
#define CO_FE_2(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_1(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_3(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_2(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_4(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_3(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_5(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_4(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_6(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_5(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_7(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_6(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_8(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_7(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_9(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_8(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_FE_10(op_sep, sep, op_data, ignored_data, x, ...) op_data(x) op_sep(sep) CO_EXPAND(CO_FE_9(op_sep, sep, op_data, ignored_data, ##__VA_ARGS__))
#define CO_GET_NTH_ARG( _0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
#define CO_FOR_EACH_IMPL(op_sep, op_data, sep, ...) CO_EXPAND(CO_GET_NTH_ARG(__VA_ARGS__, CO_FE_10, CO_FE_9, CO_FE_8, CO_FE_7, \
CO_FE_6, CO_FE_5, CO_FE_4, CO_FE_3, CO_FE_2, CO_FE_1, CO_FE_0)(op_sep, sep, op_data, ##__VA_ARGS__))
#define CO_FOR_EACH(op_data, ...) CO_FOR_EACH_IMPL(CO_EMPTY_EXPAND, op_data, EMPTY_SEPERATOR, IGNORED_ARG, ##__VA_ARGS__)
#define CO_FOR_EACH_COMMA(op_data, ...) CO_FOR_EACH_IMPL(CO_COMMA_GEN, op_data, EMPTY_SEPERATOR, IGNORED_ARG, ##__VA_ARGS__)
#define CO_FOR_EACH_SEP(op_data, sep, ...) CO_FOR_EACH_IMPL(CO_EXPAND, op_data, sep, IGNORED_ARG, ##__VA_ARGS__)
#define CO_COUNT_INC(x) +1
#define CO_ARG_COUNT(...) (0 CO_FOR_EACH(CO_COUNT_INC, __VA_ARGS__))
// Now let us show an example according to following class cort_example.
// Coroutie class should be public subclass of cort_proto
//struct cort_example : public cort_proto{
//First put all your context variable(with life cycle acrossing a callback proccess) here as class members. Like temporary local virable definition in C language.
//int run_times;
//Then you can overload or define some member functions. The definition can be put outside of the class
//void clear(){}
//~cort_example(){}
//cort_example();
//Next you can use CO_DECL to declare your class is a coroutine class and the default coroutine entry function name.
//Attention your coroutine class can have some more coroutine entry functions.
//CO_DECL(cort_example) to declare this is a coroutine class with "start" as the default coroutine entry function.
//or CO_DECL(cort_example, new_start) to use "new_start" instead default "start" as the default coroutine entry function.
#define CO_DECL(...) \
public: \
CO_DECL_PROTO(__VA_ARGS__) \
/*coroutine function name is start for default*/ \
static cort_proto* cort_start_static(cort_proto* this_ptr){ \
return ((cort_type*)this_ptr)->CO_EXPAND(CO_GET_2ND_ARG(__VA_ARGS__,start))();} \
cort_proto* cort_start() { return this->CO_EXPAND(CO_GET_2ND_ARG(__VA_ARGS__,start))();} \
cort_proto* cort_start(cort_proto* &echo_ptr) {echo_ptr = this; \
return this->CO_EXPAND(CO_GET_2ND_ARG(__VA_ARGS__,start))();}
//If you do not want to declare the defualt entry function name, using CO_DECL_PROTO instead.
#define CO_DECL_PROTO(...) typedef CO_EXPAND(CO_GET_1ST_ARG(__VA_ARGS__)) cort_type;
//Now declare or define your defualt entry function.
//Anyway, you can define it in another file or out of class, leaving only declaration here.
//You can define more than 1 entry functions(not only the default entry).
//It must return cort_proto* and with no arguments.
//Suppose the function has name "start".
//cort_proto* start(){
//The function should begin with "CO_BEGIN", end with "CO_END". The return type should be cort_proto*.
#define CO_BEGIN \
typedef cort_type cort_local_type; \
struct cort_start_impl{\
typedef struct cort_state_struct{ void dummy(){ \
CORT_NEXT_STATE(cort_begin_type)
//The codes between CO_BEGIN and CO_END are your coroutine function codes. You can use the class member variable as the function local variable.
//We will introduce some macro interfaces for the coroutine function codes.
//In your coroutine codes, you may await with other coroutines. Using following macros.
//Do not use them outside the coroutine function.
//You can use CO_AWAIT to wait the finish of another coroutine X.
//First argument is the address of X, or a smart pointer! We only need support of "->" operation of X.
//Second argument is the entry function name of X. It is optional and will use the name declared in CO_DECL if you do not provide the argument.
//If you provide the entry function name, then you can provide the arguments of the function as the rest argument of CO_AWAIT.
//It can not be used in any branch or loop.
#define CO_AWAIT(...) CO_AWAIT_IMPL(CO_EXPAND(CO_GET_1ST_ARG(__VA_ARGS__)), \
CO_EXPAND(CO_GET_2ND_ARG(__VA_ARGS__, cort_start)), (CO_REMOVE_TWO(__VA_ARGS__)))
//You can use CO_AWAIT_ALL to wait no more than 10 sub-coroutine.
//The arguments are the address or smart pointer of X.
//Only their default entry function declared in CO_DECL can be used. You can not specify other entry function name.
//It can not be used in any branch or loop.
#define CO_AWAIT_ALL(...) do{ \
size_t current_wait_count = 0; \
CO_FOR_EACH(CO_AWAIT_MULTI_IMPL, __VA_ARGS__) \
if(current_wait_count != 0){ \
this->set_wait_count(current_wait_count); \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
} \
}while(false);\
CO_NEXT_STATE
//You can use CO_AWAIT_RANGE to wait variate number of coroutines between two forward iterators.
//The arguments are the two iterators of the range and its value_type should be the address or smart pointers.
//Only their default entry function declared in CO_DECL can be used. You can not specify other entry function name.
//It can not be used in any branch or loop.
#define CO_AWAIT_RANGE(sub_cort_begin, sub_cort_end) do{ \
if(cort_wait_range(this, sub_cort_begin, sub_cort_end) != 0){ \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
} \
}while(false); \
CO_NEXT_STATE
//Await finish of any n coroutines from the coroutine arguments.
//Only their default entry function declared in CO_DECL can be used. You can not specify other entry function name.
//It can not be used in any branch or loop.
#define CO_AWAIT_ANY_N(n, ...) do{ \
size_t __current_finished_count = 0; \
size_t __current_waited_count = 0; \
const size_t __max_count = (size_t)(n); \
cort_wait_n *__wait_any_cort = new cort_wait_n(); \
CO_FOR_EACH(CO_AWAIT_ANY_IMPL, __VA_ARGS__) \
/*(__current_waited_count + __current_finished_count) is the total count */ \
__wait_any_cort->init_resume_any(__current_waited_count, __max_count - __current_finished_count); \
__wait_any_cort->start(); \
__wait_any_cort->set_parent(this); \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
}while(false);\
CO_NEXT_STATE
//Await finish of any n coroutines from the coroutine in the range between sub_cort_begin and sub_cort_end.
//Only their default entry function declared in CO_DECL can be used. You can not specify other entry function name.
//It can not be used in any branch or loop.
#define CO_AWAIT_RANGE_ANY_N(n, sub_cort_begin, sub_cort_end) do{ \
if(cort_wait_range_any(this, sub_cort_begin, sub_cort_end, n) != 0){ \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
} \
}while(false);\
CO_NEXT_STATE
//Await finish of any coroutine from the coroutine arguments.
//Only their default entry function declared in CO_DECL can be used. You can not specify other entry function name.
//It can not be used in any branch or loop.
#define CO_AWAIT_ANY(...) CO_AWAIT_ANY_N(1, __VA_ARGS__)
//Await finish of any coroutine from the coroutine in the range between sub_cort_begin and sub_cort_end.
//Only their default entry function declared in CO_DECL can be used.
//It can not be used in any branch or loop.
#define CO_AWAIT_RANGE_ANY(sub_cort_begin, sub_cort_end) CO_AWAIT_RANGE_ANY_N(1, sub_cort_begin, sub_cort_end)
//Above macro interfaces can not be used in a loop body or a conditional branch, due to the C++ limit.
//So we provides some adapter iterfaces to simulate the loop(backward jump) or branch jump(forward jump).
//Definition: we name the first code line after a CO_BEGIN or CO_AWAIT or CO_AWAIT_X, "resume point".
//The resume points divide the function body into different "states".
//After wait in CO_AWAIT_AGAIN finished, it will not turn to next resume point as CO_AWAIT but previous one. It behaves like a loop.
//The argument of CO_AWAIT_AGAIN is same as CO_AWAIT.
#define CO_AWAIT_AGAIN(...) do{ \
cort_proto* __wait_result_cort = (CO_EXPAND(CO_GET_1ST_ARG(__VA_ARGS__)))->CO_EXPAND(CO_GET_2ND_ARG(__VA_ARGS__, cort_start))();\
if(__wait_result_cort != 0){\
__wait_result_cort->set_parent(this); \
this->set_wait_count(1); \
this->set_callback_function((run_type)(&cort_this_type::start_static)); \
return this; \
} \
}while(false); \
return start_static(this);
#define CO_AWAIT_ALL_AGAIN(...) do{ \
size_t current_wait_count = 0; \
CO_FOR_EACH(CO_AWAIT_MULTI_IMPL, __VA_ARGS__) \
if(current_wait_count != 0){ \
this->set_wait_count(current_wait_count); \
this->set_callback_function((run_type)(&cort_this_type::start_static)); \
return this; \
} \
}while(false);\
return start_static(this);
//Sometimes you know you will await some coroutine but you do not know who it is. Or current coroutine is a leaf coroutine and should be resumed manually.
//Using CO_AWAIT_UNKNOWN(), other coroutines can later use cort_proto::await to tell current one what it should wait.
//This is a useful interface for "Dependency Inversion": it enables setting the resume condition of the coroutine after its pause.
//It still can not be used in any branch or loop.
#define CO_AWAIT_UNKNOWN() do{ \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
}while(false); \
CO_NEXT_STATE
#define CO_AWAIT_UNKNOWN_AGAIN() do{ \
this->set_callback_function((run_type)(&cort_this_type::start_static)); \
return this; \
}while(false);
//Some other language use "yield" keyword to implement the "CO_AWAIT_UNKNOWN". So we provide a similiar interface name.
//So CO_YIELD or CO_YIELD_X also generates new resume point.
#define CO_YIELD() CO_AWAIT_UNKNOWN()
#define CO_YIELD_AGAIN() CO_AWAIT_UNKNOWN_AGAIN()
//Following are conditional form await interfaces, they will await only if the bool_exp is true.
#define CO_AWAIT_IF(bool_exp, ...) \
if(!(bool_exp)){CO_SKIP_AWAIT; } \
CO_AWAIT(__VA_ARGS__)
#define CO_AWAIT_ALL_IF(bool_exp, ...) \
if(!(bool_exp)){CO_SKIP_AWAIT; } \
CO_AWAIT_ALL(__VA_ARGS__)
#define CO_AWAIT_RANGE_IF(bool_exp, sub_cort_begin, sub_cort_end) \
if(!(bool_exp)){CO_SKIP_AWAIT; } \
CO_AWAIT_RANGE(sub_cort_begin, sub_cort_end)
#define CO_AWAIT_AGAIN_IF(bool_exp, ...) \
if(bool_exp){CO_AWAIT_AGAIN(__VA_ARGS__); } \
#define CO_AWAIT_UNKNOWN_IF(bool_exp) \
if(!(bool_exp)){CO_SKIP_AWAIT;} \
CO_AWAIT_UNKNOWN()
#define CO_AWAIT_UNKNOWN_AGAIN_IF(bool_exp) \
if(bool_exp){CO_AWAIT_UNKNOWN_AGAIN(); } \
#define CO_YIELD_IF(bool_exp) CO_AWAIT_UNKNOWN_IF(bool_exp)
#define CO_YIELD_AGAIN_IF(bool_exp) CO_AWAIT_UNKNOWN_AGAIN_IF(bool_exp)
//CO_AWAIT(sub_cort) means: start sub_cort and current coroutine will await its finish.
//CO_UNTIL(sub_cort) means: sub_cort has started(before CO_UNTIL), current coroutine will await its finish.
#define CO_UNTIL(sub_cort) CO_YIELD_IF(this->until(sub_cort) != 0)
#define CO_UNTIL_IMPL(sub_cort) ((this->until(sub_cort) == 0) ? 1 : 0)
#define CO_UNTIL_ALL(...) CO_YIELD_IF((CO_FOR_EACH_SEP(CO_UNTIL_IMPL, +, __VA_ARGS__)) != (CO_ARG_COUNT(__VA_ARGS__)))
#define CO_UNTIL_ANY(...) CO_UNTIL_ANY_N(1, __VA_ARGS__)
#define CO_UNTIL_ANY_IMPL(sub_cort){ \
cort_proto *__wait_result_cort = __wait_any_cort->until(sub_cort); \
if(__wait_result_cort != 0){ \
__wait_result_cort->set_parent(__wait_any_cort); \
++__current_waited_count; \
} \
else if(++__current_finished_count == __max_count){ \
if(__current_waited_count != 0){ \
__wait_any_cort->start(); \
} \
else{ \
delete __wait_any_cort; \
} \
break; \
} \
} \
#define CO_UNTIL_ANY_N(n, ...) do{ \
size_t __current_finished_count = 0; \
size_t __current_waited_count = 0; \
const size_t __max_count = (size_t)(n); \
cort_wait_n *__wait_any_cort = new cort_wait_n(); \
CO_FOR_EACH(CO_UNTIL_ANY_IMPL, __VA_ARGS__) \
/*(__current_waited_count + __current_finished_count) is the total count */ \
__wait_any_cort->init_resume_any(__current_waited_count, __max_count - __current_finished_count); \
__wait_any_cort->start(); \
__wait_any_cort->set_parent(this); \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
}while(false);\
CO_NEXT_STATE
//The control interfaces above can generate a "resume point" and following not.
//The control interfaces above should be used with brackets and following not.
//The control interfaces above can not used in loop/branch body or other "{}" in CO_BEGIN and CO_END and following can.
//Sometimes you want to exit from the current coroutine. Using CO_RETURN.
//This is "normal" return, like codes running to CO_END and finished.
#define CO_RETURN return this->on_finish();
//CO_EXIT will exit like CO_RETURN but it does not call on_finish and then_function.
#define CO_EXIT do{ \
this->remove_parent(); \
set_callback_function(0); \
return 0; \
}while(false)
//CO_AGAIN will behave like CO_YIELD but it does not generate a new resume point.
// Current coroutine will be resumed at current resume point.
//This is useful for delayed retry, like dealing errno "EAGAIN".
#define CO_AGAIN do{ \
this->set_callback_function((run_type)(&cort_this_type::start_static)); \
return this; \
}while(false)
//CO_RESTART will restart the codes between CO_BEGIN and CO_END without calling on_finish and then_function
#define CO_RESTART return ((cort_super_type*)this)->local_start();
//You can directly jump to next resume point by CO_SKIP_AWAIT or CO_NEXT
#define CO_SKIP_AWAIT goto ____action_end;
#define CO_NEXT CO_SKIP_AWAIT
//You can directly jump to previous resume point by CO_PREV
#define CO_PREV return ((cort_prev_type*)this)->local_start();
//Implement
#define CO_AWAIT_MULTI_IMPL(sub_cort) \
CO_AWAIT_MULTI_IMPL_BINARY(this, sub_cort)
#define CO_AWAIT_MULTI_IMPL_BINARY(this_ptr, sub_cort) {\
cort_proto* __wait_result_cort = (sub_cort)->cort_start(); \
if(__wait_result_cort != 0){ \
__wait_result_cort->set_parent(this_ptr); \
++current_wait_count; \
} \
}
template <typename T>
size_t cort_wait_range(cort_proto* this_ptr, T begin_forward_iterator, T end_forward_iterator){
size_t current_wait_count = 0;
while(begin_forward_iterator != end_forward_iterator){
typename std::iterator_traits<T>::value_type tmp_cort_new = (*begin_forward_iterator);
CO_AWAIT_MULTI_IMPL_BINARY(this_ptr, tmp_cort_new)
++begin_forward_iterator;
}
this_ptr->set_wait_count(current_wait_count);
return current_wait_count;
}
#define CO_AWAIT_IMPL(sub_cort, func_name, argument_list) \
do{ \
cort_proto* __wait_result_cort = (sub_cort)->func_name(CO_REMOVE_PARENTHESIS(argument_list));\
if(__wait_result_cort != 0){\
__wait_result_cort->set_parent(this); \
this->set_wait_count(1); \
this->set_callback_function((run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
return this; \
} \
}while(false); \
CO_NEXT_STATE
#define CO_NEXT_STATE \
CO_ENTER_NEXT_STATE; \
CORT_NEXT_STATE(CO_JOIN(CO_STATE_NAME, __LINE__))
#define CO_ENTER_NEXT_STATE \
goto ____action_end; ____action_end: return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_start();
#define CORT_NEXT_STATE(cort_state_name) \
}} CO_JOIN(cort_state_name, _prev_type); \
/* Function and class definition of previous state end!*/\
\
CORT_NEXT_STATE_IMPL(cort_state_name)
#define CORT_NEXT_STATE_IMPL(cort_state_name) \
typedef struct cort_state_name : public cort_local_type { \
typedef cort_state_name cort_this_type; \
typedef CO_JOIN(cort_state_name, _prev_type) cort_prev_type; \
static cort_proto* start_static(cort_proto* this_ptr){ \
return ((cort_this_type*)(cort_prev_type*)(this_ptr))->local_start();} \
cort_proto* local_start() { goto ____action_begin; ____action_begin:
#define CO_IF(co_bool_condition) \
goto ____action_end; ____action_end: \
typedef CO_JOIN(cort_state_name_skip, __LINE__)::CO_JOIN(CO_STATE_NAME, __LINE__) skip_type; \
if(co_bool_condition){ \
return ((skip_type*)(this))->local_start(); \
} \
return ((skip_type*)(this))->local_next_start(); \
}}CO_JOIN(cort_state_name_prev_prev, __LINE__); \
/* Function and class definition of previous state end! */\
/* We will use class CO_JOIN(cort_state_name_skip, __LINE__) to wrap the whole if else body! */\
typedef struct CO_JOIN(cort_state_name_skip, __LINE__){ \
typedef struct CO_JOIN(CO_STATE_NAME, __LINE__) : public cort_local_type { \
CO_DECL_PROTO(CO_JOIN(CO_STATE_NAME, __LINE__)) \
typedef CO_JOIN(cort_state_name_prev_prev, __LINE__) cort_prev_type; \
cort_proto* local_start(){ \
/* We need to provide local_start and local_next_start two interfaces. \
So we have to use CO_BEGIN.\
*/ \
CO_BEGIN \
#define CO_BRANCH_BEGIN_IMPL \
CORT_NEXT_STATE(CO_JOIN(CO_STATE_NAME, __LINE__)) \
CO_BEGIN
#define CO_BRANCH_END_IMPL \
goto ____action_end; ____action_end: \
return co_if_end(this); \
}}cort_end_type; \
/* Function and class definition of previous "if else elseif" body end! */\
}; \
return ((cort_start_impl::cort_begin_type*)(cort_prev_type*) \
(cort_start_impl::cort_end_type*)this)->local_start(); \
} \
#define CO_IF_END \
CO_BRANCH_END_IMPL \
cort_proto* local_next_start(){ \
return co_if_end(this); \
} \
} CO_JOIN(cort_state_name_prev, __LINE__); \
/* Any body in "CO_IF/CO_ELSE/CO_ELSE_IF" will call co_if_end to avoid further judge. */ \
static cort_proto* co_if_end(cort_local_type *cort){ \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(CO_JOIN(cort_state_name_prev, __LINE__)*)(cort)) \
->local_start() ;\
CORT_NEXT_STATE(CO_JOIN(CO_STATE_NAME, __LINE__))
#define CO_ELSE_IF_END CO_IF_END
#define CO_ELSE_IF(co_bool_condition) \
CO_BRANCH_END_IMPL \
cort_proto* local_next_start(){ \
goto ____action_end; ____action_end: \
if(co_bool_condition){ \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_start();\
} \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_next_start(); \
CO_BRANCH_BEGIN_IMPL
#define CO_ELSE \
CO_BRANCH_END_IMPL \
cort_proto* local_next_start(){ \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_start(); \
CO_BRANCH_BEGIN_IMPL
#define CO_ELSE_END CO_IF_END
#define CO_WHILE(co_bool_condition, ...) \
goto ____action_end; ____action_end: \
if(co_bool_condition){ \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_start(); \
} \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_next_skip(); \
}}CO_JOIN(cort_state_name, __LINE__); \
CO_WHILE_IMPL(co_bool_condition, ##__VA_ARGS__)
#define CO_DO_WHILE(co_bool_condition, ...) \
goto ____action_end; ____action_end: \
return ((CO_JOIN(CO_STATE_NAME, __LINE__)*)(this))->local_start(); \
}}CO_JOIN(cort_state_name, __LINE__); \
CO_WHILE_IMPL(co_bool_condition, ##__VA_ARGS__)
#define CO_WHILE_IMPL(co_bool_condition, ...) \
typedef struct CO_JOIN(CO_STATE_NAME, __LINE__) : public cort_local_type { \
bool co_while_test(){ \
return (co_bool_condition); \
} \
void co_on_continue(){ \
__VA_ARGS__; \
} \
CO_DECL_PROTO(CO_JOIN(CO_STATE_NAME, __LINE__)) \
typedef cort_type break_type; \
typedef CO_JOIN(cort_state_name, __LINE__) cort_prev_type; \
cort_proto* local_start(){ \
CO_BEGIN
#define CO_BREAK return break_type::local_next_skip()
#define CO_CONTINUE return break_type::local_next_start()
#define CO_WHILE_END \
goto ____action_end; ____action_end: \
CO_CONTINUE; \
}}cort_end_type; \
}; \
return ((cort_start_impl::cort_begin_type*)(cort_prev_type*) \
(cort_start_impl::cort_end_type*)this)->local_start(); \
} \
cort_proto* local_next_start(){ \
((cort_type*)this)->co_on_continue(); \
if(co_while_test()){ \
return this->local_start();\
} \
return this->local_next_skip(); \
} \
cort_proto* local_next_skip(){ \
CO_NEXT_STATE
#define CO_LABEL(co_goto_label) \
goto ____action_end; ____action_end: return ((CO_JOIN(CO_STATE_NAME, co_goto_label)*)(this))->local_start(); \
CORT_NEXT_STATE(CO_JOIN(CO_STATE_NAME, co_goto_label))
#define CO_GOTO(co_goto_label) \
return ((CO_JOIN(CO_STATE_NAME, co_goto_label)*)(this))->local_start();
//When you write "CO_END" and "}", a typical coroutine entry function is defined finished.
#define CO_END \
CO_RETURN; }}cort_end_type; \
typedef cort_begin_type cort_super_type; \
/*Why not direct ((cort_start_impl::cort_begin_type*)this)->local_start()? Because type of (*this) may be a template class and we need to add a "typename" before cort_start_impl::cort_begin_type*/ \
static cort_proto* local_start(cort_type* ptr){ \
return ((cort_begin_type*)(cort_end_type*)(cort_super_type*)ptr)->local_start();\
} }; \
return cort_start_impl::local_start(this);
//}
//};//end of cort_example definition
//Following is a full example, used for CO_WAIT_ANY.
struct cort_wait_n : public cort_proto{
void init_resume_any(size_t total_wait_count, size_t first_wait_count){
data10.rest_wait_count = total_wait_count - first_wait_count;
set_wait_count(first_wait_count);
}
cort_proto* on_finish(){
delete this;
return this; //parent->resume(this->get_last_resumer()); is called mannually so we should not return 0.
}
enum{is_auto_delete = true};
CO_DECL(cort_wait_n)
cort_proto* start(){
CO_BEGIN
CO_YIELD();
cort_proto* parent = get_parent();
if(parent != 0){ //Now n waited coroutines finished so we resume our parent.
parent->resume(this->get_last_resumer());
}else{ //All the coroutines are finished.
CO_RETURN;
}
set_wait_count(data10.rest_wait_count);
CO_YIELD_IF(data10.rest_wait_count != 0);
//Now rest waited coroutines finished so we can delete this according to on_finish.
CO_END
}
};
#define CO_AWAIT_ANY_IMPL(sub_cort) {\
cort_proto *__echo_cort; \
cort_proto *__wait_result_cort = (sub_cort)->cort_start(__echo_cort); \
if(__wait_result_cort != 0){ \
__wait_result_cort->set_parent(__wait_any_cort); \
++__current_waited_count; \
} \
else if(++__current_finished_count == __max_count){ \
this->set_last_resumer(__echo_cort); \
if(__current_waited_count != 0){ \
__wait_any_cort->set_wait_count(__current_waited_count); \
__wait_any_cort->start(); \
} \
else{ \
delete __wait_any_cort; \
} \
break; \
} \
}
template<typename T>
cort_proto* cort_wait_range_any(cort_proto* this_ptr, T begin_forward_iterator, T end_forward_iterator, size_t max_count){
if(begin_forward_iterator == end_forward_iterator){
return 0;
}
size_t current_finished_count = 0;
size_t waited_count = 0;
cort_wait_n *wait_any_cort = new cort_wait_n();
for(;begin_forward_iterator != end_forward_iterator;++begin_forward_iterator){
typename std::iterator_traits<T>::value_type tmp_cort_new = (*begin_forward_iterator);
cort_proto *__wait_result_cort = tmp_cort_new->cort_start();
if(__wait_result_cort != 0){
__wait_result_cort->set_parent(wait_any_cort);
++waited_count;
}
else if(++current_finished_count == max_count){
this_ptr->set_last_resumer(tmp_cort_new);
if(waited_count != 0){
wait_any_cort->set_wait_count(waited_count);
wait_any_cort->start();
}
else{
delete wait_any_cort;
}
return 0;
}
}
wait_any_cort->set_parent(this_ptr);
wait_any_cort->set_wait_count(waited_count);
wait_any_cort->start();
return this_ptr;
}
//Coroutine self wait:
//As mentioned above, coroutine can have multiple entry function.
//Entry function A can call CO_AWAIT_THIS(B) to await member function B of this.
//
//However, there is a problem: function on_finish will be called twice at finish of both A and B.
//You can use CO_BEGIN_THIS instead of CO_BEGIN to avoid calling on_finish in function B.
#define CO_AWAIT_THIS(member_func_name) do{ \
CO_DECL(cort_parent_save) \
cort_proto *result = this->member_func_name(); \
if(result != 0){ \
new cort_parent_save(this, (run_type)(&CO_JOIN(CO_STATE_NAME, __LINE__)::start_static)); \
} \
}while(false); \
CO_NEXT_STATE
#define CO_BEGIN_THIS \
struct cort_local_type: public cort_type{ \
cort_proto* on_finish(){return 0;} \
}; \
typedef cort_type cort_super_type; \
struct cort_start_impl{\
struct cort_begin_type; \
typedef struct cort_state_struct{ void dummy(){ \
CORT_NEXT_STATE(cort_begin_type)
struct cort_parent_save : public cort_proto{
static cort_proto* cort_start_static(cort_proto* arg){
cort_proto* p = arg->get_last_resumer();
p->set_parent(arg->get_parent());
p->set_callback_function(arg->then());
p->resume(p);
delete arg;
return arg; //Because arg->parent() != p, so we should mannually "p->resume(p)"
}
cort_parent_save(cort_proto* arg, run_type func){
set_wait_count(1);
set_callback_function(&cort_start_static);
then(func);
set_parent(arg->get_parent());
arg->set_parent(this);
}
};
//CO_AWAIT can wait another coroutine.
//cort_proto::then can concate another function to a coroutine. The function can be called when the coroutine function is finished and function on_finish is called.
//But ususally cort_proto::then is not called directly with the function.
//First argumet of CO_THEN is the new sub-class type and the rest is the coroutine address.
//CO_THEN will accept a sub-class like following.
//struct cort_then_example: public cort_proto{
// CO_DECL(cort_then_example)
// cort_proto* on_finish(){
// cort_proto* result;
// if(result = cort_proto::on_finish() ){
// return result;
// }
// printf("cort finished!\n");
// return 0;
// }
// cort_proto* start(){
// CO_BEGIN
// printf("cort_then example!\n" );
// return 0;
// CO_END
// }
// }x, y;
//struct cort_then : public cort_then_example{
// CO_DECL(cort_then)
// cort_proto* start(){
// CO_BEGIN
// printf("cort_then function is called!\n" );
// CO_END
// }
//};
//CO_THEN(cort_then, &x);
//CO_THEN(cort_then, &x);
//x.start();
//
//Output is:
//
//cort_then example!
//cort_then function is called!
//cort finished!
//cort finished!
//
//Through x is set twice, only last setting before start is accepted.
//So cort_then::start is called only once.
//cort_then_example::on_finish is called twice, at end of cort_then_example::start and cort_then::start.
//If you want to skip the last calling, using CO_EXIT before CO_END or instead of CO_RETURN;
//cort_then::start is called at first execution of "if(result = cort_proto::on_finish() )".
//Before the calling, the setting is cleared in cort_proto::on_finish.
//So at second calling of cort_proto::on_finish(), cort_then::start will not be called.
#define CO_THEN(new_type, sub_cort) sub_cort->then(new_type::cort_start_static);
//Supporing proto lambda grammar
#include "cort_lambda.h"
#endif