Please note that since context switching using swapcontext is
non-preemptive,
so race conditions should not occur.

This also implies that there should be no need for the use of the Locker
class in V8.

On Tue, Mar 2, 2010 at 3:09 PM, Erik Corry <[email protected]> wrote:

> 2010/3/2 ejpbruel <[email protected]>:
> > Hello,
> >
> > I am currently working on a proof of concept of using the stdlibc
> > makecontext/swapcontext functions in conjunction with v8. I know that
> > v8 is technically not thread safe, but since these functions allow the
> > explicit creation and scheduling of thread contexts, no race
> > conditions should occur, so it is at least conceivable that this
> > willwork.
>
> I'm afraid I don't think this will work.  V8 only works with Posix
> threads, and you have to use the Locker (and optionally Unlocker)
> classes from include/v8.h when switching threads.  See the comments in
> that file (not to be confused with the file src/v8.h).
>
> >
> > The control flow for the sample code below is as follows:
> > - main() creates a thread context which will call a central dispatch
> > loop (loop())
> > - main() initializes v8 with three function callbacks on the global
> > object (readAsync, read, and log)
> > - main() compiles and runs the script in str (defined at the beginning
> > of the script)
> >
> > - the evaluated script calls readAsync()
> > - readAsync() calls push() with readAysnc_helper() as argument
> > - push() creates a new thread context which will call
> > readAsync_helper()
> > - push() and readAsync() both return
> >
> > - the evaluated script calls read()
> > - read() calls read_helper() directly
> > - read_helper() needs to do a blocking call on fd 0 (stdin), so it
> > calls yield() with that fd as argument
> > - yield() registers the current context as being blocked
> > - yield() switches to the context for the central dispatch loop
> >
> > - loop() first handles any newly create contexts
> > - the context for readAsync_helper() is newly created, so loop()
> > switches to there
> > - readAsync() also calls read_helper()
> > - read_helper() needs to do a (potentially) blocking call on fd 3, so
> > it calls yield() with that fd as argument
> > - yield() registers the current context as being blocked
> > - yield() switches to the context for the central dispatch loop
> >
> > - no more newly created contexts are available, so loop() now handles
> > all blocking contexts
> > - loop() performs a select to see which fd's become available for
> > reading
> > - fd 3 becomes available first, so loop() switches to
> > readAsync_helper()
> > - readAsync_helper() does a blocking read (which will not block
> > because of the select)
> > - readAsync_helper() calls a function object which was stored earlier
> > (see readAsync())
> >
> > At this point, the callback does not seem to be called (if it were,
> > this should have resulted in a call to log()). Instead, V8 complains
> > to me:
> > Uncaught RangeError: Maximum call stack size exceeded
> >
> > The cause of this error is unclear to me, but I'm suspecting that it
> > is related to the fact that each thread context has a completely
> > separate stack (which I explicitly allocate using malloc()), and this
> > somehow confuses V8. What I need to know is why V8 gets confused over
> > this, and more importantly, what I need to do in order to be able to
> > do a callback to Javascript/call on a function object from a different
> > thread context than the one on which I initialized v8.
> >
> > As a sidenote, if I do *not* call the function object, the program
> > proceeds as expected. readAsync_helper() returns to the dispatch loop,
> > which will subsequently unblock and switch to read_helper() when the
> > user types something in the terminal (so that fd 0 becomes ready for
> > reading).
> >
> > Last but not least, here's the example code I used (note that a file
> > named test must be available in the same directory in order for it to
> > run correctly!):
> >
> > #include <ucontext.h>
> > #include <stdlib.h>
> > #include <fcntl.h>
> > #include <unistd.h>
> > #include <stdio.h>
> > #include <sys/select.h>
> > #include <v8-debug.h>
> >
> > using namespace v8;
> >
> > #define SIZE 1024
> >
> > const char str[] = "readAsync(function () { log('async ok') });"
> >                   "read(); log('sync ok');";
> >
> > ucontext_t uc; // Dispatch loop context
> > int nacs;
> > ucontext_t acs[10]; // Newly created contexts
> > int nfds;
> > fd_set fds;
> > ucontext_t ucs[10]; // Currently blocked context
> > Persistent<Context> context;
> > Persistent<Function> function;
> >
> > Handle<Value> log(const Arguments& args)
> > {
> >    String::Utf8Value value(args[0]);
> >
> >    printf("%s\n", *value);
> > }
> >
> > void yield(int fd)
> > {
> >    if (nfds < fd + 1)
> >        nfds = fd + 1;
> >    FD_SET(fd, &fds);
> >    swapcontext(&ucs[fd], &uc);
> >    FD_CLR(fd, &fds);
> >    do
> >        --fd;
> >    while (fd >= 0 && !FD_ISSET(fd, &fds));
> >    if (nfds > fd + 1)
> >        nfds = fd + 1;
> > }
> >
> > void read_helper(int fd)
> > {
> >    char data[SIZE];
> >    size_t size;
> >
> >    yield(fd);
> >    size = ::read(fd, data, SIZE);
> >    fwrite(data, size, 1, stdout);
> > }
> >
> > Handle<Value> read(const Arguments& args)
> > {
> >    read_helper(0);
> >    return Undefined();
> > }
> >
> > void readAsync_helper()
> > {
> >    int fd = open("test", O_RDONLY);
> >
> >    read_helper(fd);
> >    function->Call(function, 0, 0);
> > }
> >
> > void push(void (*func)(void))
> > {
> >    ucontext_t *ucp = &acs[nacs++];
> >
> >    getcontext(ucp);
> >  ucp->uc_link = &uc;
> >    ucp->uc_stack.ss_sp = malloc(SIGSTKSZ);
> >    ucp->uc_stack.ss_size = SIGSTKSZ;
> >    makecontext(ucp, func, 0);
> > }
> >
> > Handle<Value> readAsync(const Arguments& args)
> > {
> >    function =
> > Persistent<Function>::New(Handle<Function>::Cast(args[0]));
> >
> >    push(readAsync_helper);
> >    return Undefined();
> > }
> >
> > void loop()
> > {
> >    fd_set readfds;
> >    int n, i;
> >
> >    for (;;) {
> >        while (nacs > 0)
> >            swapcontext(&uc, &acs[--nacs]);
> >        readfds = fds;
> >        n = select(nfds, &readfds, 0, 0, 0);
> >        i = 0;
> >        while (n--) {
> >            while (!FD_ISSET(i, &readfds))
> >                ++i;
> >            swapcontext(&uc, &ucs[i]);
> >        }
> >    }
> > }
> >
> > int main(int argc, char** argv)
> > {
> >    HandleScope scope;
> >    Handle<ObjectTemplate> global;
> >
> >    getcontext(&uc);
> >    uc.uc_link = NULL;
> >    uc.uc_stack.ss_sp = malloc(SIGSTKSZ);
> >    uc.uc_stack.ss_size = SIGSTKSZ;
> >    makecontext(&uc, (void (*)(void)) loop, 0);
> >    global = ObjectTemplate::New();
> >    global->Set(String::New("readAsync"),
> > FunctionTemplate::New(readAsync));
> >    global->Set(String::New("read"), FunctionTemplate::New(read));
> >    global->Set(String::New("log"), FunctionTemplate::New(log));
> >    context = Context::New(0, global);
> >    {
> >        Context::Scope scope(context);
> >        Handle<String> source = String::New(str);
> >        Handle<Script> script = Script::Compile(source);
> >
> >        script->Run();
> >    }
> >    context.Dispose();
> >    return 0;
> > }
> >
> > --
> > v8-users mailing list
> > [email protected]
> > http://groups.google.com/group/v8-users
> >
>
> --
> v8-users mailing list
> [email protected]
> http://groups.google.com/group/v8-users

-- 
v8-users mailing list
[email protected]
http://groups.google.com/group/v8-users

Reply via email to