Upgrade to rv-split now!
If you’ve been hanging around the XMMS2 hype-sphere lately, you’ve certainly heard “wait for rv-split to be merged” almost as often as “it should be a service client”. Well, wait no more, for rv-split has made it into the -devel tree!
It means that all C clients are broken now, and possibly clients in other languages too. Yes, yours too. It needs fixing, and I’m going to explain how.
But, you ask, what is rv-split in the first place?
The result/value-split, AKA “rv-split” or sometimes just “rv”, is a change in the XMMS2 clientlib. Up until now, values returned by the server were kept inside the
structure that you got back from the call. The data was to be fetched directly from the result.
In sync mode, it went something like this:
const char *name;
result = xmmsc_playlist_current_active (conn);
xmmsc_result_wait (result);
if (xmmsc_result_iserror (result)) {
printf ("Server error: %s\n", xmmsc_result_get_error (result));
exit (1);
}
// The name is retrieved from the result
if (xmmsc_result_get_string (result, &name)) {
printf ("Active playlist is %s\n", name);
}
// We free the result, which also frees all the data it referenced,
// i.e. the ‘name’ string
xmmsc_result_unref (result);
The goal of rv-split was to isolate values outside of the result structure (for reasons that will become more obvious when we talk about the async calls). To do that, we introduced a new structure called
(for “xmms value”), that can contain any type of value. Accessor functions are used to extract each type from it, in a similar way to how it was done with result structures.
Let’s show the sync example again:
xmmsv_t *value;
const char *name, *errbuf;
result = xmmsc_playlist_current_active (conn);
xmmsc_result_wait (result);
value = xmmsc_result_get_value (result);
if (xmmsv_get_error (value, &errbuf)) {
printf ("Server error: %s\n", errbuf);
exit (1);
}
if (xmmsv_get_string (value, &name)) {
printf ("Active playlist is %s\n", name);
}
// We free the result, which also frees the value it contained,
// i.e. the ‘value’ variable, and the ‘name’ string
xmmsc_result_unref (result);
All we have to do is extract the
from the
. No raw data is stored in the result structure anymore.
Note that it also affects the error handling: if the server returns an error, it’s not a special state; it just returns an
of type
.
is used to retrieve the error message, or the function will return
if the value was not an error. Just like before, the value is freed automatically, so no need to worry about anything, just free the result structure.
Now, let’s move to the async case. In async, we don’t wait on results, we register one (or more) callback (AKA notifier) that will be called when the answer comes back from the server. Up until now, callbacks received the original result structure in argument (plus a userdata pointer). Like before, the result contained the value returned by the server. It could also be used to restart signals or disconnect broadcasts (more on that later).
In code:
callback (xmmsc_result_t *result, void *udata)
{
const char *name;
if (xmmsc_result_iserror (result)) {
printf ("Server error: %s\n", xmmsc_result_get_error (result));
exit (1);
}
// The name is retrieved from the result
if (xmmsc_result_get_string (result, &name)) {
printf ("Active playlist is %s\n", name);
}
// Each notifier holds a reference to the result
xmmsc_result_unref (result);
}
int
main ()
{
// [...]
xmmsc_result *result;
result = xmmsc_playlist_current_active (conn);
xmmsc_result_notifier_set (result, callback, NULL);
xmmsc_result_unref (result);
}
One of the arguments for the rv-split refactoring was that the result structure is meant to handle the command and how we treat it, but we don’t really need it to be passed to the callback. It allows developers to do weird stuff (wait on the result in the callback, restart a non-signal, etc — don’t try this at home).
All we need really is the value that the server returned. That is, the
!
Which gives:
callback (xmmsv_t *value, void *udata)
{
const char *name, *errbuf;
if (xmmsv_get_error (value, &errbuf)) {
printf ("Server error: %s\n", errbuf);
exit (1);
}
// The name is retrieved from the value
if (xmmsv_get_string (value, &name)) {
printf ("Active playlist is %s\n", name);
}
// Note that we do not need to unref anything here!
// The value will be freed automagically after this!
// I will explain this in a minute…
return FALSE;
}
int
main ()
{
// [...]
xmmsc_result *result;
result = xmmsc_playlist_current_active (conn);
xmmsc_result_notifier_set (result, callback, NULL);
xmmsc_result_unref (result);
}
Easy and safer!
But maybe you noticed a hitch: hey, how do I restart my signals or disconnect my broadcasts from the callback now?
Well, it’s even easier than before. Previously, here is how you restarted signals:
signal_callback (xmmsc_result_t *result, void *udata)
{
xmmsc_result_t *newres;
// [...]
newres = xmmsc_result_restart (result);
xmmsc_unref (result);
xmmsc_unref (newres);
}
This simply becomes:
signal_callback (xmmsv_t *value, void *udata)
{
// [...]
return TRUE;
}
That’s right, it’s that simple! The return value of a callback is some sort of “keep alive” flag. If a callback returns
, the signal will be restarted or the broadcast will keep going. If it returns
, the signal will die off (not restarted) or the broadcast will be disconnected.
Note that the return value has no meaning in the case of regular commands, like the earlier example.
Here are a couple of other things you might need to know about the new API:
- By default, the
xmmsv_t
is freed automatically (since it’s owned by the
xmmsc_result_t). If you want to keep it around for some reason, you can ref it using
xmmsv_ref, but don’t forget to unref it again when you’re done with
xmmsv_unref, else your client will leak!
- Dict and List accessors have been made more generic and sexy, so check the documentation to learn how to access these types!
- PropDicts (returned by
xmmsc_medialib_get_info
) have been replaced by dict-in-dicts, but you can convert them back to regular Dicts using the
xmmsv_propdict_to_dicthelper function. Again, use the Doc, Luke!
- Check how your favorite bindings handled the upgrade, the upgrade path might be softer depending on the language.
- The tutorials have been updated (or will be shortly as soon as my patch for #2018 is merged — you can check it in the meantime). It features some code working with propdicts and list iterators, and other things in general.
I think you should now be ready to upgrade your clients to rv-split! It’s been fun hacking on the new code this summer, and getting help from everyone to improve it and get it into -devel!
Feel free to ask questions on IRC or in the comments!
You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
Leave a Reply