diff --git a/Makefile.am b/Makefile.am index f25fc02711..336fbd5a7a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -4444,6 +4444,7 @@ EXTRA_DIST += \ shared/nm-utils/c-list.h \ shared/nm-utils/gsystem-local-alloc.h \ shared/nm-utils/nm-glib.h \ + shared/nm-utils/nm-obj.h \ shared/nm-utils/nm-macros-internal.h \ shared/nm-utils/nm-shared-utils.c \ shared/nm-utils/nm-shared-utils.h \ diff --git a/shared/nm-utils/nm-obj.h b/shared/nm-utils/nm-obj.h new file mode 100644 index 0000000000..1a9d48681a --- /dev/null +++ b/shared/nm-utils/nm-obj.h @@ -0,0 +1,82 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* NetworkManager -- Network link manager + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * (C) Copyright 2017 Red Hat, Inc. + */ + +#ifndef __NM_OBJ_H__ +#define __NM_OBJ_H__ + +/*****************************************************************************/ + +#define NM_OBJ_REF_COUNT_STACKINIT (G_MAXINT) + +typedef struct _NMObjBaseInst NMObjBaseInst; +typedef struct _NMObjBaseClass NMObjBaseClass; + +struct _NMObjBaseInst { + /* The first field of NMObjBaseInst is compatible with GObject. + * Basically, NMObjBaseInst is an abstract base type of GTypeInstance. + * + * If you do it right, you may derive a type of NMObjBaseInst as a proper GTypeInstance. + * That involves allocating a GType for it, which can be inconvenient because + * a GType is dynamically created (and the class can no longer be immutable + * memory). + * + * Even if your implementation of NMObjBaseInst is not a full fledged GType(Instance), + * you still can use GTypeInstances in the same context as you can decide based on the + * NMObjBaseClass with what kind of object you are dealing with. + * + * Basically, the only thing NMObjBaseInst gives you is access to an + * NMObjBaseClass instance. + */ + union { + const NMObjBaseClass *klass; + GTypeInstance g_type_instance; + }; +}; + +struct _NMObjBaseClass { + /* NMObjBaseClass is the base class of all NMObjBaseInst implementations. + * Note that it is also an abstract super class of GTypeInstance, that means + * you may implement a NMObjBaseClass as a subtype of GTypeClass. + * + * For that to work, you must properly set the GTypeClass instance (and it's + * GType). + * + * Note that to implement a NMObjBaseClass that is *not* a GTypeClass, you wouldn't + * set the GType. Hence, this field is only useful for type implementations that actually + * extend GTypeClass. + * + * In a way it is wrong that NMObjBaseClass has the GType member, because it is + * a base class of GTypeClass and doesn't necessarily use the GType. However, + * it is here so that G_TYPE_CHECK_INSTANCE_TYPE() and friends work correctly + * on any NMObjectClass. That means, while not necessary, it is convenient that + * a NMObjBaseClass has all members of GTypeClass. + * Also note that usually you have only one instance of a certain type, so this + * wastes just a few bytes for the unneeded GType. + */ + union { + GType g_type; + GTypeClass g_type_class; + }; +}; + +/*****************************************************************************/ + +#endif /* __NM_OBJ_H__ */ diff --git a/src/platform/nmp-object.c b/src/platform/nmp-object.c index 2d77824bf9..4bf2b9f8fd 100644 --- a/src/platform/nmp-object.c +++ b/src/platform/nmp-object.c @@ -208,7 +208,7 @@ NMPObject * nmp_object_ref (NMPObject *obj) { g_return_val_if_fail (NMP_OBJECT_IS_VALID (obj), NULL); - g_return_val_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT, NULL); + g_return_val_if_fail (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT, NULL); obj->_ref_count++; return obj; @@ -219,7 +219,7 @@ nmp_object_unref (NMPObject *obj) { if (obj) { g_return_if_fail (obj->_ref_count > 0); - g_return_if_fail (obj->_ref_count != NMP_REF_COUNT_STACKINIT); + g_return_if_fail (obj->_ref_count != NM_OBJ_REF_COUNT_STACKINIT); if (--obj->_ref_count <= 0) { const NMPClass *klass = obj->_class; @@ -294,7 +294,7 @@ _nmp_object_stackinit_from_class (NMPObject *obj, const NMPClass *klass) memset (obj, 0, sizeof (NMPObject)); obj->_class = klass; - obj->_ref_count = NMP_REF_COUNT_STACKINIT; + obj->_ref_count = NM_OBJ_REF_COUNT_STACKINIT; return obj; } @@ -434,7 +434,7 @@ nmp_object_to_string (const NMPObject *obj, NMPObjectToStringMode to_string_mode return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", + "[%s,%p,%u,%ccache,%calive,%cvisible; %s]", klass->obj_type_name, obj, obj->_ref_count, obj->is_cached ? '+' : '-', nmp_object_is_alive (obj) ? '+' : '-', @@ -461,7 +461,7 @@ _vt_cmd_obj_to_string_link (const NMPObject *obj, NMPObjectToStringMode to_strin return klass->cmd_plobj_to_string_id (&obj->object, buf, buf_size); case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", + "[%s,%p,%u,%ccache,%calive,%cvisible,%cin-nl,%p; %s]", klass->obj_type_name, obj, obj->_ref_count, obj->is_cached ? '+' : '-', nmp_object_is_alive (obj) ? '+' : '-', @@ -502,7 +502,7 @@ _vt_cmd_obj_to_string_lnk_vlan (const NMPObject *obj, NMPObjectToStringMode to_s case NMP_OBJECT_TO_STRING_ALL: g_snprintf (buf, buf_size, - "[%s,%p,%d,%ccache,%calive,%cvisible; %s]", + "[%s,%p,%u,%ccache,%calive,%cvisible; %s]", klass->obj_type_name, obj, obj->_ref_count, obj->is_cached ? '+' : '-', nmp_object_is_alive (obj) ? '+' : '-', diff --git a/src/platform/nmp-object.h b/src/platform/nmp-object.h index fb7f713f31..a21d6fd192 100644 --- a/src/platform/nmp-object.h +++ b/src/platform/nmp-object.h @@ -21,6 +21,7 @@ #ifndef __NMP_OBJECT_H__ #define __NMP_OBJECT_H__ +#include "nm-utils/nm-obj.h" #include "nm-platform.h" #include "nm-multi-index.h" @@ -138,13 +139,14 @@ struct _NMPCacheId { }; typedef struct { + NMObjBaseClass parent; + const char *obj_type_name; + int sizeof_data; + int sizeof_public; NMPObjectType obj_type; int addr_family; int rtm_gettype; - int sizeof_data; - int sizeof_public; NMPlatformSignalIdType signal_type_id; - const char *obj_type_name; const char *signal_type; const guint8 *supported_cache_ids; @@ -266,8 +268,11 @@ typedef struct { } NMPObjectIP6Route; struct _NMPObject { - const NMPClass *_class; - int _ref_count; + union { + NMObjBaseInst parent; + const NMPClass *_class; + }; + guint _ref_count; bool is_cached; union { NMPlatformObject object; @@ -326,8 +331,6 @@ NMP_CLASS_IS_VALID (const NMPClass *klass) && ((((char *) klass) - ((char *) _nmp_classes)) % (sizeof (_nmp_classes[0]))) == 0; } -#define NMP_REF_COUNT_STACKINIT (G_MAXINT) - static inline NMPObject * NMP_OBJECT_UP_CAST(const NMPlatformObject *plobj) { @@ -358,7 +361,7 @@ NMP_OBJECT_IS_STACKINIT (const NMPObject *obj) { nm_assert (!obj || NMP_OBJECT_IS_VALID (obj)); - return obj && obj->_ref_count == NMP_REF_COUNT_STACKINIT; + return obj && obj->_ref_count == NM_OBJ_REF_COUNT_STACKINIT; } static inline const NMPClass * diff --git a/src/platform/tests/test-nmp-object.c b/src/platform/tests/test-nmp-object.c index 42dfc572e6..adc006e6f2 100644 --- a/src/platform/tests/test-nmp-object.c +++ b/src/platform/tests/test-nmp-object.c @@ -33,6 +33,56 @@ struct { /*****************************************************************************/ +static void +test_obj_base (void) +{ + static const GObject *g = NULL; + static const GTypeClass *k = NULL; + static const NMPObject *o = NULL; + static const NMPClass *c = NULL; + + NMObjBaseInst *obj; + gs_unref_object GCancellable *obj_cancellable = g_cancellable_new (); + nm_auto_nmpobj NMPObject *obj_link = nmp_object_new_link (10); + +#define STATIC_ASSERT(cond) \ + G_STMT_START { \ + G_STATIC_ASSERT (cond); \ + G_STATIC_ASSERT_EXPR (cond); \ + g_assert (cond); \ + } G_STMT_END + + STATIC_ASSERT (&g->g_type_instance == (void *) &o->_class); + STATIC_ASSERT (&g->g_type_instance.g_class == (void *) &o->_class); + STATIC_ASSERT (&g->ref_count == (void *) &o->_ref_count); + + STATIC_ASSERT (sizeof (o->parent) == sizeof (GTypeInstance)); + + STATIC_ASSERT (&c->parent == (void *) c); + STATIC_ASSERT (&c->parent.g_type_class == (void *) c); + STATIC_ASSERT (&c->parent.g_type == (void *) c); + STATIC_ASSERT (&c->parent.g_type == &k->g_type); + + STATIC_ASSERT (sizeof (c->parent) == sizeof (GTypeClass)); + + STATIC_ASSERT (&o->parent == (void *) o); + STATIC_ASSERT (&o->parent.klass == (void *) &o->_class); + + STATIC_ASSERT (G_STRUCT_OFFSET (NMPObject, _ref_count) == sizeof (NMObjBaseInst)); + STATIC_ASSERT (G_STRUCT_OFFSET (NMPClass, obj_type_name) == sizeof (NMObjBaseClass)); + + obj = (NMObjBaseInst *) obj_cancellable; + g_assert (!NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + + obj = (NMObjBaseInst *) obj_link; + g_assert (NMP_CLASS_IS_VALID ((NMPClass *) obj->klass)); + g_assert (!G_TYPE_CHECK_INSTANCE_TYPE (obj, G_TYPE_CANCELLABLE)); + +} + +/*****************************************************************************/ + static gboolean _nmp_object_id_equal (const NMPObject *a, const NMPObject *b) { @@ -429,6 +479,7 @@ main (int argc, char **argv) udev_enumerate_unref (enumerator); } + g_test_add_func ("/nmp-object/obj-base", test_obj_base); g_test_add_func ("/nmp-object/cache_link", test_cache_link); result = g_test_run ();