[Pidgin] #11431: Signals no longer work in Perl plugins
Pidgin
trac at pidgin.im
Fri Feb 26 05:03:14 EST 2010
#11431: Signals no longer work in Perl plugins
----------------------+-----------------------------------------------------
Reporter: dharding | Owner: MarkDoliner
Type: defect | Status: new
Milestone: 2.7.0 | Component: plugins
Version: 2.6.6 | Resolution:
Keywords: perl |
----------------------+-----------------------------------------------------
Comment(by dharding):
Replying to [comment:6 MarkDoliner]:
> dharding: Do you know what the difference is between the perl code and
purple_status_type_add_attrs_vargs() in libpurple/status.c? It's not
clear to me why the latter is ok but the perl function is not.
Here's my understanding of how things work on x86: on that platform a
va_list is just a pointer into the stack indicating the position of the
next argument. The va_arg macro dereferences that pointer to get the
value of that argument, then updates the pointer to point to the next
argument.
In libpurple/status.c, va_arg is invoked within a loop in a single
function that process all the arguments
(purple_status_set_active_with_attrs or
purple_status_type_add_attrs_vargs). In both of those cases, args is a
local variable that va_arg is able to update, so everything works fine.
In the Perl code, perl_signal_cb loops over the args, but for each one
calls purple_perl_sv_from_vargs, which is the function that actually calls
va_arg. Consider the case on x86 if a va_list (not a va_list*) is passed
to purple_perl_sv_from_vargs. When purple_perl_sv_from_vargs invokes the
va_arg macro, it updates the local args variable, but it doesn't update
the args variable in its caller, perl_signal_cb. Thus on each subsequent
iteration through the loop, perl_signal_cb will still pass a pointer to
the first argument to purple_perl_sv_from_vargs, and expected, all sorts
of weirdness ensues.
The naive solution is to pass a va_list* to purple_perl_sv_from_vargs, so
that the changes made by va_arg get seen by the caller, perl_signal_cb, so
that it can pass the updated pointer on subsequent iterations of the loop.
Unfortunately, as discussed on the mailing list in the link above and in
Ticket #7404, on some platforms, including x86-64, va_list is implemented
as an array, not a pointer. Because of the way C conflates arrays and
pointers, this causes problems when trying to take the address of a
va_list to pass it as a va_list* parameter.
There are three solutions I know of at this point. One are to use #ifdefs
based on whether va_list is an array or not. This is the change made to
address Ticket #7404, except that to be correct, it should have used
#ifdefs around the prototype for purple_perl_sv_from_vargs depending on
whether it was being passed a va_list or a va_list*, which would also mean
that purple_perl_sv_from_vargs would need #ifdefs internally whenever
referencing the args parameter, depending on whether it was a va_list or a
va_list*. In short, really messy, ugly code.
The second is to wrap the va_list in a struct, so that its address can be
taken in a consistent manner, whether it is a pointer or an array. This
results in much cleaner code than using #ifdefs, but requires making an
extra copy of the va_list at runtime (though this is not likely to have
any performance implications considering all the marshalling of arguments
that is being done between Pidgin and Perl).
The third solution would be to take the code from
purple_perl_sv_from_vargs and embed it in the loop in perl_signal_cb, so
that all the calls to va_arg are in a single function. This would work
because there are no other callers to purple_perl_sv_from_vargs, but it
would make code for perl_signal_cb much less clear.
--
Ticket URL: <http://developer.pidgin.im/ticket/11431#comment:7>
Pidgin <http://pidgin.im>
Pidgin
More information about the Tracker
mailing list