Merge pull request #3344 from alexanderkyte/propagate_mono_error_async
[mono.git] / mono / mini / tasklets.c
1
2 #include "config.h"
3 #include "tasklets.h"
4 #include "mono/metadata/exception.h"
5 #include "mono/metadata/gc-internals.h"
6 #include "mini.h"
7
8 #if defined(MONO_SUPPORT_TASKLETS)
9
10 static mono_mutex_t tasklets_mutex;
11 #define tasklets_lock() mono_os_mutex_lock(&tasklets_mutex)
12 #define tasklets_unlock() mono_os_mutex_unlock(&tasklets_mutex)
13
14 /* LOCKING: tasklets_mutex is assumed to e taken */
15 static void
16 internal_init (void)
17 {
18         if (!mono_gc_is_moving ())
19                 /* Boehm requires the keepalive stacks to be kept in a hash since mono_gc_alloc_fixed () returns GC memory */
20                 g_assert_not_reached ();
21 }
22
23 static void*
24 continuation_alloc (void)
25 {
26         MonoContinuation *cont = g_new0 (MonoContinuation, 1);
27         return cont;
28 }
29
30 static void
31 continuation_free (MonoContinuation *cont)
32 {
33         if (cont->saved_stack)
34                 mono_gc_free_fixed (cont->saved_stack);
35         g_free (cont);
36 }
37
38 static MonoException*
39 continuation_mark_frame (MonoContinuation *cont)
40 {
41         MonoJitTlsData *jit_tls;
42         MonoLMF *lmf;
43         MonoContext ctx, new_ctx;
44         MonoJitInfo *ji, rji;
45         int endloop = FALSE;
46
47         if (cont->domain)
48                 return mono_get_exception_argument ("cont", "Already marked");
49
50         jit_tls = (MonoJitTlsData *)mono_native_tls_get_value (mono_jit_tls_id);
51         lmf = mono_get_lmf();
52         cont->domain = mono_domain_get ();
53         cont->thread_id = mono_native_thread_id_get ();
54
55         /* get to the frame that called Mark () */
56         memset (&rji, 0, sizeof (rji));
57         memset (&ctx, 0, sizeof (ctx));
58         do {
59                 ji = mono_find_jit_info (cont->domain, jit_tls, &rji, NULL, &ctx, &new_ctx, NULL, &lmf, NULL, NULL);
60                 if (!ji || ji == (gpointer)-1) {
61                         return mono_get_exception_not_supported ("Invalid stack frame");
62                 }
63                 ctx = new_ctx;
64                 if (endloop)
65                         break;
66                 if (!ji->is_trampoline && strcmp (jinfo_get_method (ji)->name, "Mark") == 0)
67                         endloop = TRUE;
68         } while (1);
69
70         cont->top_sp = MONO_CONTEXT_GET_SP (&ctx);
71         /*g_print ("method: %s, sp: %p\n", jinfo_get_method (ji)->name, cont->top_sp);*/
72
73         return NULL;
74 }
75
76 static int
77 continuation_store (MonoContinuation *cont, int state, MonoException **e)
78 {
79         MonoLMF *lmf = mono_get_lmf ();
80         gsize num_bytes;
81
82         if (!cont->domain) {
83                 *e =  mono_get_exception_argument ("cont", "Continuation not initialized");
84                 return 0;
85         }
86         if (cont->domain != mono_domain_get () || !mono_native_thread_id_equals (cont->thread_id, mono_native_thread_id_get ())) {
87                 *e = mono_get_exception_argument ("cont", "Continuation from another thread or domain");
88                 return 0;
89         }
90
91         cont->lmf = lmf;
92         cont->return_ip = __builtin_extract_return_addr (__builtin_return_address (0));
93         cont->return_sp = __builtin_frame_address (0);
94
95         num_bytes = (char*)cont->top_sp - (char*)cont->return_sp;
96
97         /*g_print ("store: %d bytes, sp: %p, ip: %p, lmf: %p\n", num_bytes, cont->return_sp, cont->return_ip, lmf);*/
98
99         if (cont->saved_stack && num_bytes <= cont->stack_alloc_size) {
100                 /* clear to avoid GC retention */
101                 if (num_bytes < cont->stack_used_size) {
102                         memset ((char*)cont->saved_stack + num_bytes, 0, cont->stack_used_size - num_bytes);
103                 }
104                 cont->stack_used_size = num_bytes;
105         } else {
106                 tasklets_lock ();
107                 internal_init ();
108                 if (cont->saved_stack)
109                         mono_gc_free_fixed (cont->saved_stack);
110                 cont->stack_used_size = num_bytes;
111                 cont->stack_alloc_size = num_bytes * 1.1;
112                 cont->saved_stack = mono_gc_alloc_fixed (cont->stack_alloc_size, NULL, MONO_ROOT_SOURCE_THREADING, "saved tasklet stack");
113                 tasklets_unlock ();
114         }
115         memcpy (cont->saved_stack, cont->return_sp, num_bytes);
116
117         return state;
118 }
119
120 static MonoException*
121 continuation_restore (MonoContinuation *cont, int state)
122 {
123         MonoLMF **lmf_addr = mono_get_lmf_addr ();
124         MonoContinuationRestore restore_state = mono_tasklets_arch_restore ();
125
126         if (!cont->domain || !cont->return_sp)
127                 return mono_get_exception_argument ("cont", "Continuation not initialized");
128         if (cont->domain != mono_domain_get () || !mono_native_thread_id_equals (cont->thread_id, mono_native_thread_id_get ()))
129                 return mono_get_exception_argument ("cont", "Continuation from another thread or domain");
130
131         /*g_print ("restore: %p, state: %d\n", cont, state);*/
132         *lmf_addr = cont->lmf;
133         restore_state (cont, state, lmf_addr);
134         g_assert_not_reached ();
135 }
136
137 void
138 mono_tasklets_init (void)
139 {
140         mono_os_mutex_init_recursive (&tasklets_mutex);
141
142         mono_add_internal_call ("Mono.Tasklets.Continuation::alloc", continuation_alloc);
143         mono_add_internal_call ("Mono.Tasklets.Continuation::free", continuation_free);
144         mono_add_internal_call ("Mono.Tasklets.Continuation::mark", continuation_mark_frame);
145         mono_add_internal_call ("Mono.Tasklets.Continuation::store", continuation_store);
146         mono_add_internal_call ("Mono.Tasklets.Continuation::restore", continuation_restore);
147 }
148
149 void
150 mono_tasklets_cleanup (void)
151 {
152 }
153 #else
154
155 static
156 void continuations_not_supported (void)
157 {
158         mono_set_pending_exception (mono_get_exception_not_implemented ("Tasklets are not implemented on this platform."));
159 }
160
161 static void*
162 continuation_alloc (void)
163 {
164         continuations_not_supported ();
165         return NULL;
166 }
167
168 static void
169 continuation_free (MonoContinuation *cont)
170 {
171         continuations_not_supported ();
172 }
173
174 static MonoException*
175 continuation_mark_frame (MonoContinuation *cont)
176 {
177         continuations_not_supported ();
178         return NULL;
179 }
180
181 static int
182 continuation_store (MonoContinuation *cont, int state, MonoException **e)
183 {
184         continuations_not_supported ();
185         return 0;
186 }
187
188 static MonoException*
189 continuation_restore (MonoContinuation *cont, int state)
190 {
191         continuations_not_supported ();
192         return NULL;
193 }
194
195 void
196 mono_tasklets_init(void)
197 {
198         mono_add_internal_call ("Mono.Tasklets.Continuation::alloc", continuation_alloc);
199         mono_add_internal_call ("Mono.Tasklets.Continuation::free", continuation_free);
200         mono_add_internal_call ("Mono.Tasklets.Continuation::mark", continuation_mark_frame);
201         mono_add_internal_call ("Mono.Tasklets.Continuation::store", continuation_store);
202         mono_add_internal_call ("Mono.Tasklets.Continuation::restore", continuation_restore);
203
204 }
205 #endif
206