2010/3/2 Eddy Bruël <[email protected]> > 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.
No, you have to use the Locker class even if you are not using preemption. The locker does more than just lock it also informs V8 that you are switching stacks. > > > 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 > -- v8-users mailing list [email protected] http://groups.google.com/group/v8-users
