ext4 vs fsync, my take

There has been a lot of discussion about the ext4 data loss issue, and I see a lot of misconceptions, both about why rename() is used and what guarantees POSIX gives. I’ll try to give the background, and then my opinion on the situation.

There are two basic ways to update a file. You can either truncate the old file and write the new contents, or you can write the new contents to a temporary file and rename it over the old file when finished. The rename method have several advantages, partly based on the fact that rename is atomic. The exact wording from POSIX (IEEE Std 1003.1TM, 2003 Edition) is:

In this case, a link named new shall remain visible to other processes throughout the renaming operation and refer either to the file referred to by new or old before the operation began.

This gives the rename method some useful properties:

  • If the application crashes while writing the new file, the original file is left in place
  • If an application reads the file the same time as someone is updating it the reading application gets either the old or the new file in its entirety. I.e. we will never read a partially finished file, a mixup of two files, or a missing file.
  • If two applications update the file at the same time we will at worst lose the changes from one of the writers, but never cause a corrupted file.

Note that nothing above talks about what happens in the case of a system crash. This I because system crashes are not specified at all by POSIX. In fact, the behaviour specified predates journaled filesystems where you have any reasonable expectation that recently written data is availible at all after a system crash. For instance, a traditional unix filesystem like UFS or ext2 may well lose the entire filesystem on a system crash if you’re unlucky, but it is still POSIX compliant.

In addition to the above POSIX specifies the “fsync” call, which can be used in the rename method. It flushes all in-memory buffers corresponding to the file onto hardware (this is vaguely specified and the exact behaviour is hw and sw dependent), not returning until its fully saved. If called on the new file before renaming it over the old file it gives a number of advantages:

  • If there is a hardware I/O error during the write to the disk we can detect and report this.
  • In case of a system crash shortly after the write, its more likely that we get the new file than the old file (for maximum chance of this you additionally need to fsync the directory the file is in)
  • Some filesystems may order the metadata writes such that the rename is written to disk, but the contents of the new file are not yet on disk. If we crash at this point this is detected on mount and the file is truncated to 0 bytes. Calling fsync() guarantees that this does not happen. [ext4]

However, it also has a number of disadvantages:

  • It forces a write immediately, spinning up the disk and causing more power use and more wear on flash filesystems.
  • It causes a longer wait for the user, waiting for data to be on disk.
  • It causes lower throughput if updating multiple files in a row.
  • Some filesystems guarantee ordering constraint such that fsync more or less implies a full sync of all outstanding buffers, which may cause system-wide performance issues. [ext3]

It should be noted that POSIX, and even ext4 gives no guarantees that the file will survive a system crash even if using fsync. For instance, the data could be outstanding in hardware buffers when the crash happens, or the filesystem in use may not be journaled or otherwise be robust wrt crashes. However, in case of a filesystem crash it gives a much better chance of getting the new data rather than the old, and on reordering filesystems like an unpatched ext4 it avoids truncated files from the rename method.

Both the fsync and the non-fsync version has their places. For very important data the guarantees given by fsync are important enough to outweight the disadvantages. But in many cases the disadvantages makes it too heavy to use, and the possible data loss is not as big of an issue (after all, system crashes are pretty uncommon).

So much for the background, now over to my personal opinions on filesystem behaviour. I think that in the default configuration all general purpose filesystem that claim to be robust (be it via journalling or whatever) should do their best to preserve the runtime guarantees of the atomic rename save operation so that they extend to the system crash case too. In other words, given a write to a new file followed by a rename over an old file, we shall find either the old data or the new data. This is a less of a requirement than fsync-on-close, but a requirement nevertheless that does result in a performance loss. However, just the fact that you’re running a journaled filesystem is a performance cost already, and something the user has explicitly chosen in order to have less risk of losing data.

It would be nice if the community could work out a way to express intent of the save operation to the filesystem in such a way that we avoid the unnecessary expensive fsync() call. For instance, we could add a fcntl like F_SETDATAORDERED that tells the kernel to ensure the data is written to the disk before writing the metadata for the file to the disk. With this in place applications could choose either if they want the new file on disk *now*, or just if it wants either the old or the new file, without risk for total data loss. (And fall back on fsync if the fcntl is not supported.)

This is the current status of the rename method on the commonly used Linux filesystems to my best knowledge:
(In this context “safe” means we get either the old or the new version of the file after a crash.)

ext2: No robustness guarantees on system crash at all.

ext3: In the default data=ordered mode it is safe, because data is written before metadata. If you crash before the data is written (5 seconds by default) you get the old data. With data=writeback mode it is unsafe.

ext4: Currently unsafe, with a quite long window where you risk data loss. With the patches queued for 2.6.30 it is safe.

btrfs: Currently unsafe, the maintainer claims that patches are queued for 2.6.30 to make it safe

XFS: Currently unsafe (as far as i can tell), however the truncate and overwrite method is safe.

Eternal Vigilance!

I’ve spent a lot of time during the years fixing nautilus memory use. I noticed the other day that it seemed to be using a lot of memory again, doing nothing but displaying the desktop:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND          
14315 alex      20   0  487m  46m  15m S  0.3  1.2   0:00.86 nautilus

So, its time for another round of de-bloating. I fired up massif to see what used so much memory, and it turns out that there is a cache in GnomeBG that caches the original desktop background image. We don’t really need that since we keep around the final pixmap for the background.

It turns out that my desktop image is 2560×1600, which means the unscaled pixbuf uses 12 megs of memory. Just fixing this makes things a bit better:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND          
16129 alex      20   0  538m  33m  15m S  4.9  0.8   0:00.87 nautilus

However, looking at the actual allocations in massif its obvious that we’re not actually using this much memory. For a short time when creating the desktop background pixmap we do several large temporary allocations, but these are quickly freed. So, it seems we’re suffering from the heap growing and then not being returned to the OS due to fragmentation.

It is ‘well known’ that glibc uses mmap for large (> 128k by default) allocations and that such allocations should be returned to the OS directly when freed. However, this doesn’t seem to happen for some reason. Lots of research follows…

It turns out that this isn’t true anymore, since about 2006. Glibc now uses a dynamic threshold for when to start using mmap for allocations. It uses the size of freed mmaped memory chunks to update the threshold, and this is causing problems for nautilus which has a behaviour where almost all allocations are small or medium sized, but there are a few large allocations when handling the desktop background. This is leading to several large temporary allocations going to the heap, never to be returned to the OS.

Enter mallopt(), with lets us set a static mmap limit. So, we set this back to the old value of 128k:

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND          
 4971 alex      20   0  479m  26m  15m S  0.0  0.7   0:00.90 nautilus

Not bad. Dropped 20 meg of resident size with a few hours of work. Now nautilus isn’t leading the pack anymore but comes after apps like gnome-panel, gnome-screensaver and gnome-settings-daemon.

How to remove flicker from Gtk+

In between spending time taking care of a sick kid, a sick wife and being sick myself I have slowly been working on the remaining issues in the client-side-windows branch of Gtk+. The initial and main interest in having client side windows is that it lets us emulate all thats needed for widgets to work without any server side windows, which lets us do things like put Gtk+ widgets inside clutter, etc. However another interesting, and not entierly obvious advantage of client side windows is that it allows us to remove flicker. This post will describe how this works and show the effects.

Gtk+ already does quite a lot of things to avoid flicker. For instance, all drawing in expose events is automatically double buffered so that you never see partially drawn results. The remaining flickering is related to the effect of moving or resizing server side subwindows. Although even these are minimized by Gtk+, since many widgets don’t use such windows or only use input-only windows which don’t cause any visual effects. However, there are still some areas where subwindows are used, mostly in cases where scrolling is involved.

Lets start with an example on how scrolling works:

Evince

This is a regular Evince window showing a pdf, and we want to scroll down. This happens in several steps. First we copy the bottom area of the window to the top of the window:

Evince 2

Then we mark the newly scrolled in area at the bottom as invalid:

Evince scrolling 3

As a result of this Gtk+ will call the application to redraw the invalid region as soon as it has finished handling the incomming events:

scrolling-4

Voila! We have scrolled. (In reality more happened above, the scrollbar area was marked invalid and repainted also, but lets ignore that for now.)

This example also makes it easy to see where flicker comes from. The drawing of the newly exposed area is double buffered, so the newly drawn area is replaced atomically, however the initial copy is not done with the Gtk+ drawing system, instead its done with a XCopyArea directly on the window (not a subwindow move, but with similar effect). So, the xserver will display that immediately, while there might be some delay before the expose of the scrolled in area is drawn causing visual tearing.

Another common problem is widget resizing/move that can be seen in my previous blog entry.  In this case what happens is that a widget with a subwindow is moved and/or resized and it ends up over another widget. The window move operation is done immediately in the server and results in a copy similar to the above, and then there is some delay before the widgets are redrawn to match that.

Now, client side windows don’t by itself fix this, but the copies above and all rendering is now under control of the client (i.e. the app) so we have the tools to do something about it. The solution is to delay the copying until we’re ready to draw everything that will be drawn, so we never show any partial results. Whenever some region of a window is copied we just record the area to be copied and by how much. When we’re handling the expose events for the invalid area we handle the expose up to the point of drawing everything in the double buffer. At this point we replay all the copies we recorded, except we don’t care about copying anything that will draw into the area which will be drawn by the expose. Then we blit out the final result of the expose event.

Furthermore, in practice it often happens that we do several moves/scrolls of the same region before it its drawn. This works with the above approach, but is a bit wasteful as some regions are copied twice. So, instead of just simply keeping track of all copies being made we try to combine such double copies into a single copy, thus minimizing the actual copies we make in the end.

So, how does this look in the end? Its kind of hard to capture this kind of flicker with a screengrabber, so here is a video I took with my phone:

[vimeo]http://www.vimeo.com/3148023[/vimeo]

Can you tell which one uses the standard Gtk+?

Flicker free Gtk+ continued

Remember my preview of subwindowless Gtk+? It got rid of some flicker, but it was still pretty raw.

I’ve been working on a new version of the subwindowless patch, and today I implemented a cool trick that gives fully flicker-free subwindow move/resize:

[videofile width=”500″ height=”400″]http://www.gnome.org/~alexl/noflicker2.ogg[/videofile]

This video is done with the same kind of increased X latency as the previous ones, but no flickering is detectable.

Gdb is dead. Long live Gdb!

All hail Archer and Tom Tromeys python gdb integration:

Before:


(gdb) bt
#0  nautilus_file_unref (file=0xdbbc60) at nautilus-file.c:724
#1  0x004a9e74 in nautilus_directory_async_state_changed (directory=0x7bf090) at nautilus-directory-async.c:4891
#2  0x004ae363 in nautilus_directory_call_when_ready_internal (directory=0x7bf090, file=0xdbbc60, file_attributes=3, wait_for_file_list=0,
directory_callback=<value optimized out>, file_callback=0x4c4b80 <file_list_file_ready_callback>, callback_data=0x9729c0) at nautilus-directory-async.c:1344
#3  0x004fcad1 in vfs_file_call_when_ready (file=0xc19768, file_attributes=0, callback=0, callback_data=<value optimized out>) at nautilus-vfs-file.c:68
#4  0x004c69e1 in nautilus_file_list_call_when_ready (file_list=<value optimized out>, attributes=3, handle=0x972880, callback=0x4eaa80 <activate_activation_uris_ready_callback>, callback_data=0x972830) at nautilus-file.c:6900
#5  0x0046f52a in fm_directory_view_activate_files (view=0xa07430, files=0x1109480, mode=NAUTILUS_WINDOW_OPEN_ACCORDING_TO_MODE, flags=0, confirm_multiple=1) at fm-directory-view.c:703
#6  0xf4fbf6fd in IA__g_closure_invoke (closure=0xa43740, returnvalue=0x0, n_param_values=2, param_values=0x110d950, invocation_hint=0x7fffffffcc10) at gclosure.c:767
#7  0xf4fd6760 in signal_emit_unlocked_R (node=0xa360b0, detail=0, instance=0x92d340, emission_return=0x0, instance_and_params=0x110d950) at gsignal.c:3244
#8  0xf4fd7e49 in IA__g_signal_emit_valist (instance=0x92d340, signal_id=<value optimized out>, detail=0, var_args=0x7fffffffcdf0) at gsignal.c:2977
#9  0xf4fd8383 in IA__g_signal_emit (instance=0xdbbc60, signal_id=12687208, detail=0) at gsignal.c:3034
#10 0x004d5405 in activate_selected_items (container=0x92d340) at nautilus-icon-container.c:6669
#11 0x0004df20d in item_event_callback (item=<value optimized out>, event=0x7fffffffd3b0, data=<value optimized out>) at nautilus-icon-container.c:6237
#12 0xf7dcde38 in eel_marshal_BOOLEAN__BOXED (closure=0xf0b7f0, returnvalue=0x7fffffffd130, n_param_values=<value optimized out>, param_values=0x1113d80,
invocation_hint=<value optimized out>, marshal_data=0x4decc0) at eel-marshal.c:121
#13 0xf4fbf6fd in IA__g_closure_invoke (closure=0xf0b7f0, returnvalue=0x7fffffffd130, n_param_values=2, param_values=0x1113d80, invocation_hint=0x7fffffffd0f0) at gclosure.c:767
#14 0xf4fd6760 in signal_emit_unlocked_R (node=0xa3ade0, detail=0, instance=0xd6ed00, emission_return=0x7fffffffd270, instance_and_params=0x1113d80) at gsignal.c:3244
#15 0xf4fd7cf8 in IA__g_signal_emit_valist (instance=0xd6ed00, signal_id=<value optimized out>, detail=0, var_args=0x7fffffffd2d0) at gsignal.c:2987
#16 0xf4fd8383 in IA__g_signal_emit (instance=0xdbbc60, signal_id=12687208, detail=0) at gsignal.c:3034
#17 0xf7dacca8 in emit_event (canvas=<value optimized out>, event=<value optimized out>) at eel-canvas.c:2518
#18 0x004dd749 in button_press_event (widget=0x92d340, event=0x100a100) at nautilus-icon-container.c:4183
#19 0xf67ac148 in _gtk_marshal_BOOLEAN__BOXED (closure=0x7ee870, returnvalue=0x7fffffffd6c0, n_param_values=<value optimized out>, param_values=0x112c0f0,
invocation_hint=<value optimized out>, marshal_data=0x4dd6b0) at gtkmarshalers.c:84
#20 0xf4fbf6fd in IA__g_closure_invoke (closure=0x7ee870, returnvalue=0x7fffffffd6c0, n_param_values=2, param_values=0x112c0f0, invocation_hint=0x7fffffffd680) at gclosure.c:767
#21 0x4fd6432 in signal_emit_unlocked_R (node=0x7eea00, detail=0, instance=0x92d340, emission_return=0x7fffffffd800, instance_and_params=0x112c0f0) at gsignal.c:3282
#22 0xf4fd7cf8 in IA__g_signal_emit_valist (instance=0x92d340, signal_id=<value optimized out>, detail=0, var_args=0x7fffffffd860) at gsignal.c:2987
#23 0xf4fd8383 in IA__g_signal_emit (instance=0xdbbc60, signal_id=12687208, detail=0) at gsignal.c:3034
#24 0xf68aec3e in gtk_widget_event_internal (widget=0x92d340, event=0x100a100) at gtkwidget.c:4745
...

After: (interesting areas marked out)

(gdb) gbt
#0  nautilus_file_unref (file=<NautilusVFSFile:0xdbbc60>) at nautilus-file.c:724
#1  0x004a9e74 in nautilus_directory_async_state_changed (directory=<NautilusVFSDirectory:0x7bf090>) at nautilus-directory-async.c:4891
#2  0x004ae363 in nautilus_directory_call_when_ready_internal (directory=<NautilusVFSDirectory:0x7bf090>, file=<NautilusVFSFile:0xdbbc60>, file_attributes=3, wait_for_file_list=0, directory_callback=<value optimized out>, file_callback=0x4c4b80 <file_list_file_ready_callback>, callback_data=0x9729c0) at nautilus-directory-async.c:1344
#3  0x004fcad1 in vfs_file_call_when_ready (file=0xc19768, file_attributes=0, callback=0, callback_data=<value optimized out>) at nautilus-vfs-file.c:68
#4  0x004c69e1 in nautilus_file_list_call_when_ready (file_list=<value optimized out>, attributes=3, handle=0x972880, callback=0x4eaa80 <activate_activation_uris_ready_callback>, callback_data=0x972830) at nautilus-file.c:6900
# Emit signal activate on instance <FMIconContainer:0x92d340>
#9  0xf4fd8383 in g_signal_emit (instance=0xdbbc60, signal_id=12687208, detail=0) at gsignal.c:3034
#10 0x004d5405 in activate_selected_items (container=<FMIconContainer:0x92d340>) at nautilus-icon-container.c:6669
#11 0x004df20d in item_event_callback (item=<value optimized out>, event=0x7fffffffd3b0, data=<value optimized out>) at nautilus-icon-container.c:6237
# Emit signal event on instance <NautilusIconCanvasItem:0xd6ed00>
#16 0xf4fd8383 in g_signal_emit (instance=0xdbbc60, signal_id=12687208, detail=0) at gsignal.c:3034
#17 0xf7dacca8 in emit_event (canvas=<value optimized out>, event=<value optimized out>) at eel-canvas.c:2518
#18 0x004dd749 in button_press_event (widget=<FMIconContainer:0x92d340>, event=0x100a100) at nautilus-icon-container.c:4183
# Emit signal button-press-event on instance <FMIconContainer:0x92d340>
#23 0xf4fd8383 in g_signal_emit (instance=0xdbbc60, signal_id=12687208, detail=0) at gsignal.c:3034
#24 0xf68aec3e in gtk_widget_event_internal (widget=<FMIconContainer:0x92d340>, event=0x100a100) at gtkwidget.c:4745
...

As a bonus, I throw in the break_finalize command:


(gdb) p file
$1 = (NautilusFile *) 0xdbbc60
(gdb) break_finalize file
Breakpoint 2 at 0x7ffff4fc3b00: file gobject.c, line 742.
(gdb) c
Continuing.
Breakpoint 2, g_object_finalize (object=0xdbbc60) at gobject.c:742
742     {

All code availible via git from http://www.gnome.org/~alexl/git/gnome-gdb.git. Needs latest version of the archer-tromey-python branch from Archer git.

If you have other cool gdb hacks please send them so we can collect them all in the same place.

A small preview of subwindowless Gtk+

Before:

[videofile width=”500″ height=”400″]http://www.gnome.org/~alexl/flicker.ogg[/videofile]

After:

[videofile width=”500″ height=”400″]http://www.gnome.org/~alexl/noflicker1.ogg[/videofile]

This code is still early in progress work, but it shows a lot of promise.

Note that the effect in the videos above is somewhat exagurated by doing two ssh forwards over the network so that i could screengrab the flicker, its not as obvious in plain local use.

Gtk can haz aliens too!

Towards a Ridley-based platform

Today I finally made the last changes to remove libgnome and its dependencies from nautilus. Its not in svn yet because some patches to gnome-desktop has to go in first.

Before:

ldd `which nautilus` | wc -l
91

After:

ldd `which nautilus` | wc -l
60

So, we are now linking to  30 libraries less! Libraries which we hardly used but still take time to load, initialize and resolve symbols from.

As a comparison, gtk-demo links to 37 libraries on my system. The additional libraries come from session management, thread use, dbus, gconf, libgnomedesktop and some other nautilus-specific features.

So, Project Ridley is alive and kicking (even if the wiki page is a bit outdated). I’m just waiting for dconf/GtkSettings to be finished and then we’ll really have a competitive next generation platform without all the old deprecated libgnome era stuff.

UPDATE: I’ve now commited this to trunk. You need new gnome-desktop, eel and nautilus

I’m back! (sorta)

After a long parental leave I’m now partially back to work. I’ll be working half-time until the end of the year.

I’ll try to get a sense of the status of the modules I work on, and I’m glad to help answer the list of questions people have built up over the last 6 months. I’m on email, and will hang out on irc around 9:00 to 15:00 CET.

Async I/O made “easy” using JavaScript

Writing code that does async I/O is a total pain in the ass. And I’ve written a whole lot of async I/O code, so I should know.

However, if you’re working in a language with full support for continuations it is a lot easier. JavaScript doesn’t support continuations fully, but it has generators, which is like a lite-version of continuations. That is part of the reason why I’m interested in JavaScript scripting.

So, I’ve been experimenting with  how to bind GIO-style async operations in JavaScript so that its easier to write async I/O code. I’ve got something going now that looks pretty ok.

Here is an example that calls an async function “$open” to read three files. It also demonstrates how to do calls from one async generator to another.

do_stuff = new AsyncRunner (function  (filenames) {
    for (let i in filenames) {
        var data = yield $open (filenames[i], 0);
        print ("data for " + filenames[i] + "  = " + data);
    }
    var ret = yield do_stuff2.call("other.txt");
    print ("AsyncRunner call returned: " + ret);
});
do_stuff2 = new AsyncRunner (function (filename) {
    var data = yield $open (filename, 0);
    print ("data for " + filename + " = " + data);
    yield "the end (of do_stuff2)"
});
do_stuff.start(null, ["test.txt", "test2.txt"]);

Its IMHO pretty easy to understand. At every async op we yield which suspends execution and restarts it at that point when we have the operation results. The “$” in “$open” is just to make it different from the sync variant and still short.

This code is far easier than it would look in C, where we would have to split out a new function at each async function call. The loop in do_stuff makes this quite tricky to do.

The code that implements this is here. Its somewhat complicated, using things like currying, closures and generators, so read it carefully.

I’d like some feedback on this. Are there any tricks I’ve missed that could make this nicer? I really wish javascript had a macro facility so that the yield keyword could be put inside the async calls and we could avoid the boilderplate around the AsyncRunner creation. I would also like to make the AsyncRunner objects callable like “do_stuff2()”, but that doesn’t seem possible with standard javascript (although it is the AsyncRunner class is implemented natively via the spidermonkey APIs…)

Embeddable languages, an implementation

I read Havocs post on embeddable languages with great joy. I very much agree that this is a good model for writing a larger application. I.E. you write the core of your application in C/C++ and then do the higher levels in an embedded dynamic scripting language. All the reasons Havoc lists are important, especially for the Gnome platform (a swarm-of-processes model where we want to avoid duplicating the platform in each language used).

There are a lot of dynamic scripting languages around, but only a few of them really qualify as embeddable languages in the form Havoc proposes. I would say the list is basically just: lua, JavaScript and some of the lisp dialects.

I’m far from a JavaScript lover, but it seems pretty obvious to me that from these alternatives it is the best choice. For a variety of reasons:

  • Its a modern dynamic language
    Its dynamically typed, it has precise gargabe collection, lambda functions, generators, array comprehension, etc
  • Its object oriented (sorta)
    We want to wrap the GObject type system which is object oriented, so this is important. The JavaScript prototype-based model is a bit weird, but its simple and it works.
  • It has no “platform” of its own
    If we inject the Gnome platform (Gtk+, Gio, etc) into JavaScript it doesn’t have to fight with any “native” versions of the same functionallity, and we won’t be duplicating anything causing bloat and conversions. (JS does have a Date object, but that is basically all.)
  • Lots of people know it, and if not there are great docs
    Chances of finding someone who knows JS is far higher than finding someone who knows e.g. lua or scheme, and having a large set of potential developers is important for a free software project.
  • Lots of activity around the language and implementations
    The language continually evolves, and there is a standards body trying to shephard this. There is also a multitude of competetive implementations, each trying to be best. This leads to things like the huge performance increases with TraceMoney and Google v8. Such performance work and focus is unlikely to happen in smaller and less used languages.

To experiment with this I started working on GScript (gscript module in gnome svn). It is two things. First of all its an API for easily embedding JavaScript (using SpiderMonkey atm) in a Gtk+ application. Secondly its a binding of GObject and GObject-based libraries to JavaScript.

Here is a quick example:

GScriptEngine *engine;
GScriptValue *res;
engine = g_script_engine_new ();
res = g_script_engine_evaluate_script (engine, "5+5");
g_print ("script result: %s", g_script_value_to_string (res));
g_object_unref (res);

If you want to expose a gobject (such as a GtkLabel) as a global property in JS you can do:

GtkLabel *label = gtk_label_new ("Test");
GScriptValue *val = g_script_value_new_from_gobject (G_OBJECT (label));
g_script_value_set_property (g_script_engine_get_global (engine), "the_label", val);

Then you can access the label from  javascript by the name “the_label”. You can read and set the object properties, connect and emit the signals and call all the methods that are availible via introspection.

You can also easily call from javascript into native code.

static GScriptValue *native_function (int n_args, GScriptValue **args);
GScriptValue *f = g_script_value_new_from_function (native_function, num_args);
g_script_value_set_property (g_script_engine_get_global (engine), "function", f);

The JavaScript wrappers are fully automatic, and lazily binds objects/classes as  they are used in JS. The object properties and signal information are extracted from the GType machinery. Method calls are done using the new GObject-Introspection system.

More work is clearly needed on the details of the JS bindings, but this is already a usable piece of code. I’m very interested in feedback about interest in something like this, and discussions about how the JS bindings should look.