[gjs: 1/2] object: Embed wrapped object list into ObjectInstance
- From: Philip Chimento <pchimento src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gjs: 1/2] object: Embed wrapped object list into ObjectInstance
- Date: Mon, 30 Apr 2018 04:56:56 +0000 (UTC)
commit 27f5405f6a48286ad70904c626a49ba52a40990c
Author: Carlos Garnacho <carlosg gnome org>
Date: Mon Apr 9 19:01:57 2018 +0200
object: Embed wrapped object list into ObjectInstance
Replace the usage of std::set by making ObjectInstance keep links to the
previous/next ObjectInstance. This makes insertion/removal O(1), while
keeping the O(n) behavior when objects are checked en masse, and reduces
memory overhead since we now need 2 pointers per object instead of
a full node in a R-B tree plus per-node malloc overhead.
https://gitlab.gnome.org/GNOME/gjs/issues/142
Closes: #142
gi/object.cpp | 127 +++++++++++++++++++++++++++++++++++++++++++++++++---------
1 file changed, 107 insertions(+), 20 deletions(-)
---
diff --git a/gi/object.cpp b/gi/object.cpp
index a927b645..6d4e1bfe 100644
--- a/gi/object.cpp
+++ b/gi/object.cpp
@@ -54,6 +54,65 @@
#include <util/log.h>
#include <girepository.h>
+typedef class GjsListLink GjsListLink;
+typedef struct ObjectInstance ObjectInstance;
+
+static GjsListLink* object_instance_get_link(ObjectInstance *priv);
+
+class GjsListLink {
+ private:
+ ObjectInstance *m_prev;
+ ObjectInstance *m_next;
+
+ public:
+ ObjectInstance* prev() {
+ return m_prev;
+ }
+
+ ObjectInstance* next() {
+ return m_next;
+ }
+
+ void prepend(ObjectInstance *this_instance,
+ ObjectInstance *head) {
+ GjsListLink *elem = object_instance_get_link(head);
+
+ g_assert(object_instance_get_link(this_instance) == this);
+
+ if (elem->m_prev) {
+ GjsListLink *prev = object_instance_get_link(elem->m_prev);
+ prev->m_next = this_instance;
+ this->m_prev = elem->m_prev;
+ }
+
+ elem->m_prev = this_instance;
+ this->m_next = head;
+ }
+
+ void unlink() {
+ if (m_prev)
+ object_instance_get_link(m_prev)->m_next = m_next;
+ if (m_next)
+ object_instance_get_link(m_next)->m_prev = m_prev;
+
+ m_prev = m_next = NULL;
+ }
+
+ int size() {
+ GjsListLink *elem = this;
+ int count = 0;
+
+ do {
+ count++;
+ if (!elem->m_next)
+ break;
+ elem = object_instance_get_link(elem->m_next);
+ } while (elem);
+
+ return count;
+ }
+};
+
struct ObjectInstance {
GIObjectInfo *info;
GObject *gobj; /* NULL if we are the prototype and not an instance */
@@ -68,6 +127,8 @@ struct ObjectInstance {
prototypes) */
GTypeClass *klass;
+ GjsListLink instance_link;
+
unsigned js_object_finalized : 1;
unsigned g_object_finalized : 1;
@@ -84,7 +145,7 @@ using ParamRefArray = std::vector<ParamRef>;
static std::unordered_map<GType, ParamRefArray> class_init_properties;
static bool weak_pointer_callback = false;
-static std::set<ObjectInstance *> wrapped_gobject_list;
+ObjectInstance *wrapped_gobject_list;
extern struct JSClass gjs_object_instance_class;
GJS_DEFINE_PRIV_FROM_JS(ObjectInstance, gjs_object_instance_class)
@@ -959,6 +1020,28 @@ object_instance_props_to_g_parameters(JSContext *context,
return true;
}
+static GjsListLink *
+object_instance_get_link(ObjectInstance *priv)
+{
+ return &priv->instance_link;
+}
+
+static void
+object_instance_unlink(ObjectInstance *priv)
+{
+ if (wrapped_gobject_list == priv)
+ wrapped_gobject_list = priv->instance_link.next();
+ priv->instance_link.unlink();
+}
+
+static void
+object_instance_link(ObjectInstance *priv)
+{
+ if (wrapped_gobject_list)
+ priv->instance_link.prepend(priv, wrapped_gobject_list);
+ wrapped_gobject_list = priv;
+}
+
static void
wrapped_gobj_dispose_notify(gpointer data,
GObject *where_the_object_was)
@@ -966,7 +1049,7 @@ wrapped_gobj_dispose_notify(gpointer data,
auto *priv = static_cast<ObjectInstance *>(data);
priv->g_object_finalized = true;
- wrapped_gobject_list.erase(priv);
+ object_instance_unlink(priv);
gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Wrapped GObject %p disposed",
where_the_object_was);
}
@@ -985,7 +1068,7 @@ gobj_no_longer_kept_alive_func(JS::HandleObject obj,
G_OBJECT_TYPE_NAME(priv->gobj));
priv->keep_alive.reset();
- wrapped_gobject_list.erase(priv);
+ object_instance_unlink(priv);
}
static void
@@ -1195,14 +1278,15 @@ gjs_object_prepare_shutdown(void)
* toggle ref removal -> gobj dispose -> toggle ref notify
* by emptying the toggle queue earlier in the shutdown sequence. */
std::vector<ObjectInstance *> to_be_released;
- for (auto iter = wrapped_gobject_list.begin(); iter != wrapped_gobject_list.end(); ) {
- ObjectInstance *priv = *iter;
- if (priv->keep_alive.rooted()) {
- to_be_released.push_back(priv);
- iter = wrapped_gobject_list.erase(iter);
- } else {
- iter++;
+ ObjectInstance *link = wrapped_gobject_list;
+ while (link) {
+ ObjectInstance *next = link->instance_link.next();
+ if (link->keep_alive.rooted()) {
+ to_be_released.push_back(link);
+ object_instance_unlink(link);
}
+
+ link = next;
}
for (ObjectInstance *priv : to_be_released)
release_native_object(priv);
@@ -1248,16 +1332,17 @@ update_heap_wrapper_weak_pointers(JSContext *cx,
{
gjs_debug_lifecycle(GJS_DEBUG_GOBJECT, "Weak pointer update callback, "
"%zu wrapped GObject(s) to examine",
- wrapped_gobject_list.size());
+ wrapped_gobject_list->instance_link.size());
std::vector<GObject *> to_be_disassociated;
+ ObjectInstance *priv = wrapped_gobject_list;
- for (auto iter = wrapped_gobject_list.begin(); iter != wrapped_gobject_list.end(); ) {
- ObjectInstance *priv = *iter;
- if (priv->keep_alive.rooted() || priv->keep_alive == nullptr ||
- !priv->keep_alive.update_after_gc()) {
- iter++;
- } else {
+ while (priv) {
+ ObjectInstance *next = priv->instance_link.next();
+
+ if (!priv->keep_alive.rooted() &&
+ priv->keep_alive != nullptr &&
+ priv->keep_alive.update_after_gc()) {
/* Ouch, the JS object is dead already. Disassociate the
* GObject and hope the GObject dies too. (Remove it from
* the weak pointer list first, since the disassociation
@@ -1268,8 +1353,10 @@ update_heap_wrapper_weak_pointers(JSContext *cx,
"%p (%s)", priv->keep_alive.get(), priv->gobj,
G_OBJECT_TYPE_NAME(priv->gobj));
to_be_disassociated.push_back(priv->gobj);
- iter = wrapped_gobject_list.erase(iter);
+ object_instance_unlink(priv);
}
+
+ priv = next;
}
for (GObject *gobj : to_be_disassociated)
@@ -1304,7 +1391,7 @@ associate_js_gobject (JSContext *context,
priv->keep_alive = object;
ensure_weak_pointer_callback(context);
- wrapped_gobject_list.insert(priv);
+ object_instance_link(priv);
g_object_weak_ref(gobj, wrapped_gobj_dispose_notify, priv);
}
@@ -1600,7 +1687,7 @@ object_instance_finalize(JSFreeOp *fop,
priv->keep_alive.reset();
}
- wrapped_gobject_list.erase(priv);
+ object_instance_unlink(priv);
if (priv->info) {
g_base_info_unref( (GIBaseInfo*) priv->info);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]