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