RTBKit  0.9
Open-source framework to create real-time ad bidding systems.
soa/js/js_call.cc
00001 /* js_call.cc
00002    Jeremy Barnes, 15 November 2010
00003    Copyright (c) 2010 Datacratic.  All rights reserved.
00004 
00005    JS calling and notifier functionality.
00006 */
00007 
00008 #include "js_call.h"
00009 #include <unordered_map>
00010 #include "jml/arch/backtrace.h"
00011 #include "v8.h"
00012 #include "node.h"
00013 
00014 
00015 using namespace std;
00016 
00017 extern "C" {
00018     // Define as a weak symbol to avoid linker errors when linking without libeio
00019     __attribute__((__weak__))
00020     eio_req * eio_custom(void (*)(eio_req*), int, int (*)(eio_req*), void*)
00021     {
00022         throw ML::Exception("node needs to be linked in to use JS context callbacks");
00023     }
00024 };
00025 
00026 namespace node {
00027 
00028 // Define as a weak symbol to avoid linker errors when linking without node
00029 __attribute__((__weak__))
00030 void FatalException(v8::TryCatch & tc)
00031 {
00032     throw ML::Exception("node needs to be linked in to use JS context callbacks");
00033 }
00034 
00035 } // namespace node
00036 
00037 
00038 namespace Datacratic {
00039 namespace JS {
00040 
00041 /*****************************************************************************/
00042 /* CALL IN JS CONTEXT                                                        */
00043 /*****************************************************************************/
00044 
00045 struct CallInJsContextData {
00046     boost::function<void ()> callback;
00047     
00048     ~CallInJsContextData()
00049     {
00050     }
00051 };
00052     
00053 static void doNothing(eio_req * req)
00054 {
00055     // TODO: don't do this; find how to use libeio properly
00056 }
00057 
00058 static int doCallInJs(eio_req * req)
00059 {
00060     v8::HandleScope scope;
00061 
00062     auto_ptr<CallInJsContextData> data((CallInJsContextData *)req->data);
00063 
00064     v8::TryCatch try_catch;
00065 
00066     try {
00067         data->callback();
00068     } catch (const JSPassException & exc) {
00069         cerr << "got JSPassException" << endl;
00070         if (!try_catch.HasCaught()) {
00071             cerr << "handler returned passed exception " << endl;
00072             ML::backtrace();
00073             abort();
00074         }
00075         // Corner case... but probably shouldn't happen
00076     } catch (const std::exception & exc) {
00077         /* v8::Handle<v8::Value> result = */ translateCurrentException();
00078         // TODO: what do we do with result?
00079         if (!try_catch.HasCaught()) {
00080             cerr << "handler returned exception: " << exc.what() << endl;
00081             ML::backtrace();
00082             abort();
00083         }
00084     } catch (...) {
00085         /* v8::Handle<v8::Value> result = */ translateCurrentException();
00086         // TODO: what to do with result?
00087         if (!try_catch.HasCaught()) {
00088             cerr << "handler returned exception " << endl;
00089             ML::backtrace();
00090             abort();
00091         }
00092     }
00093         
00094     if (try_catch.HasCaught())
00095         node::FatalException(try_catch);
00096     
00097     return 0;
00098 }
00099 
00100 void callInJsThread(const boost::function<void ()> & fn)
00101 {
00102     // This is called from a thread that is probably not the right thread for
00103     // JS to be executed in.  Here we arrange for the right thread to be called
00104     // back by libev once the JS engine is ready to do something.
00105     
00106     auto_ptr<CallInJsContextData> data(new CallInJsContextData());
00107     data->callback = fn;
00108     eio_custom(doNothing, EIO_PRI_DEFAULT, doCallInJs, data.release());
00109 }
00110 
00111 boost::function<void ()>
00112 createCrossThreadCallback(v8::Handle<v8::Function> fn,
00113                           v8::Handle<v8::Object> This)
00114 {
00115     calltojsbase args(fn, This);
00116 
00117     auto onceInJsContext = [=] ()
00118         {
00119             v8::HandleScope scope;
00120             v8::Handle<v8::Value> result;
00121             {
00122                 v8::TryCatch tc;
00123                 result = args.params->fn->Call(args.params->This, 0, 0);
00124                 
00125                 if (result.IsEmpty()) {
00126                     if(tc.HasCaught()) {
00127                         tc.ReThrow();
00128                         throw JSPassException();
00129                     }
00130                     throw ML::Exception("didn't return anything");
00131                 }
00132             }
00133         };
00134 
00135     auto result = [=] ()
00136         {
00137             callInJsContext(onceInJsContext);
00138         };
00139 
00140     return result;
00141 }
00142 
00143 
00144 /*****************************************************************************/
00145 /* JS OPERATIONS                                                             */
00146 /*****************************************************************************/
00147 
00148 std::unordered_map<const std::type_info *, JSOperations> operations;
00149 
00150 void registerJsOps(const std::type_info & type,
00151                    JSOperations ops)
00152 {
00153     //cerr << "registered " << ML::demangle(type) << " at " << &type << " with " << &ops << endl;
00154     //ML::backtrace();
00155     
00156     operations[&type] = ops;
00157 }
00158 
00159 JSOperations
00160 getOps(const std::type_info & fntype)
00161 {
00162     auto it = operations.find(&fntype);
00163     if (it == operations.end())
00164         throw ML::Exception("no JS operations registered for "
00165                             + ML::demangle(fntype));
00166     return it->second;
00167 }
00168 
00169 } // namespace JS
00170 } // namespace Datacratic
 All Classes Namespaces Functions Variables Typedefs Enumerations Enumerator