fix codetest failure - ASSERT_ARGS does not have a ; after and
[parrot.git] / src / interp / inter_cb.c
blobe7277c55759c34f9ab6fee1efa3f29e7fca9e247
1 /*
2 Copyright (C) 2001-2010, Parrot Foundation.
3 $Id$
5 =head1 NAME
7 src/interp/inter_cb.c - Parrot Interpreter - Callback Function Handling
9 =head1 DESCRIPTION
11 NCI callback functions may run whenever the C code executes the callback.
12 To be prepared for asynchronous callbacks these are converted to callback
13 events.
15 Often callbacks should run synchronously. This can only happen when
16 the C-library calls the callback, because Parrot called a function in
17 the C-library.
19 =head2 Functions
21 =over 4
23 =cut
27 #include "parrot/parrot.h"
28 #include "parrot/extend.h"
29 #include "pmc/pmc_parrotinterpreter.h"
30 #include "inter_cb.str"
33 /* HEADERIZER HFILE: include/parrot/interpreter.h */
35 /* HEADERIZER BEGIN: static */
36 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
38 static void callback_CD(PARROT_INTERP,
39 ARGIN(char *external_data),
40 ARGMOD(PMC *user_data))
41 __attribute__nonnull__(1)
42 __attribute__nonnull__(2)
43 __attribute__nonnull__(3)
44 FUNC_MODIFIES(*user_data);
46 static void verify_CD(
47 ARGIN(char *external_data),
48 ARGMOD_NULLOK(PMC *user_data))
49 __attribute__nonnull__(1)
50 FUNC_MODIFIES(*user_data);
52 #define ASSERT_ARGS_callback_CD __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
53 PARROT_ASSERT_ARG(interp) \
54 , PARROT_ASSERT_ARG(external_data) \
55 , PARROT_ASSERT_ARG(user_data))
56 #define ASSERT_ARGS_verify_CD __attribute__unused__ int _ASSERT_ARGS_CHECK = (\
57 PARROT_ASSERT_ARG(external_data))
58 /* Don't modify between HEADERIZER BEGIN / HEADERIZER END. Your changes will be lost. */
59 /* HEADERIZER END: static */
63 =item C<PMC* Parrot_make_cb(PARROT_INTERP, PMC* sub, PMC* user_data, STRING
64 *cb_signature)>
66 Create a callback function according to pdd16.
68 =cut
72 PARROT_EXPORT
73 PARROT_CANNOT_RETURN_NULL
74 PARROT_WARN_UNUSED_RESULT
75 PMC*
76 Parrot_make_cb(PARROT_INTERP, ARGMOD(PMC* sub), ARGIN(PMC* user_data),
77 ARGIN(STRING *cb_signature))
79 ASSERT_ARGS(Parrot_make_cb)
80 PMC *cb, *cb_sig;
81 int type = 0;
82 STRING *sc;
83 char * const signature = Parrot_str_to_cstring(interp, cb_signature);
85 * we stuff all the information into the user_data PMC and pass that
86 * on to the external sub
88 PMC * const interp_pmc = VTABLE_get_pmc_keyed_int(interp, interp->iglobals,
89 (INTVAL) IGLOBALS_INTERPRETER);
91 /* be sure __LINE__ is consistent */
92 sc = CONST_STRING(interp, "_interpreter");
93 VTABLE_setprop(interp, user_data, sc, interp_pmc);
94 sc = CONST_STRING(interp, "_sub");
95 VTABLE_setprop(interp, user_data, sc, sub);
96 /* only ASCII signatures are supported */
97 if (strlen(signature) == 3) {
98 /* Callback return type ignored */
100 if (signature[1] == 'U') {
101 type = 'D';
103 else {
104 if (signature[2] == 'U') {
105 type = 'C';
109 Parrot_str_free_cstring(signature);
110 if (type != 'C' && type != 'D')
111 Parrot_ex_throw_from_c_args(interp, NULL, 1,
112 "unhandled signature '%Ss' in make_cb", cb_signature);
114 cb_sig = Parrot_pmc_new(interp, enum_class_String);
115 VTABLE_set_string_native(interp, cb_sig, cb_signature);
116 sc = CONST_STRING(interp, "_signature");
117 VTABLE_setprop(interp, user_data, sc, cb_sig);
119 * We are going to be passing the user_data PMC to external code, but
120 * it may go out of scope until the callback is called -- we don't know
121 * for certain as we don't know when the callback will be called.
122 * Therefore, to prevent the PMC from being destroyed by a GC sweep,
123 * we need to anchor it.
126 Parrot_pmc_gc_register(interp, user_data);
129 * Finally, the external lib awaits a function pointer.
130 * Create a PMC that points to Parrot_callback_C (or _D);
131 * it can be passed on with signature 'p'.
133 cb = Parrot_pmc_new(interp, enum_class_UnManagedStruct);
135 * Currently, we handle only 2 types:
136 * _C ... user_data is 2nd parameter
137 * _D ... user_data is 1st parameter
139 if (type == 'C')
140 VTABLE_set_pointer(interp, cb, F2DPTR(Parrot_callback_C));
141 else
142 VTABLE_set_pointer(interp, cb, F2DPTR(Parrot_callback_D));
143 Parrot_pmc_gc_register(interp, cb);
145 return cb;
150 =item C<static void verify_CD(char *external_data, PMC *user_data)>
152 Verify user_data PMC then continue with callback_CD
154 =cut
158 static void
159 verify_CD(ARGIN(char *external_data), ARGMOD_NULLOK(PMC *user_data))
161 ASSERT_ARGS(verify_CD)
162 PARROT_INTERP = NULL;
163 PMC *interp_pmc;
164 STRING *sc;
167 * 1.) user_data is from external code so:
168 * verify that we get a PMC that is one that we have passed in
169 * as user data, when we prepared the callback
172 /* a NULL pointer or a pointer not aligned is very likely wrong */
173 if (!user_data)
174 PANIC(interp, "user_data is NULL");
175 if (PMC_IS_NULL(user_data))
176 PANIC(interp, "user_data is PMCNULL");
177 if ((UINTVAL)user_data & 3)
178 PANIC(interp, "user_data doesn't look like a pointer");
180 /* Fetch original interpreter from prop */
181 LOCK(interpreter_array_mutex);
183 interp = interpreter_array[0];
184 sc = CONST_STRING(interp, "_interpreter");
185 interp_pmc = VTABLE_getprop(interp, user_data, sc);
186 GETATTR_ParrotInterpreter_interp(interp, interp_pmc, interp);
188 UNLOCK(interpreter_array_mutex);
189 if (!interp)
190 PANIC(interp, "interpreter not found for callback");
193 * 2) some more checks
194 * now we should have the interpreter where that callback
195 * did originate - do some further checks on the PMC
198 /* if that doesn't look like a PMC we are still lost */
199 if (!PObj_is_PMC_TEST(user_data))
200 PANIC(interp, "user_data isn't a PMC");
202 if (!user_data->vtable)
203 PANIC(interp, "user_data hasn't a vtable");
205 * ok fine till here
207 callback_CD(interp, external_data, user_data);
212 =item C<static void callback_CD(PARROT_INTERP, char *external_data, PMC
213 *user_data)>
215 Common callback function handler. See pdd16.
217 =cut
221 static void
222 callback_CD(PARROT_INTERP, ARGIN(char *external_data), ARGMOD(PMC *user_data))
224 ASSERT_ARGS(callback_CD)
226 PMC *passed_interp; /* the interp that originated the CB */
227 PMC *passed_synchronous; /* flagging synchronous execution */
228 int synchronous = 0; /* cb is hitting this sub somewhen
229 * inmidst, or not */
230 STRING *sc;
232 * 3) check interpreter ...
234 sc = CONST_STRING(interp, "_interpreter");
235 passed_interp = VTABLE_getprop(interp, user_data, sc);
236 if (VTABLE_get_pointer(interp, passed_interp) != interp)
237 PANIC(interp, "callback gone to wrong interpreter");
239 sc = CONST_STRING(interp, "_synchronous");
240 passed_synchronous = VTABLE_getprop(interp, user_data, sc);
241 if (!PMC_IS_NULL(passed_synchronous) &&
242 VTABLE_get_bool(interp, passed_synchronous))
243 synchronous = 1;
246 * 4) check if the call_back is synchronous:
247 * - if yes we are inside the NCI call
248 * we could run the Sub immediately now (I think)
249 * - if no, and that's always safe, post a callback event
252 if (synchronous) {
254 * just call the sub
256 Parrot_run_callback(interp, user_data, external_data);
258 else {
260 * create a CB_EVENT, put user_data and data inside and finito
262 * *if* this function is finally no void, i.e. the calling
263 * C program awaits a return result from the callback,
264 * then wait for the CB_EVENT_xx to finish and return the
265 * result
267 Parrot_cx_schedule_callback(interp, user_data, external_data);
273 =item C<void Parrot_run_callback(PARROT_INTERP, PMC* user_data, char*
274 external_data)>
276 Run a callback function. The PMC* user_data holds all
277 necessary items in its properties.
279 =cut
283 PARROT_EXPORT
284 void
285 Parrot_run_callback(PARROT_INTERP,
286 ARGMOD(PMC* user_data), ARGIN(char* external_data))
288 ASSERT_ARGS(Parrot_run_callback)
289 PMC *signature;
290 PMC *sub;
291 STRING *sig_str;
292 char *p;
293 char ch;
294 char *sig_cstr;
295 char pasm_sig[5];
296 INTVAL i_param;
297 PMC *p_param;
298 void *param = NULL; /* avoid -Ox warning */
299 STRING *sc;
301 sc = CONST_STRING(interp, "_sub");
302 sub = VTABLE_getprop(interp, user_data, sc);
303 sc = CONST_STRING(interp, "_signature");
304 signature = VTABLE_getprop(interp, user_data, sc);
306 sig_str = VTABLE_get_string(interp, signature);
307 sig_cstr = Parrot_str_to_cstring(interp, sig_str);
308 p = sig_cstr;
309 ++p; /* Skip return type */
311 pasm_sig[0] = 'P';
312 if (*p == 'U') /* user_data Z in pdd16 */
313 ++p; /* p is now type of external data */
314 switch (*p) {
315 case 'v':
316 pasm_sig[1] = 'v';
317 break;
318 case 'l':
319 i_param = (INTVAL)(long) external_data;
320 goto case_I;
321 case 'i':
322 i_param = (INTVAL)(int)(long) external_data;
323 goto case_I;
324 case 's':
325 i_param = (INTVAL)(short)(long) external_data;
326 goto case_I;
327 case 'c':
328 i_param = (INTVAL)(char)(long)external_data;
329 case_I:
330 pasm_sig[1] = 'I';
331 param = (void*) i_param;
332 break;
333 case 'p':
334 /* created a UnManagedStruct */
335 p_param = Parrot_pmc_new(interp, enum_class_UnManagedStruct);
336 VTABLE_set_pointer(interp, p_param, external_data);
337 pasm_sig[1] = 'P';
338 param = (void*) p_param;
339 break;
340 case 't':
341 pasm_sig[1] = 'S';
342 param = Parrot_str_new(interp, external_data, 0);
343 break;
344 default:
345 ch = *p;
346 Parrot_str_free_cstring(sig_cstr);
347 Parrot_ex_throw_from_c_args(interp, NULL, 1,
348 "unhandled signature char '%c' in run_cb", ch);
350 Parrot_str_free_cstring(sig_cstr);
351 pasm_sig[2] = '-';
352 pasm_sig[3] = '>'; /* no return value supported yet */
353 pasm_sig[4] = '\0';
354 Parrot_ext_call(interp, sub, pasm_sig, user_data, param);
358 =item C<void Parrot_callback_C(char *external_data, PMC *user_data)>
360 =item C<void Parrot_callback_D(PMC *user_data, char *external_data)>
362 NCI callback functions. See pdd16.
364 =cut
368 PARROT_EXPORT
369 void
370 Parrot_callback_C(ARGIN(char *external_data), ARGMOD_NULLOK(PMC *user_data))
372 ASSERT_ARGS(Parrot_callback_C)
373 verify_CD(external_data, user_data);
376 PARROT_EXPORT
377 void
378 Parrot_callback_D(ARGMOD(PMC *user_data), ARGMOD_NULLOK(char *external_data))
380 ASSERT_ARGS(Parrot_callback_D)
381 verify_CD(external_data, user_data);
386 * Local variables:
387 * c-file-style: "parrot"
388 * End:
389 * vim: expandtab shiftwidth=4: