Fix zero luma corner case
[lsnes.git] / src / core / coroutine.cpp
blob402fe7e9d4e8ad6dd362985c9077c23e9ca1287a
1 #include "core/coroutine.hpp"
3 #include <iostream>
4 #include <cstdlib>
5 #include <cstdio>
6 #include <vector>
8 namespace
10 #if defined(__amd64__)
11 void trampoline_fn(void (*fn)(void* arg), void* arg) __attribute__((sysv_abi));
12 #else
13 #if defined(__i386__)
14 void trampoline_fn(void (*fn)(void* arg), void* arg) __attribute__((stdcall));
15 #else
16 #error "This CPU is not supported"
17 #endif
18 #endif
19 void trampoline_fn(void (*fn)(void* arg), void* arg)
21 fn(arg);
22 coroutine::cexit();
25 #ifdef __amd64__
26 bool stacks_grow_down = true;
27 void switch_stacks(void (*fn)(void* arg), void* arg, void* new_esp)
29 __asm__ __volatile__("movq %%rax,%%rsp; call *%%rdx" :: "D"(fn), "S"(arg), "a"(new_esp), "d"(trampoline_fn));
31 #else
32 #ifdef __i386__
33 bool stacks_grow_down = true;
34 void switch_stacks(void (*fn)(void* arg), void* arg, void* new_esp)
36 __asm__ __volatile__("movl %%eax,%%esp; pushl %%esi; push %%edi ; call *%%edx" :: "D"(fn), "S"(arg),
37 "a"(new_esp), "d"(trampoline_fn));
39 #else
40 #error "This CPU is not supported"
41 #endif
42 #endif
43 jmp_buf main_saved_env;
44 coroutine* executing_coroutine = NULL;
47 coroutine::coroutine(void (*fn)(void* arg), void* arg, size_t stacksize)
49 dead = false;
50 if(executing_coroutine) {
51 std::cerr << "FATAL: Coroutine create only allowed from main coroutine!" << std::endl;
52 exit(1);
54 executing_coroutine = this;
55 if(setjmp(main_saved_env)) {
56 executing_coroutine = NULL;
57 return;
59 stackblock = new unsigned char[stacksize];
60 unsigned char* esp = stackblock;
61 if(stacks_grow_down)
62 esp = esp + stacksize;
63 switch_stacks(fn, arg, esp);
66 coroutine::~coroutine() throw()
68 if(!dead) {
69 std::cerr << "FATAL: Trying to delete a live coroutine!" << std::endl;
70 exit(1);
72 delete[] stackblock;
75 void coroutine::resume()
77 if(executing_coroutine) {
78 std::cerr << "FATAL: Coroutine resume only allowed from main coroutine!" << std::endl;
79 exit(1);
81 if(dead)
82 return;
83 executing_coroutine = this;
84 if(setjmp(main_saved_env)) {
85 executing_coroutine = NULL;
86 return;
88 longjmp(saved_env, 1);
91 void coroutine::yield()
93 if(!executing_coroutine) {
94 std::cerr << "FATAL: Coroutine yield not allowed from main coroutine!" << std::endl;
95 exit(1);
97 if(setjmp(executing_coroutine->saved_env))
98 return;
99 longjmp(main_saved_env, 1);
102 bool coroutine::is_dead()
104 return dead;
107 void coroutine::cexit()
109 if(!executing_coroutine) {
110 std::cerr << "FATAL: Main coroutine can't exit!" << std::endl;
111 exit(1);
113 executing_coroutine->dead = true;
114 yield();
117 #ifdef TEST_COROUTINES
119 void fn(void* arg)
121 std::cout << "Print #1 from coroutine (" << arg << ")" << std::endl;
122 coroutine::yield();
123 std::cout << "Print #2 from coroutine (" << arg << ")" << std::endl;
124 coroutine::yield();
125 std::cout << "Print #3 from coroutine (" << arg << ")" << std::endl;
128 int main()
130 int x;
131 coroutine c(fn, &x, 8 * 1024 * 1024);
132 std::cout << "Back to main thread" << std::endl;
133 std::cout << "Coroutine dead flag is " << c.is_dead() << std::endl;
134 c.resume();
135 std::cout << "Back to main thread" << std::endl;
136 std::cout << "Coroutine dead flag is " << c.is_dead() << std::endl;
137 c.resume();
138 std::cout << "Back to main thread" << std::endl;
139 std::cout << "Coroutine dead flag is " << c.is_dead() << std::endl;
140 return 0;
143 #endif