Singleton objects support (patch included)
This patch brings support for Singleton objects in vala. Patch properly handles multithreading as well as reference handling.
It adds attribute [Singleton] which makes it possible to write this
[Singleton]
public class TestSingleton : Object
{
construct {
}
}
Singleton object is obtained properly by both cases of access
TestSingleton a = new TestSingleton();
// and
TestSingleton b = Object.new (typeof(TestSingleton));
// No matter how many tries to create new object, they all point to the same reference
Reference counting allows for object to be dropped whenever this is wanted. As soon as all references are dropped from normal vala code, object instance is freed and on next access new singleton is created. If application or implementation needs permanent then this should be handled by either keeping one reference in Vala code or by calling ref(). This approach makes it really easy to manage memory
This code creates following code inside construct (complete thread-safe implementation)
static GObject *
test_singleton_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam * construct_properties)
{
GObject * obj;
GObjectClass * parent_class;
static GObject * __TestSingleton_instance = NULL;
static GMutex __TestSingleton_singleton;
static volatile gsize __TestSingleton_once = 0;
TestSingleton * self;
// Initializes mutex properly by using g_once_init_enter()
if (g_once_init_enter (&__TestSingleton_once) == TRUE) {
g_mutex_init (&__TestSingleton_singleton);
g_once_init_leave (&__TestSingleton_once, 42);
}
// Locks mutex and checks if instance is already available
g_mutex_lock (&__TestSingleton_singleton);
if (__TestSingleton_instance != NULL) {
// Unlocks mutex
g_mutex_unlock (&__TestSingleton_singleton);
// Adds one more reference to put it on the same count as generated vala code for any subsequent access
g_object_ref (__TestSingleton_instance);
// Returns same instance each time
return __TestSingleton_instance;
}
// If instance is not yet available, then new one is created normally
parent_class = G_OBJECT_CLASS (test_singleton_parent_class);
obj = parent_class->constructor (type, n_construct_properties, construct_properties);
self = G_TYPE_CHECK_INSTANCE_CAST (obj, TYPE_TEST_SINGLETON, TestSingleton);
// Assigns newly created object as class instance
__TestSingleton_instance = obj;
// Adds weak pointer for instance in case if singleton will need recreation when Vala drops it
g_object_add_weak_pointer (__TestSingleton_instance, (gpointer) &__TestSingleton_instance);
// Unlocks mutex
g_mutex_unlock (&__TestSingleton_singleton);**
return obj;
}
Implementations adds two compiler errors (as shown and tested in added test.vala).
One for invalid inheritance when some object tries to inherit from Singleton object.
Error output: test.vala:23.1-23.49: error: TestInheritanceError´ cannot inherit from Singleton class
TestSingleton´
[Singleton]
public class TestInheritanceError : TestSingleton
{
construct {
}
}
Another if object tries to pass Singleton on object which does not inherit from GObject (directly or indirectly) Error output: test.vala:14.1-14.31: error: Singleton class `TestNoGObjectError´ requires inheritance from GLib.Object
[Singleton]
public class TestNoGObjectError
{
}
Only current limitation is that construct {} must be present. I think proper solution would be patching different thing. Currently, GObject code generation runs through 2 different cases. One where construct {} is present and one without. I think proper solution would be redirecting to the same and pushing empty construct body when Object is inherited from GObject.