Changeset 14547 for trunk/FACT++


Ignore:
Timestamp:
10/31/12 14:30:01 (12 years ago)
Author:
tbretz
Message:
Added a callback for the case when an event is received. To allow v8 interrupting the running JavaScript, waiting and sleeping have been ported to JavaScript, some fixed for crashes when the thread gets trminated during a call to Date::New
Location:
trunk/FACT++/src
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/FACT++/src/InterpreterV8.cc

    r14540 r14547  
    2121bool InterpreterV8::ReportException(TryCatch* try_catch)
    2222{
     23    if (!try_catch->CanContinue())
     24        return false;
     25
    2326    const HandleScope handle_scope;
    2427
     
    2730    if (*exception && string(*exception)=="exit")
    2831        return true;
     32    if (*exception && string(*exception)=="null")
     33        return false;
    2934
    3035    const Handle<Message> message = try_catch->Message();
     36    if (message.IsEmpty())
     37        return false;
    3138
    3239    // Print (filename):(line number): (message).
     
    3643
    3744    if (*filename)
    38         out << *filename;
    39     if (!message.IsEmpty())
    40         out << ": l." << message->GetLineNumber();
     45        out << *filename << ": ";
     46    out << "l." << message->GetLineNumber();
    4147    if (*exception)
    4248        out << ": " << *exception;
    4349
    4450    JsException(out.str());
    45 
    46     if (message.IsEmpty())
    47         return false;
    4851
    4952    // Print line of source code.
     
    103106    const bool rc = ExecuteStringNT(code, file);
    104107
    105     if (!exception.CanContinue())
    106         return false;
     108    // Check if this is a termination exception
     109    //if (!exception.CanContinue())
     110    //    return false;
    107111
    108112    if (exception.HasCaught())
     
    115119{
    116120    return ExecuteCode(String::New(code.c_str(), code.size()),
    117                          String::New(file.c_str()));
     121                       String::New(file.c_str()));
    118122}
    119123
     
    148152        return ThrowException(String::New("Argument 1 not a string."));
    149153
    150     if (!args[1]->IsInt32())
    151         return ThrowException(String::New("Argument 2 not an int32."));
     154    if (!args[1]->IsInt32() && !args[1]->IsString())
     155        return ThrowException(String::New("Argument 2 not an int32 and not a string."));
    152156
    153157    if (args.Length()==3 && !args[2]->IsUint32())
    154158        return ThrowException(String::New("Argument 3 not an uint32."));
    155159
     160    // Using a Javascript function has the advantage that it is fully
     161    // interruptable without the need of C++ code
     162
     163    const string index   = args[1]->IsInt32() ? "s.index" : "s.name";
     164    const bool   timeout = args.Length()==3;
     165    const string arg0    = *String::Utf8Value(args[0]);
     166    const string state   = args[1]->IsString() ? *String::Utf8Value(args[1]) : "";
     167    const string arg1    = args[1]->IsString() ? ("\""+state+"\"") : to_string(args[1]->Int32Value());
     168
     169    if (arg0.find_first_of("\"'")!=string::npos)
     170        return ThrowException(String::New("Server name must not contain quotation marks."));
     171
     172    if (args[1]->IsString())
     173        if (state.find_first_of("\"'")!=string::npos)
     174            return ThrowException(String::New("State name must not contain quotation marks."));
     175
     176    string code =  "(function(name,state,ms)"
     177                   "{";
     178    if (timeout)
     179        code +=       "var t = new Date();";
     180    code +=           "while (1)"
     181                      "{"
     182                         "var s = dim.state(name);"
     183                         "if(!"+index+")throw 'Waitig for state "+arg1+" of server "+arg0+" failed.';"
     184                         "if(state=="+index+")return true;";
     185    if (timeout)
     186        code +=          "if((new Date()-t)>ms)return false;";
     187
     188    code +=              "dim.sleep();"
     189                      "}"
     190                   "})('"+arg0+"',"+arg1;
     191    if (timeout)
     192        code +=    "," + to_string(args[2]->Int32Value());
     193    code +=        ");";
     194
     195    const HandleScope handle_scope;
     196
     197    // It is not strictly necessary to catch the exception, instead
     198    // script->Run() could just be returned, but catching the
     199    // exception allow to print the position in the file in
     200    // the exception handler instead of just the posiiton in the script.
     201    TryCatch exception;
     202
     203    const Handle<Script> script = Script::Compile(String::New(code.c_str()));
     204    const Handle<Value>  result = script->Run();
     205
     206    return exception.HasCaught() ? exception.ReThrow() : result;
     207
     208    /*
    156209    const string   server   = *String::Utf8Value(args[0]);
    157210    const  int32_t state    = args[1]->Int32Value();
     
    164217
    165218    return ThrowException(String::New(("Waitig for state "+to_string(state)+" of server '"+server+"' failed.").c_str()));
     219        */
    166220}
    167221
     
    193247    if (args.Length()==0)
    194248    {
    195         JsSleep(1);
     249        // Theoretically, the CPU usage can be reduced by maybe a factor
     250        // of four using a larger value, but this also means that the
     251        // JavaScript is locked for a longer time.
     252        usleep(1000);
    196253        return Undefined();
    197254    }
     
    203260        return ThrowException(String::New("Argument 1 must be an uint32."));
    204261
    205     JsSleep(args[0]->Int32Value());
    206 
    207     return Undefined();
     262    // Using a Javascript function has the advantage that it is fully
     263    // interruptable without the need of C++ code
     264
     265    const string code =
     266        "(function(){"
     267        "var t=new Date();"
     268        "while ((new Date()-t)<"+to_string(args[0]->Int32Value())+") dim.sleep();"
     269        "})();";
     270
     271    const HandleScope handle_scope;
     272
     273    const Handle<Script> script = Script::Compile(String::New(code.c_str()));
     274    return script->Run();
     275
     276    //JsSleep(args[0]->Int32Value());
     277    //return Undefined();
    208278}
    209279
     
    218288    // Return state.name/state.index
    219289
    220     HandleScope handle_scope;
    221 
    222290    const String::Utf8Value str(args[0]);
    223291
     
    227295    //    return Undefined();
    228296
     297    HandleScope handle_scope;
     298
     299    // It is important to catch the exception thrown
     300    // by Date::New in case of thread termination!
     301    Local<Value> date;
     302    {
     303        TryCatch exception;
     304        date = Date::New(rc.time.JavaDate());
     305        if (exception.HasCaught())
     306            return exception.ReThrow();
     307    }
     308
    229309    Handle<ObjectTemplate> obj = ObjectTemplate::New();
    230 
     310    obj->Set(String::New("time"),  date);
    231311    obj->Set(String::New("index"), rc.index<=-256?Undefined():Integer::New(rc.index),       ReadOnly);
    232312    obj->Set(String::New("name"),  rc.index<=-256?Undefined():String::New(rc.name.c_str()), ReadOnly);
    233     obj->Set(String::New("time"),  Date::New(rc.time.JavaDate()),                           ReadOnly);
    234313    //obj->Set(String::New("toString"),  String::New(("[Object state "+string(*str)+":"+to_string(rc.index)+"]").c_str()));
    235314
     
    239318Handle<Value> InterpreterV8::FuncExit(const Arguments &)
    240319{
    241     v8::V8::TerminateExecution(fThreadId);
     320    V8::TerminateExecution(fThreadId);
    242321    return ThrowException(String::New("exit"));
    243322/*
     
    306385Handle<Value> InterpreterV8::FuncInclude(const Arguments& args)
    307386{
    308     for (int i = 0; i<args.Length(); i++)
     387    for (int i=0; i<args.Length(); i++)
    309388    {
    310389        const HandleScope handle_scope;
     
    329408    HandleScope handle_scope;
    330409
    331     void *ptr = Local<External>::Cast(args.This()->GetInternalField(0))->Value();
     410    void *ptr = Handle<External>::Cast(args.This()->GetInternalField(0))->Value();
    332411    if (!ptr)
    333412        return Boolean::New(false);
     
    354433    HandleScope handle_scope;
    355434
    356     void *ptr = Local<External>::Cast(args.This()->GetInternalField(0))->Value();
     435    void *ptr = Handle<External>::Cast(args.This()->GetInternalField(0))->Value();
    357436    if (!ptr)
    358437        return Undefined();
     
    452531                }
    453532
     533                time_t date = 0;
    454534                if (sql_type.find("TIMESTAMP")!=string::npos)
     535                    date = mysqlpp::Time((*it)[i]);
     536
     537                if (sql_type.find("DATETIME")!=string::npos)
     538                    date = mysqlpp::DateTime((*it)[i]);
     539
     540                if (sql_type.find(" DATE ")!=string::npos)
     541                    date = mysqlpp::Date((*it)[i]);
     542
     543                if (date>0)
    455544                {
    456                     row->Set(name, Date::New(time_t(mysqlpp::Time((*it)[i]))*1000), ReadOnly);
    457                     continue;
     545                    // It is important to catch the exception thrown
     546                    // by Date::New in case of thread termination!
     547                    TryCatch exception;
     548                    Local<Value> val = Date::New(date*1000);
     549                    if (exception.HasCaught())
     550                        return exception.ReThrow();
     551                    //if (V8::IsExecutionTerminating())
     552                    //    return Undefined();
     553
     554                    row->Set(name, val, ReadOnly);
    458555                }
    459 
    460                 if (sql_type.find("DATETIME")!=string::npos)
    461                 {
    462                     row->Set(name, Date::New(time_t(mysqlpp::DateTime((*it)[i]))*1000), ReadOnly);
    463                     continue;
    464                 }
    465 
    466                 if (sql_type.find(" DATE ")!=string::npos)
    467                 {
    468                     row->Set(name, Date::New(time_t((mysqlpp::Date)(*it)[i])*1000), ReadOnly);
    469                     continue;
    470                 }
    471 
    472556            }
    473557
     
    562646Handle<Value> InterpreterV8::FuncClose(const Arguments &args)
    563647{
    564     HandleScope handle_scope;
    565 
    566     //const void *ptr = Local<External>::Cast(info.This()->GetInternalField(0))->Value();
     648    const HandleScope handle_scope;
     649
     650    //const void *ptr = Local<External>::Cast(args.Holder()->GetInternalField(0))->Value();
    567651
    568652    const String::Utf8Value str(args.Holder()->Get(String::New("name")));
     653
     654    const auto it = fReverseMap.find(*str);
     655    if (it!=fReverseMap.end())
     656    {
     657        it->second.Dispose();
     658        fReverseMap.erase(it);
     659    }
     660
    569661    return Boolean::New(JsUnsubscribe(*str));
    570662}
    571663
    572 Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
    573 {
    574     HandleScope handle_scope;
    575 
    576     const String::Utf8Value str(args.Holder()->Get(String::New("name")));
    577 
    578     const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
    579 
    580     const EventImp *evt = p.second;
    581     if (!evt)
    582         return Undefined();
    583 
    584     //if (counter==cnt)
    585     //    return info.Holder();//Holder()->Get(String::New("data"));
    586 
    587     const vector<Description> vec = JsDescription(*str);
     664Handle<Value> InterpreterV8::ConvertEvent(const EventImp *evt, uint64_t counter, const char *str)
     665{
     666    Local<Value> date;
     667
     668    // It is important to catch the exception thrown
     669    // by Date::New in case of thread termination!
     670    {
     671        TryCatch exception;
     672        date = Date::New(evt->GetJavaDate());
     673        if (exception.HasCaught())
     674            return exception.ReThrow();
     675    }
     676
     677    const vector<Description> vec = JsDescription(str);
    588678
    589679    Handle<Array> ret = Array::New();
     680    ret->Set(String::New("name"),    String::New(str),             ReadOnly);
    590681    ret->Set(String::New("format"),  String::New(evt->GetFormat().c_str()), ReadOnly);
    591     ret->Set(String::New("named"),   Boolean::New(vec.size()>0), ReadOnly);
    592     ret->Set(String::New("counter"), Integer::New(p.first), ReadOnly);
    593     ret->Set(String::New("time"),    Date::New(evt->GetJavaDate()), ReadOnly);
     682    ret->Set(String::New("named"),   Boolean::New(vec.size()>0),    ReadOnly);
    594683    ret->Set(String::New("qos"),     Integer::New(evt->GetQoS()),   ReadOnly);
    595684    ret->Set(String::New("size"),    Integer::New(evt->GetSize()),  ReadOnly);
     685    ret->Set(String::New("counter"), Integer::New(counter),         ReadOnly);
     686    ret->Set(String::New("time"),    date,                          ReadOnly);
    596687
    597688    typedef boost::char_separator<char> separator;
     
    609700        {
    610701            if (ptr>=evt->GetText())
    611                 return handle_scope.Close(ret);
     702                return ret;
    612703
    613704            char type = (*it)[0];
     
    618709
    619710            if (it==tok.end() && type!=':')
    620                 return ThrowException(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
     711                return Exception::Error(String::New(("Format string invalid '"+evt->GetFormat()+"'").c_str()));
    621712
    622713            string name = pos<vec.size() ? vec[pos].name : "";
     
    652743            ret->Set(String::New("data"), obj, ReadOnly);
    653744
    654         return handle_scope.Close(ret);
     745        return ret;
    655746    }
    656747    catch (...)
    657748    {
    658         return ThrowException(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
    659     }
     749        return Exception::Error(String::New(("Format string conversion '"+evt->GetFormat()+"' failed.").c_str()));
     750    }
     751}
     752
     753Handle<Value> InterpreterV8::FuncGetData(const Arguments &args)
     754{
     755    HandleScope handle_scope;
     756
     757    const String::Utf8Value str(args.Holder()->Get(String::New("name")));
     758
     759    const pair<uint64_t, EventImp *> p = JsGetEvent(*str);
     760
     761    const EventImp *evt = p.second;
     762    if (!evt)
     763        return Undefined();
     764
     765    //if (counter==cnt)
     766    //    return info.Holder();//Holder()->Get(String::New("data"));
     767
     768    Handle<Value> ret = ConvertEvent(evt, p.first, *str);
     769    return ret->IsNativeError() ? ThrowException(ret) : handle_scope.Close(ret);
     770}
     771
     772// This is a callback from the RemoteControl piping event handling
     773// to the java script ---> in test phase!
     774void InterpreterV8::JsHandleEvent(const EventImp &evt, uint64_t cnt, const string &service)
     775{
     776    if (fThreadId<0)
     777        return;
     778
     779    const auto it = fReverseMap.find(service);
     780    if (it==fReverseMap.end())
     781        return;
     782
     783    Locker locker;
     784
     785    const HandleScope handle_scope;
     786
     787    Handle<Object> obj = it->second;
     788    if (obj.IsEmpty())
     789        return;
     790
     791    const Handle<String> onchange = String::New("onchange");
     792    if (!obj->Has(onchange))
     793        return;
     794
     795    const Handle<Value> val = obj->Get(onchange);
     796    if (!val->IsFunction())
     797        return;
     798
     799    // -------------------------------------------------------------------
     800    // We are not in a context... we need to get into one for Array::New
     801
     802    Persistent<Context> context = Context::New();
     803    if (context.IsEmpty())
     804        return;
     805
     806    const Context::Scope scope(context);
     807
     808    // -------------------------------------------------------------------
     809
     810    TryCatch exception;
     811
     812    Handle<Value> ret = ConvertEvent(&evt, cnt, service.c_str());
     813    if (ret->IsArray())
     814    {
     815        Handle<Array> data = Handle<Array>::Cast(ret);
     816        Handle<Value> args[] = { data };
     817
     818        Handle<Function>::Cast(val)->Call(obj, 1, args);
     819    }
     820
     821    if (exception.HasCaught())
     822        ReportException(&exception);
     823
     824    if (ret->IsNativeError())
     825        JsException(service+".onchange callback - "+*String::Utf8Value(ret));
     826
     827    context.Dispose();
     828
     829    if (ret->IsUndefined() || ret->IsNativeError() || exception.HasCaught())
     830        V8::TerminateExecution(fThreadId);
    660831}
    661832
     
    695866    obj->SetInternalField(0, External::New(ptr));
    696867
     868    fReverseMap[*str] = Persistent<Object>::New(obj);
     869
    697870    return handle_scope.Close(obj);
    698871
     
    704877bool InterpreterV8::JsRun(const string &filename, const map<string, string> &map)
    705878{
    706     v8::Locker locker;
     879    Locker locker;
    707880    fThreadId = V8::GetCurrentThreadId();
    708881
     
    737910    }
    738911
    739     v8::Context::Scope scope(context);
    740 
    741     Local<Array> args = Array::New(map.size());
     912    Context::Scope scope(context);
     913
     914    Handle<Array> args = Array::New(map.size());
    742915    for (auto it=map.begin(); it!=map.end(); it++)
    743916        args->Set(String::New(it->first.c_str()), String::New(it->second.c_str()));
     
    748921
    749922    //context->Enter();
    750     v8::Locker::StartPreemption(10);
     923    Locker::StartPreemption(10);
    751924    const bool rc = ExecuteFile(filename);
    752925
     
    763936    // }
    764937
    765     v8::Locker::StopPreemption();
     938    Locker::StopPreemption();
    766939    //context->Exit();
     940
     941    for (auto it=fReverseMap.begin(); it!=fReverseMap.end(); it++)
     942        it->second.Dispose();
     943    fReverseMap.clear();
    767944
    768945    context.Dispose();
     
    774951#endif
    775952
    776 
    777953    JsEnd(filename);
    778954
     
    783959{
    784960    Locker locker;
     961
    785962    //cout << "Terminate " << fThreadId << endl;
    786963    if (This->fThreadId>=0)
    787964        V8::TerminateExecution(This->fThreadId);
    788965    //cout << "Terminate " << fThreadId << endl;
    789     Unlocker unlocker;
     966
     967    //Unlocker unlocker;
    790968}
    791969
    792970#endif
    793971
     972/*
     973#0  0x00007ffff5ae0e62 in ?? () from /usr/lib/libv8.so.3.7.12.22
     974#1  0x00007ffff5ae1969 in ?? () from /usr/lib/libv8.so.3.7.12.22
     975#2  0x00007ffff5af1976 in v8::V8::Dispose() () from /usr/lib/libv8.so.3.7.12.22
     976#3  0x0000000000426459 in RemoteControl<Console>::~RemoteControl() ()
     977#4  0x00007ffff51e9901 in __run_exit_handlers (status=3, listp=0x7ffff5566688, run_list_atexi
     978#5  0x00007ffff51e9985 in __GI_exit (status=<optimized out>) at exit.c:100
     979#6  0x00000000004374db in DimErrorRedirecter::exitHandler(int) ()
     980#7  0x00007ffff66bce72 in exit_user_routine () from /home/fact/FACT++.in-run-fad-loss/.libs/l
     981#8  0x00007ffff6477387 in recv_dns_dis_rout () from /home/fact/FACT++.in-run-fad-loss/.libs/l
     982#9  0x00007ffff647b79f in ast_read_h () from /home/fact/FACT++.in-run-fad-loss/.libs/libDim.s
     983#10 0x00007ffff647f388 in do_read () from /home/fact/FACT++.in-run-fad-loss/.libs/libDim.so
     984#11 0x00007ffff647ff32 in tcpip_task () from /home/fact/FACT++.in-run-fad-loss/.libs/libDim.s
     985#12 0x00007ffff6482c57 in dim_tcpip_thread () from /home/fact/FACT++.in-run-fad-loss/.libs/li
     986#13 0x00007ffff7bc4e9a in start_thread (arg=0x7ffff13be700) at pthread_create.c:308
     987#14 0x00007ffff52a1cbd in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:112
     988*/
  • trunk/FACT++/src/InterpreterV8.h

    r14540 r14547  
    2020    static InterpreterV8 *This;
    2121
     22    // The main thread id, needed to be able to terminate
     23    // the thread forcefully from 'the outside'
    2224    int fThreadId;
     25
     26    // A loookup table which allows to indentify the
     27    // the JavaScript object corrsponding to the
     28    // service name (for checking of an .onchange
     29    // subscription exists for that object)
     30    std::map<std::string, v8::Persistent<v8::Object>> fReverseMap;
    2331
    2432#ifdef HAVE_SQL
     
    6977
    7078    static v8::Handle<v8::Value> Convert(char type, const char* &ptr);
     79    v8::Handle<v8::Value> ConvertEvent(const EventImp *evt, uint64_t, const char *str);
    7180#endif
    7281
     
    93102    virtual void  JsException(const std::string &) { }
    94103    virtual bool  JsSend(const std::string &) { return true; }
    95     virtual void  JsSleep(uint32_t) { }
    96     virtual int   JsWait(const std::string &, int32_t, uint32_t) { return -1; };
     104    //virtual void  JsSleep(uint32_t) { }
     105    //virtual int   JsWait(const std::string &, int32_t, uint32_t) { return -1; };
    97106    virtual State JsState(const std::string &) { return State(); };
    98107    virtual void *JsSubscribe(const std::string &) { return 0; };
     
    100109    virtual std::vector<Description> JsDescription(const std::string &) { return std::vector<Description>(); };
    101110    virtual std::pair<uint64_t, EventImp *> JsGetEvent(const std::string &) { return std::make_pair(0, (EventImp*)0); };
     111
     112    void JsHandleEvent(const EventImp &, uint64_t, const std::string &);
    102113
    103114    bool JsRun(const std::string &, const std::map<std::string,std::string> & = std::map<std::string,std::string>());
Note: See TracChangeset for help on using the changeset viewer.