1 /*
2 Copyright (C) 2001-2014, Parrot Foundation.
3
4 =head1 NAME
5
6 src/interp/inter_cb.c - Parrot Interpreter - Callback Function Handling
7
8 =head1 DESCRIPTION
9
10 NCI callback functions may run whenever the C code executes the callback.
11 To be prepared for asynchronous callbacks these are converted to callback
12 events.
13
14 Often callbacks should run synchronously. This can only happen when
15 the C-library calls the callback, because Parrot called a function in
16 the C-library.
17
18 =head2 Functions
19
20 =over 4
21
22 =cut
23
24 */
25
26 #include "parrot/parrot.h"
27 #include "parrot/extend.h"
28 #include "pmc/pmc_parrotinterpreter.h"
29 #include "pmc/pmc_callback.h"
30 #include "inter_cb.str"
31
32 static Interp * default_interp = NULL;
33
34 /* HEADERIZER HFILE: include/parrot/interpreter.h */
35
36 /* HEADERIZER BEGIN: static */
37 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
38
39 static void callback_CD(PARROT_INTERP,
40 ARGIN(char *external_data),
41 ARGMOD(PMC *user_data))
42 __attribute__nonnull__(1)
43 __attribute__nonnull__(2)
44 __attribute__nonnull__(3)
45 FUNC_MODIFIES(*user_data);
46
47 static void verify_CD(
48 ARGIN(char *external_data),
49 ARGMOD_NULLOK(PMC *user_data))
50 __attribute__nonnull__(1)
51 FUNC_MODIFIES(*user_data);
52
53 #define ASSERT_ARGS_callback_CD __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
54 PARROT_ASSERT_ARG(interp) \
55 , PARROT_ASSERT_ARG(external_data) \
56 , PARROT_ASSERT_ARG(user_data))
57 #define ASSERT_ARGS_verify_CD __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
58 PARROT_ASSERT_ARG(external_data))
59 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
60 /* HEADERIZER END: static */
61
62 /*
63
64 =item C<PMC* Parrot_make_cb(PARROT_INTERP, PMC* sub, PMC* user_data, STRING
65 *cb_signature)>
66
67 Create a callback function according to pdd16.
68
69 =cut
70
71 */
72
73 PARROT_EXPORT
74 PARROT_CANNOT_RETURN_NULL
75 PARROT_WARN_UNUSED_RESULT
76 PMC*
Parrot_make_cb(PARROT_INTERP,ARGMOD (PMC * sub),ARGIN (PMC * user_data),ARGIN (STRING * cb_signature))77 Parrot_make_cb(PARROT_INTERP, ARGMOD(PMC* sub), ARGIN(PMC* user_data),
78 ARGIN(STRING *cb_signature))
79 {
80 ASSERT_ARGS(Parrot_make_cb)
81 PMC *cb, *cb_sig;
82 int type = 0;
83 STRING *sc;
84
85 /*
86 * we stuff all the information into the user_data PMC and pass that
87 * on to the external sub
88 */
89 PMC * const interp_pmc = VTABLE_get_pmc_keyed_int(interp, interp->iglobals,
90 (INTVAL) IGLOBALS_INTERPRETER);
91
92 if (default_interp == NULL)
93 default_interp = interp;
94
95 /* be sure __LINE__ is consistent */
96 sc = CONST_STRING(interp, "_interpreter");
97 Parrot_pmc_setprop(interp, user_data, sc, interp_pmc);
98 sc = CONST_STRING(interp, "_sub");
99 Parrot_pmc_setprop(interp, user_data, sc, sub);
100 /* only ASCII signatures are supported */
101 if (STRING_length(cb_signature) == 3) {
102 /* Callback return type ignored */
103
104 if (STRING_ord(interp, cb_signature, 1) == 'U') {
105 type = 'D';
106 }
107 else {
108 if (STRING_ord(interp, cb_signature, 2) == 'U') {
109 type = 'C';
110 }
111 }
112 }
113 if (type != 'C' && type != 'D')
114 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
115 "unhandled signature '%Ss' in make_cb", cb_signature);
116
117 cb_sig = Parrot_pmc_new(interp, enum_class_String);
118 VTABLE_set_string_native(interp, cb_sig, cb_signature);
119 sc = CONST_STRING(interp, "_signature");
120 Parrot_pmc_setprop(interp, user_data, sc, cb_sig);
121 /*
122 * We are going to be passing the user_data PMC to external code, but
123 * it may go out of scope until the callback is called -- we don't know
124 * for certain as we don't know when the callback will be called.
125 * Therefore, to prevent the PMC from being destroyed by a GC sweep,
126 * we need to anchor it.
127 *
128 */
129 Parrot_pmc_gc_register(interp, user_data);
130
131 /*
132 * Finally, the external lib awaits a function pointer.
133 * Create a PMC that points to Parrot_callback_C (or _D);
134 * it can be passed on with signature 'p'.
135 */
136 cb = Parrot_pmc_new(interp, enum_class_UnManagedStruct);
137 /*
138 * Currently, we handle only 2 types:
139 * _C ... user_data is 2nd parameter
140 * _D ... user_data is 1st parameter
141 */
142 if (type == 'C')
143 VTABLE_set_pointer(interp, cb, F2DPTR(Parrot_callback_C));
144 else
145 VTABLE_set_pointer(interp, cb, F2DPTR(Parrot_callback_D));
146 Parrot_pmc_gc_register(interp, cb);
147
148 return cb;
149 }
150
151 /*
152
153 =item C<static void verify_CD(char *external_data, PMC *user_data)>
154
155 Verify user_data PMC then continue with callback_CD
156
157 =cut
158
159 */
160
161 static void
verify_CD(ARGIN (char * external_data),ARGMOD_NULLOK (PMC * user_data))162 verify_CD(ARGIN(char *external_data), ARGMOD_NULLOK(PMC *user_data))
163 {
164 ASSERT_ARGS(verify_CD)
165 PARROT_INTERP = default_interp;
166 PMC *interp_pmc;
167 STRING *sc;
168
169 /*
170 * 1.) user_data is from external code so:
171 * verify that we get a PMC that is one that we have passed in
172 * as user data, when we prepared the callback
173 */
174
175 /* a NULL pointer or a pointer not aligned is very likely wrong */
176 if (!user_data)
177 PANIC(interp, "user_data is NULL");
178 if (PMC_IS_NULL(user_data))
179 PANIC(interp, "user_data is PMCNULL");
180 if ((ptrcast_t)user_data & 3)
181 PANIC(interp, "user_data doesn't look like a pointer");
182
183 /* Fetch original interpreter from prop */
184 sc = CONST_STRING(interp, "_interpreter");
185 interp_pmc = Parrot_pmc_getprop(interp, user_data, sc);
186 GETATTR_ParrotInterpreter_interp(interp, interp_pmc, interp);
187 if (!interp)
188 PANIC(interp, "interpreter not found for callback");
189
190 /*
191 * 2) some more checks
192 * now we should have the interpreter where that callback
193 * did originate - do some further checks on the PMC
194 */
195
196 /* if that doesn't look like a PMC we are still lost */
197 if (!PObj_is_PMC_TEST(user_data))
198 PANIC(interp, "user_data isn't a PMC");
199
200 if (!user_data->vtable)
201 PANIC(interp, "user_data hasn't a vtable");
202 /*
203 * ok fine till here
204 */
205 callback_CD(interp, external_data, user_data);
206 }
207
208 /*
209
210 =item C<static void callback_CD(PARROT_INTERP, char *external_data, PMC
211 *user_data)>
212
213 Common callback function handler. See pdd16.
214
215 =cut
216
217 */
218
219 static void
callback_CD(PARROT_INTERP,ARGIN (char * external_data),ARGMOD (PMC * user_data))220 callback_CD(PARROT_INTERP, ARGIN(char *external_data), ARGMOD(PMC *user_data))
221 {
222 ASSERT_ARGS(callback_CD)
223
224 PMC *passed_interp; /* the interp that originated the CB */
225 PMC *passed_synchronous; /* flagging synchronous execution */
226 int synchronous = 0; /* cb is hitting this sub somewhen
227 * inmidst, or not */
228 STRING *sc;
229 /*
230 * 3) check interpreter ...
231 */
232 sc = CONST_STRING(interp, "_interpreter");
233 passed_interp = Parrot_pmc_getprop(interp, user_data, sc);
234 if (VTABLE_get_pointer(interp, passed_interp) != interp)
235 PANIC(interp, "callback gone to wrong interpreter");
236
237 sc = CONST_STRING(interp, "_synchronous");
238 passed_synchronous = Parrot_pmc_getprop(interp, user_data, sc);
239 if (!PMC_IS_NULL(passed_synchronous) &&
240 VTABLE_get_bool(interp, passed_synchronous))
241 synchronous = 1;
242
243 /*
244 * 4) check if the call_back is synchronous:
245 * - if yes we are inside the NCI call
246 * we could run the Sub immediately now (I think)
247 * - if no, and that's always safe, post a callback event
248 */
249
250 if (synchronous) {
251 /*
252 * just call the sub
253 */
254 Parrot_run_callback(interp, user_data, external_data);
255 }
256 else {
257 /*
258 * create a CB_EVENT, put user_data and data inside and finito
259 *
260 * *if* this function is finally no void, i.e. the calling
261 * C program awaits a return result from the callback,
262 * then wait for the CB_EVENT_xx to finish and return the
263 * result
264 */
265 PMC * const callback = Parrot_pmc_new(interp, enum_class_Callback);
266 Parrot_Callback_attributes * const cb_data = PARROT_CALLBACK(callback);
267 cb_data->user_data = user_data;
268 cb_data->external_data = (PMC*) external_data;
269
270 Parrot_cx_schedule_immediate(interp, callback);
271 }
272 }
273
274 /*
275
276 =item C<void Parrot_run_callback(PARROT_INTERP, PMC* user_data, void*
277 external_data)>
278
279 Run a callback function. The PMC* user_data holds all
280 necessary items in its properties.
281
282 =cut
283
284 */
285
286 PARROT_EXPORT
287 void
Parrot_run_callback(PARROT_INTERP,ARGMOD (PMC * user_data),ARGIN (void * external_data))288 Parrot_run_callback(PARROT_INTERP,
289 ARGMOD(PMC* user_data), ARGIN(void* external_data))
290 {
291 ASSERT_ARGS(Parrot_run_callback)
292 PMC *signature;
293 PMC *sub;
294 STRING *sig_str;
295 INTVAL ch;
296 char pasm_sig[5];
297 INTVAL i_param;
298 PMC *p_param;
299 void *param = NULL; /* avoid -Ox warning */
300 STRING *sc;
301
302 sc = CONST_STRING(interp, "_sub");
303 sub = Parrot_pmc_getprop(interp, user_data, sc);
304 sc = CONST_STRING(interp, "_signature");
305 signature = Parrot_pmc_getprop(interp, user_data, sc);
306 sig_str = VTABLE_get_string(interp, signature);
307
308 pasm_sig[0] = 'P';
309 ch = STRING_ord(interp, sig_str, 1);
310 if (ch == 'U') /* user_data Z in pdd16 */
311 ch = STRING_ord(interp, sig_str, 2); /* ch is now type of external data */
312 switch (ch) {
313 case 'v':
314 pasm_sig[1] = 'v';
315 break;
316 case 'l':
317 /* FIXME: issue #742 */
318 i_param = (INTVAL)(long)(INTVAL) external_data;
319 goto case_I;
320 case 'i':
321 /* FIXME: issue #742 */
322 i_param = (INTVAL)(int)(INTVAL) external_data;
323 goto case_I;
324 case 's':
325 /* FIXME: issue #742 */
326 i_param = (INTVAL)(short)(INTVAL) external_data;
327 goto case_I;
328 case 'c':
329 /* FIXME: issue #742 */
330 i_param = (INTVAL)(char)(INTVAL) external_data;
331 case_I:
332 pasm_sig[1] = 'I';
333 param = (void*) i_param;
334 break;
335 case 'p':
336 /* created a UnManagedStruct */
337 p_param = Parrot_pmc_new(interp, enum_class_UnManagedStruct);
338 VTABLE_set_pointer(interp, p_param, external_data);
339 pasm_sig[1] = 'P';
340 param = (void*) p_param;
341 break;
342 case 't':
343 pasm_sig[1] = 'S';
344 param = Parrot_str_new(interp, (const char*)external_data, 0);
345 break;
346 default:
347 Parrot_ex_throw_from_c_args(interp, NULL, EXCEPTION_INVALID_OPERATION,
348 "unhandled signature char '%c' in run_cb", ch);
349 }
350 pasm_sig[2] = '-';
351 pasm_sig[3] = '>'; /* no return value supported yet */
352 pasm_sig[4] = '\0';
353 Parrot_ext_call(interp, sub, pasm_sig, user_data, param);
354 }
355 /*
356
357 =item C<void Parrot_callback_C(char *external_data, PMC *user_data)>
358
359 =item C<void Parrot_callback_D(PMC *user_data, char *external_data)>
360
361 NCI callback functions. See pdd16.
362
363 =cut
364
365 */
366
367 PARROT_EXPORT
368 void
Parrot_callback_C(ARGIN (char * external_data),ARGMOD_NULLOK (PMC * user_data))369 Parrot_callback_C(ARGIN(char *external_data), ARGMOD_NULLOK(PMC *user_data))
370 {
371 ASSERT_ARGS(Parrot_callback_C)
372 verify_CD(external_data, user_data);
373 }
374
375 PARROT_EXPORT
376 void
Parrot_callback_D(ARGMOD (PMC * user_data),ARGMOD_NULLOK (char * external_data))377 Parrot_callback_D(ARGMOD(PMC *user_data), ARGMOD_NULLOK(char *external_data))
378 {
379 ASSERT_ARGS(Parrot_callback_D)
380 verify_CD(external_data, user_data);
381 }
382
383 /*
384
385 =back
386
387 */
388
389
390 /*
391 * Local variables:
392 * c-file-style: "parrot"
393 * End:
394 * vim: expandtab shiftwidth=4 cinoptions='\:2=2' :
395 */
396