Singletons: On their use and abuse.

In my last status report I mentioned a prototype for DecNums with a unique global context. The easiest way to implement global data as parrot PMCs is by using a 'singleton' PMC, so that is what I set out to do: a DecContext singleton dynpmc. Doing it was simple enough once I figured out what the creation mechanism was. There is one problem, however, once you know the mechanism you know its potential for abuse. This post is about what singletons should be, what they shouldn't be, and what I think should be done about it.

The implementation of the PMC started out easy enough: grab DecNum and rip out everything not directly dealing with contexts. Once I had that working, it was just a matter of writing "singleton" on the pmclass line, right? Not exactly.

There's more to being a singleton than that, you have to implement a specific interface, in particular: "void *get_pointer()" and "void set_pointer(void *)", and there was the first issue. What pointer?

Documentation on PMCs has very little to say about singletons, and the examples on the parrot code base can be somewhat misleading on what exactly is being getted and setted. It's implied, in a way, that it's supposed to be a pointer to the PMC itself, of which there can only be one[0].

If you want to really know what's going on here you have to look at the code. The key is in pmc.c in the get_new_pmc_header() function:

if (vtable_flags & VTABLE_PMC_IS_SINGLETON) {
/*
* singletons (monadic objects) exist only once
* the interface * with the class is:
* - get_pointer: return NULL or a pointer to the single instance
* - set_pointer: set the only instance once
*
* - singletons are created in the constant pmc pool
*/
PMC *pmc = (PMC *)(vtable->get_pointer)(interp, NULL);

/* LOCK */
if (!pmc) {
pmc = Parrot_gc_new_pmc_header(interp, PObj_constant_FLAG);
PARROT_ASSERT(pmc);

pmc->vtable = vtable;
VTABLE_set_pointer(interp, pmc, pmc);
}

return pmc;

There it is. You have to keep a pointer to yourself somewhere, initialized to NULL, and then when time comes to create you, parrot will set it for you. When someone tries to create another you, parrot will notice that you already exist and skip the creation of a new PMC, returning the old one. There can only be one, so parrot makes sure that it only creates one and nobody has to lose his head.

That's how you are meant to use singletons. Now let's see how you can abuse them. I noticed that you can write singletons that completely violate every promise that the singleton contract makes. I regard this as a bug and will open a Ticket about what I think should be done about it, but I still feel that all the wrong, contract-breaking behavior allowed right now needs to be documented somewhere. At least as a warning to implementers, of what can happen if they don't write their get_pointer/set_pointer properly.

The worst kind of violation happens if you always return NULL from get_pointer(), the code above makes this painfully clear, on a NULL pointer a new PMC header is allocated, returned and "There can only be one" goes straight out the window, this along with a do-nothing set_pointer() is a singleton PMC (Yes, it is a singleton: it says so right there in the pmclass declaration) that breaks all the guarantees of singleton PMCs. That's bad. Really.

I belive this behavior must be made impossible, the way I think it should be done is this: make pmc2c[1] emit a correct, contract-abiding getter-setter pair for a static PMC* named (after existing practice in the parrot core) ${name_of_your_PMC}_PMC, and don't expose it to anyone. This makes singletons DTRT out of the box without giving users a way to break the promises singletons make to the rest of the parrot PMC-ecology. This also makes singletons trivial to document from an end user perspective: "The same as other PMCs, except it says singleton on the pmclass declaration and you always get the same one when you 'new' it."

In the meantime, people that indulge in the sort of promise-breaking trickery described will be killed in their sleep. The assassins are being hired as I type. Let's see if you can fix your code before they get to you :)

[0] {Insert your own Highlander reference here.}
[1] Or it's replacement when time comes to ditch perl5

Singletons

Thanks for the short tutorial! I'm having a great time playing with this.

Cheers, online casino sites

Christopher.