Re: Possible to fix glaring Gjs API issues before GNOME 4?



On Thu, 2013-02-28 at 15:52 +0400, Nikita Churaev wrote:
On Thu, 2013-02-28 at 10:08 +0000, Maciej Piechotka wrote:
On Thu, 2013-02-28 at 09:26 +0100, Dan Winship wrote:
4. It's impossible to create custom Gtk.TreeIter from JS (no
constructor), so can't implement a completely custom Gtk.TreeModel.

I don't know the details of this particular issue, but if it's not
possible to do at all now, then any change to make it more bindable
could not possibly break any existing code, so it could happen at any
time.

IIRC the GtkTreeIter is assumed to not allocate memory. The API and ABI
would need to be altered in order to incorporate it as:

 - 3rd party needs to free GtkTreeIter if it uses one from arbitrary
model (possibly not so important) - i.e. there is API change and ABI.
 - All elements of class struct are already occupied so the size of
struct would need to be change - thus changing the the ABI.

I looked at the issue around Gtk+ 3.2 when someone asked for non-memory
allocating iterators for libgee (I cannot find the request right now but
making it bindable would solve this issue as well).

Best regards

struct GtkTreeIter {
  gint stamp;
  gpointer user_data;
  gpointer user_data2;
  gpointer user_data3;
};

There's a stamp member that models can put unique IDs in. We could allow
Gjs to create iterators with just stamps and leave user_data alone.


DISCLAIMER: Sorry - either I didn't understood you or you didn't
understood the problem. The following e-mail is written after studying
the problem for 5 minutes so it might include counterfactual
informations

That's not an issue - you can put even a pointer but the problem is that
you need to pin object. You don't know (automatically without user
intervention) when you can free the resource. At any point GTK might
keep GtkTreeIter alive so data you put inside cannot be freed (or
garbage collected or however you'll call it) and it need's to be always
considered 'alive'. I guess it is possible to workaround the issue by
reverse engineering the Gtk internals but solution would be very
unstable and not remotely elegant. 

In other words - if I have following code:

var iter = new MyIter() {...}
pass_iter_to_gtk(iter);
// Is iter alive? who knows - maybe. We need to know about the function
// This is half of problem - let's assume we can 'model' in gtk functon
iter = new MyIter() {...};
pass_iter_to_other_lib(iter);
// Can we release memory associated with iter?

====== After reading the GTK manual =====

I read into the Gtk section about iters. From GTK 3.2 reference manual
"The lifecycle of an iterator can be a little confusing at first.
Iterators are expected to always be valid for as long as the model is
unchanged (and doesn't emit a signal). The model is considered to own
all outstanding iterators and nothing needs to be done to free them from
the user's point of view. Additionally, some models guarantee that an
iterator is valid for as long as the node it refers to is valid (most
notably the GtkTreeStore andGtkListStore). Although generally
uninteresting, as one always has to allow for the case where iterators
do not persist beyond a signal, some very important performance
enhancements were made in the sort model. As a result, the
GTK_TREE_MODEL_ITERS_PERSIST flag was added to indicate this behavior."

I would say that the need is to subclass GtkTreeModel and use it.

Now the GtkTreeModel keeps all of the issued pointers in some sort of
data structure (say list). 

Then you can add handler to listen to changes and free them as needed
(Vala syntax + libgee usage as shameless self-promotion):

public interface GITreeIter : GLib.Object {
   ...
   void equals(MyTreeIter other);
}

public abstract MyTreeIter? gi_get_iter (Gtk.TreePath path);

public gboolean get_iter (out Gtk.TreeIter iter, Gtk.TreePath path) {
    GITreeIter *giter = gi_get_iter (path); // Wrapped HL object
    if (giter == null)
       return false;
    stored_iters.add(giter);
    iter.user_data = giter;
    return true;
}

this.row_changed.connect ((path, iter) => {
    for(Iterator<MyTreeIter *> iiter = stored_iters.iterator();
        iter.next();) {
        MyTreeIter *miter = iiter.get();
        if (iter.equals(miter)) {
            // Dereferences the item so in case of wrapper objects etc.
            // this is no longer a pinned object.
            delete citer; 
            iiter.delete();
        }
    }
});

It could be possibly optimized if the gi_get_iter was guranteed to
return always the same object for the same (logical) path - which would
push a bit of complexity back to user:

this.row_changed.connect ((path, iter) => {
    delete iter.user_data;
    stored_iters.remove(iter.user_data);
});


Other possible option, more efficient but potentially less user-friendly
(as user of library needs to manage it on its own) would be to cache the
iterators pointing to row at given node. Then when node is deleted and
signal is emitted the got deleted as well. EDIT: still it would be
possible to wrap it in class:

interface MyIter {
    ...
    bool next ();
    MyNode get_node ();
}

abstract class MyNode : Object {
    List<MyIter> iters = ...
}

bool iter_next (Gtk.TreeIter iter) {
    MyIter *miter = iter.user_data;
    miter->get_node ().iters.remove (miter);
    bool success = miter.next ()
    if (success) {
        miter->get_node ().iters.add(miter);
    }
    return success;
}

This of course works only if the iterators are not copied by Gtk and it
is not assumed that bit copy of iterator is valid copy. This actually is
not specified in documentation and I'm too lazy to ready GTK+ code to
verify. If this assumption is present then it causes havoc with most
encapsulation techniques as in effect you need to have (alternatively we
can just forget about notion of iterators and assume that iterators are
just nodes):

interface MyIter {
    ...
    MyIter? next ();
    MyNode get_node ();
}

But then you need to forget about most of fancy highier level libraries
(java.util etc.) which expose iterator and hide the actual
implementation.

Or you can just number the nodes by ever increasing 3*sizeof(void *) +
sizeof(int) [given todays computers we should be fine for next 10^50
years or so] and expose interface to user by numbers (IMHO it is not
exactly user-friendly and complicated from user POV but there were
systems which used similar techniques - like Windows or wxWidgets). This
also have problems with composability.

I am not a GUI programmer so I the next paragraph is written with
*total* *irresponsibility*. The problem IM layman's O with GtkTreeIter
is that it does mixes problem of resource management
(allocation/deallocation) and resources invalidation (signal - row
deletion) which IMHO does not play well with HL languages in which the
first issue is not under direct user control. I don't know the exact
requirements of GtkTreeModel so I cannot say if such behaviour is not
the best that can be (i.e. is bad but any other solution is worse).

Best regards

PS. Sorry for long e-mail

Attachment: signature.asc
Description: This is a digitally signed message part



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]