libseed-list threads and closures



On 08/04/2010 03:25 AM, Alan Knowles wrote:
Looks like the classic problem with scripted languages and threading...

You end up with the need to have two completely seperate scopes for each
thread, and work out some message passing API (JSON should be quite good
for this)

I don't believe that :) The JSC API clearly states that it should be threadsafe as long as each thread has each own context (which may belong to a common context_group to share data).

It seems that the problem is that JSGlobalContextRelease() is not threadsafe, it can not be called from anywhere except the mainthread. I guess this is due to the GC, which must not be run in other threads. Or perhaps it's a bug in JSC. (wonder what happens if the GC is triggered in another thread by some other action?)

I made a simple module that creates the context before it starts the thread, and when the thread ends it defers the cleanup to the mainloop. Seems to work fine! See attached code.

(I plan to expand this with functions to defer functions to the mainloop, and to handle locks and async queues (unless we fix the real GAsyncQueue binding) and stuff like that..)

Perhaps seed_handle_closure() should be modified to defer the JSGlobalContextRelease() to the mainloop like this, just in case the closure is another thread?

BTW: seed_handle_closure() makes a call to seed_prepare_global_context(), which is quite expensive since it parses Seed.js and stuff like that. Is this really needed? The closure will already be lexically bound where you defined the javascript function, right? At least this seems to be the case: in the attached module, I don't prepare the context, but the thread can still access outer variables.

/Jonatan

Looks like an extension might be the way to go on this one..

Regards
Alan

On Wednesday, August 04, 2010 05:18 AM, Jonatan Liljedahl wrote:
From the JSCore API docs:

"JS_EXPORT JSContextGroupRef JSContextGroupCreate()

A JSContextGroup associates JavaScript contexts with one another.
Contexts in the same group may share and exchange JavaScript objects.
Sharing and/or exchanging JavaScript objects between contexts in
different groups will produce undefined behavior. When objects from
the same context group are used in multiple threads, explicit
synchronization is required."

Of course script-side variables need to be explicitly locked if used
by more than one thread, but perhaps something need to have locks
around it also on the C-side?

/Jonatan

On 08/03/2010 09:12 PM, Jonatan Liljedahl wrote:
Commenting out JSGlobalContextRelease ((JSGlobalContextRef) ctx) at the
end of seed_handle_closure() in seed-closure.c makes the problem "go
away", but of course leaves the ctx in the land of forgotten pointers...

So, is the problem that the ctx is released before the thread exits, or
that it's release before any nested functions inside the thread finishes
(like callbacks passed to idle_add or timeout_add), or both?

If I understand correctly, the ctx *should* be alive as long as the
thread exists, since the ctx is created before the javascript thread
function is called and released after it has returned. But! I still get
a crash on JSGlobalContextRelease() with just a simple print() or "var x
= 2*34" in the thread. (no nested callbacks involved)

Also, I guess this means that seed_handle_closure() is called from the
thread and not from main? That brings the question, is
seed_handle_closure() threadsafe?

/Jonatan


On 08/02/2010 03:15 PM, Jonatan Liljedahl wrote:
Any ideas on this? I'm stuck.. Hoping someone with more insight in
closures and gobject-introspection might help.

/Jonatan

On 07/28/2010 04:06 PM, Jonatan Liljedahl wrote:
This simple code crashes (output and stacktrace at the end of this
mail). I don't see why it shouldn't work. GLib.idle_add() is the
recommended way to defer stuff from a thread to the mainloop and I've
used it several times in C code.

Seems to be a problem with closures, threads and the garbage
collector?

/////////// thread_bugs.js ////////////
var Gtk = imports.gi.Gtk;
Gtk.init(0,0);

var w = new Gtk.Window({border_width: 20});
var b = new Gtk.Button({label: "Foo"});
w.add(b);
w.show_all();

b.signal.clicked.connect(function() {
print("Clicked");
GLib.thread_create_full(function() {
print("Thread start");
var i=0;
var x=1.3;
for(i=0;i<9999999;i++) {
x *= 1.000004;
};
print(x);
print("Dispatch to mainloop");
GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,function() {
print("In mainloop");
b.label = "Bar"+x;
return false;
});
},null,0,true);
});

Gtk.main();
//////////// EOF /////////////
_______________________________________________
libseed-list mailing list
libseed-list gnome org
http://mail.gnome.org/mailman/listinfo/libseed-list

/*
this 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 3 of
the License, or (at your option) any later version.
 
Copyright (C) 2010 Jonatan Liljedahl <lijon kymatica com>

--------------------------------------------------------------------------------

Compile with:

    gcc -shared -fPIC seed-dispatch.c -I/usr/local/include/seed `pkg-config --cflags --libs glib-2.0 gmodule-2.0 gobject-introspection-1.0` -o libseed_dispatch.so
*/

#include <seed.h>
#include <glib.h>

SeedContextGroup context_group;

typedef struct {
    SeedContext ctx;
    SeedObject func;
    GThread *thread;
} ThreadData;

gboolean thread_cleanup(gpointer userdata) {
    ThreadData *data = (ThreadData*)userdata;
    seed_value_unprotect(data->ctx,(SeedValue)data->func);
    seed_context_unref(data->ctx);
    g_free(data);
    return FALSE;
}

gpointer thread_wrapper(gpointer userdata) {
    ThreadData *data = (ThreadData*)userdata;

    seed_object_call (data->ctx, data->func, NULL, 0, 0, NULL);
    
    g_idle_add(thread_cleanup, userdata);

    return NULL;
}

SeedValue
dispatch_background (SeedContext ctx,
                     SeedObject function,
                     SeedObject this_object,
                     size_t argument_count,
                     const SeedValue arguments[],
                     SeedException * exception)
{
    ThreadData *data = g_new0(ThreadData, 1);
    data->ctx = seed_context_create (context_group, NULL);
    data->func = (SeedObject) arguments[0];
    seed_value_protect(data->ctx,(SeedValue)data->func);

    g_thread_create(thread_wrapper, (gpointer) data, 1, NULL);

    return seed_value_from_int(ctx, 1, exception);
}


seed_static_function funcs[] = {
    {"background", dispatch_background, 0},
    {NULL, NULL, 0}
};

SeedObject
seed_module_init(SeedEngine * eng)
{
    SeedClass namespace_class;
    seed_class_definition namespace_class_definition = seed_empty_class;

    context_group = eng->group;

    namespace_class_definition.static_functions = funcs;
    namespace_class = seed_create_class (&namespace_class_definition);

    return seed_make_object (eng->context, namespace_class, NULL);
}


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