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
