I want to call JSON.stringify method from the C++ side with modified arguments, but any solution that came to my mind results in a weird segfault with all frames being at "??".
I want to do the following:
api::Console is a custom console implementation for debugging purposes and has therefore static methods like api::Console::handleLog or api::Console::handleDebug.
For handleDebug which is passed correctly to the console's ObjectTemplate, the following doesn't work, v8gl::context is the current execution context and is correctly usable in other api implementations:
v8::Handle<v8::Value> Console::handleDebug(const v8::Arguments& args) {
if (args.Length() < 1) {
return v8::Undefined();
}
v8::HandleScope scope;
v8::Handle<v8::Object> global = v8gl::context->Global();
v8::Handle<v8::Object> JSON = global->Get(v8::String::New("JSON"))->ToObject();
v8::Handle<v8::Function> JSON_stringify = v8::Handle<v8::Function>::Cast(JSON->Get(v8::String::New("stringify")));
for (signed a = 0; a < args.Length(); a++) {
for (int m = 0; m < consoleMargin; m++) {
fprintf(stdout, "\t");
}
v8::Handle<v8::Value> passargs[1];
// alternative try was:
// passargs[0] = v8::String::New("{foo:'bar'}");
passargs[0] = v8::String::New(*v8::String::Utf8Value(args[a]->ToString()));
v8::String::Utf8Value value(JSON_stringify->Call(JSON, 1, passargs));
char* message = *value;
fprintf(stdout, "%s\n", message);
}
return scope.Close(v8::Undefined());
}
The backtrace in gdb is somehow weird and I have no idea why:
(gdb) backtrace
#0 0x00000000004a0880 in v8::Context::Global() ()
#1 0x00000000004128ea in api::Console::handleDebug(v8::Arguments const&) ()
#2 0x00000000004b9eab in v8::internal::Builtin_HandleApiCall(v8::internal::(anonymous namespace)::BuiltinArguments<(v8::internal::BuiltinExtraArguments)1>, v8::internal::Isolate*) ()
#3 0x000004cd67f0618e in ?? ()
#4 0x000004cd67f12998 in ?? ()
#5 0x000004cd67f060e1 in ?? ()
# (... etc ...)
So my question is the following:
How do I cast correctly from the Local value"v8::Arguments& args" to "v8::Handle<v8::Value>*" for usage with the Call() method of v8::Function?
If I want to use the args[a] directly in the loop, compiler errors are thrown for the differing signature of v8::Function::Call, which is correct due to args being a Local Value. The signature of v8::Function::Call is the following:
v8::Local<v8::Value> v8::Function::Call(v8::Handle<v8::Object>, int, v8::Handle <v8::Value>*)
// Edit: Updated passargs[false index]
You have error there:
v8::Handle<v8::Value> passargs[1];
passargs[1/* SHOULD BE 0!!!*/] =
v8::String::New(*v8::String::Utf8Value(args[a]->ToString()));
So you try to access element out of array bounds.
BTW: v8::Local<> is inherited from v8::Handle<> so you don't need any magic to convert Local to Handle.
Edit: Most of v8 features require not only v8::HandleScope but also v8::Context::Scope seems you need to create context scope too.
You can get valid context form args:
Local<Object> self = args.Holder();
Persistent<Context> context(self->CreationContext());
Then create handle scope and context scope:
Context::Scope work_in_context_scope(context);
HandleScope work_in_this_function_scope;
Then do you job.
Related
I am trying to call some native api from node js and perform long running task. I am using libuv for that perposes, store js callback to perform some meaningful task in separate thread and call callback later. This is what I've done so far:
task.cpp
struct Work {
uv_work_t request;
Local<Function> callback;
};
static void WorkAsyncComplete(uv_work_t* req, int status)
{
Isolate* isolate = Isolate::GetCurrent();
v8::HandleScope handleScope(isolate);
Work* work = static_cast<Work*>(req->data);
const unsigned argc = 1;
Local<Value> argv[argc] = { String::NewFromUtf8(isolate, "Hello world").ToLocalChecked() };
Local<Function> callback = Local<Function>::New(isolate, work->callback);
callback->Call(isolate->GetCurrentContext(), Null(isolate), argc, argv);
work->callback.Clear();
delete work;
}
static void WorkAsync(uv_work_t* req)
{
Work* work = static_cast<Work*>(req->data);
using namespace std::chrono_literals;
std::this_thread::sleep_for(std::chrono::seconds(5));
}
void RunCallback(const FunctionCallbackInfo<Value>& args)
{
Isolate* isolate = args.GetIsolate();
Work* work = new Work();
work->request.data = work;
Local<Function> callback = Local<Function>::Cast(args[1]);
work->callback.New(isolate, callback);
uv_queue_work(uv_default_loop(), &work->request, WorkAsync, WorkAsyncComplete);
args.GetReturnValue().Set(Undefined(isolate));
}
void Init(Local<Object> exports, Local<Object> module)
{
//isolate = exports->GetIsolate();
NODE_SET_METHOD(module, "exports", RunCallback);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Init)
task.js:
let task = require('./build/Release/task');
task((msg) => {
console.log(msg);
}
But I got crash in WorkAsyncComplete() when calling callback. Here is the error detail:
Debugger attached.
FATAL ERROR: v8::Function::Call Function to be called is a null pointer
1: 00007FF6579E03DF napi_wrap+109311
2: 00007FF657985096 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfElementsOffset+33302
3: 00007FF657985E66 node::OnFatalError+294
4: 00007FF65822A9BD v8::Function::Call+573
5: 00007FFA62FD1172 load_exe_hook+258
6: 00007FF657A37D90 uv_timer_stop+560
7: 00007FF657A37E67 uv_timer_stop+775
8: 00007FF657A3469B uv_async_send+331
9: 00007FF657A33E2C uv_loop_init+1292
10: 00007FF657A33FCA uv_run+202
11: 00007FF6579400A5 v8::internal::OrderedHashTable<v8::internal::OrderedHashSet,1>::NumberOfBucketsOffset+9365
12: 00007FF6579B3867 node::Start+311
13: 00007FF65781686C RC4_options+339820
14: 00007FF6587B523C v8::internal::compiler::RepresentationChanger::Uint32OverflowOperatorFor+153532
15: 00007FFA761A7C24 BaseThreadInitThunk+20
16: 00007FFA7694D4D1 RtlUserThreadStart+33
Please advice how to fix this issue. Thanks in advance.
Looks like the issue is that you're using a v8::Local<...> in a long-lived object. That doesn't work because the lifetime of a Local is tied to the surrounding HandleScope: the Local becomes invalid when the HandleScope dies. See https://v8.dev/docs/embed#handles-and-garbage-collection for details. The solution is to use a v8::Persistent<...> instead of a Local.
For the record, I'd also like to point out that while you can do C++ work in the background task, you won't be able to extend this example so that it takes a JavaScript function to execute concurrently. That would be multi-threaded programming, which JavaScript as a language doesn't support, and hence V8 as an implementation doesn't support either.
I have a class that has a bsoncxx::document::view view as a private attribute, and a method that get a document from the Mongo database and save the result in the local bsoncxx::document::value value variable and then get the view from the value.view() and save it in the class bsoncxx::document::view view variable:
const bool User::search_one_by_id(const std::string &id = "")
{
// The prototype of the method below: const std::tuple<bool, bsoncxx::document::value> search_one_by_id(mongocxx::collection &, const std::string &) const;
auto status = Crud::search_one_by_id(this->collection, id);
if (std::get<0>(status))
{
// Error
return EXIT_FAILURE;
}
else
{
// Success
bsoncxx::document::value value = std::get<1>(status);
bsoncxx::document::view view = value.view();
this->view = view;
return EXIT_SUCCESS;
}
}
The problem is that if I get some element from the view in the method above, i.e the code below before the return EXIT_SUCCESS, no errors are issued.
bsoncxx::document::element element = this->view["first_name"];
std::cout << "First Name: " << element.get_utf8().value.to_string();
But if I get a view, save it in the bsoncxx::document::view view variable and try to get some element from the view in another class method:
void get_element()
{
bsoncxx::document::element element = this->view["first_name"];
std::cout << "First Name: " << element.get_utf8().value.to_string();
}
I receive the error:
terminate called after throwing an instance of 'bsoncxx::v_noabi::exception'
what(): unset document::element
Makefile:26: recipe for target 'run' failed
make: *** [run] Aborted (core dumped)
I had tried to use a pointer to save the reference to the view that I get in the search_one_by_id method. I had check if the type of the attribute (first_name) I'm getting is the right type to get the value of the element. I had check if the attribute exist in the document. I had tried to use the release() method from the view in the User::search_one_by_id. I had check if the view is empty through:
if (this->view.empty())
{
std::cout << "Empty: " << this->view.empty();
}
else
{
std::cout << "Loaded: " << this->view.empty() << " length " << this->view.length();
}
Inside the get_element method, and the output is:
# If I comment the call to search_one_by_id
$ Empty: 1
# If I enable the call to search_one_by_id
$ Loaded: 0 length 129
The GDB log to backtrace:
#0 __GI_raise (sig=sig#entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51
#1 0x00007ffff6fd7801 in __GI_abort () at abort.c:79
#2 0x00007ffff762c957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#3 0x00007ffff7632ab6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#4 0x00007ffff7632af1 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#5 0x00007ffff7632d24 in __cxa_throw () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6
#6 0x00007ffff793985c in bsoncxx::v_noabi::document::element::type() const () from /usr/local/lib/libbsoncxx.so._noabi
#7 0x00007ffff7939abc in bsoncxx::v_noabi::document::element::get_utf8() const () from /usr/local/lib/libbsoncxx.so._noabi
#8 0x0000555555559353 in User::get_element() ()
#9 0x0000555555556668 in main ()
Some tips ? The project can be found on Github
References:
Unset Document::element, MongoCXX Find Option Projection
Document value
Document view
Document element
document::element::operator[] overloads should not throw when given an invalid element
How do I get the type of a variable?
i think you need to keep the pointer to buffer of value somewhere. view object is pointing to deleted memory i suppose. try something like this
class User
{
bsoncxx::document::value _value; <<< store at class level
};
const bool User::search_one_by_id(const std::string &id = "")
{
// The prototype of the method below: const std::tuple<bool, bsoncxx::document::value> search_one_by_id(mongocxx::collection &, const std::string &) const;
auto status = Crud::search_one_by_id(this->collection, id);
if (std::get<0>(status))
{
// Error
return EXIT_FAILURE;
}
else
{
// Success
_value = std::get<1>(status);
this->view = _value.view();
return EXIT_SUCCESS;
}
}
I am trying to save JavaScript function in my c++ app and call it from another thread.
But I got "Unhandled exception at 0x0101B5D5 in Console.exe: 0xC0000005: Access violation reading location 0x00000017."
Saving the JavaScript function (in requestCallbacks vector):
Handle<Value> HttpEngine::addListener(const Arguments& args)
{
Locker locker;
HandleScope scope;
HttpEngine* pThis = UnwrapHttpEngine( args.This() );
Persistent<Function> callback = Persistent<Function>::New(Handle<Function>::Cast(args[0]));
pThis->requestCallbacks.push_back(*callback);
return Boolean::New(true);
}
An attempt to call it from another thread:
void HttpEngine::emit()
{
Locker locker;
HandleScope scope;
for (size_t i = 0; i < requestCallbacks.size(); i++)
{
Persistent<Function> func = static_cast<Function*>(requestCallbacks[i]);
Handle<Value> args[1];
args[0] = v8::String::New("http://google.com");
func->Call(Context::GetCurrent()->Global(), 1, args);
}
}
JavaScript code (where the httpEngine is my c++ object in global scope):
httpEngine.addListener(function (url) {
print('on request: ' + url);
});
What's the type of requestCallbacks? Try putting the Persistent<Function> in there, not a raw Function* (using the * operator on a Persistent is both unsupported and most certainly not what you want to do).
My program seems to be crashing while inserting and int into a set, and I cannot track down the reason for this. Here is there relevant code:
bool request::check_list(std::vector<int> search_vec)
{
std::set<int> *tmp_ptr = create_int_set();
boost::shared_ptr<std::set<int> > c_list(tmp_ptr);
if(aerospike_query_foreach(as, &err, NULL, &query, process_set, &c_list) != AEROSPIKE_OK)
{
return false;
}
for(int i = 0; i < search_vec.size(); i++)
{
if(c_list->find(search_vec[i]) != c_list->end())
{
c_list_value_ = search_vec[i];
return true;
}
}
return false;
}
bool request::process_set(const as_val *val, void * udata)
{
try
{
boost::shared_ptr<std::set<int> > c_set = *(boost::shared_ptr<std::set<int> > *)(udata);
if(val == NULL)
{
return true;
}
if(val->type == AS_REC)
{
if (val!=NULL)
{
as_record *rec = as_record_fromval(val);
if (rec!=NULL)
{
as_integer* c_id = as_record_get_integer(rec,"c_id");
int cid = 0;
cid = boost::lexical_cast<int>(c_id->value);
if(c_set != nullptr)
{
c_set->insert(c_id);
}
as_record_destroy(rec);
as_integer_destroy(c_id);
}
}
return true;
}catch(...){}
return false;
}
The line c_set->insert(c_id); is causing a segfault. Here is this backtrace of the crash:
#0 0x00007f2064299f94 in std::_Rb_tree_rotate_right(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*&) () from /usr/lib64/libstdc++.so.6
#1 0x00007f206429a12b in std::_Rb_tree_insert_and_rebalance(bool, std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::_Rb_tree_node_base&) () from /usr/lib64/libstdc++.so.6
#2 0x00000000004829d9 in std::_Rb_tree<int, int, std::_Identity<int>, std::less<int>, std::allocator<int> >::_M_insert_<int const&> (this=0x7f1fcc005440, __x=0x0, __p=0x7f1f3c0009a0, __v=#0x7f20159e729c)
at /opt/centos/devtoolset-1.1/root/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_tree.h:981
#3 0x000000000047f1e0 in std::_Rb_tree<int, int, std::_Identity<int>, std::less<int>, std::allocator<int> >::_M_insert_unique<int const&> (this=0x7f1fcc005440, __v=#0x7f20159e729c)
at /opt/centos/devtoolset-1.1/root/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_tree.h:1299
#4 0x000000000047c473 in std::set<int, std::less<int>, std::allocator<int> >::insert (this=0x7f1fcc005440, __x=#0x7f20159e729c)
at /opt/centos/devtoolset-1.1/root/usr/lib/gcc/x86_64-redhat-linux/4.7.2/../../../../include/c++/4.7.2/bits/stl_set.h:415
#5 0x00000000004765ee in request::process_set (val=0x7f20159e73e0, udata=0x7f200b9d6620) at ../../request.cpp:1862
I am assuming there is a problem where the set is not being initialized, or something similar to that. Here is how I create and pass the set from the another function, I have tried two ways to create it:
boost::shared_ptr<std::set<int> > c_list(new std::set<int>());
and
std::set<int> *tmp_ptr = create_int_set();
boost::shared_ptr<std::set<int> > c_list(tmp_ptr);
std::set<int>* request::create_int_set()
{
return new std::set<int>();
}
The calling function is a callback function from a database driver, that takes in a few different objects, most notably however are the process_set and the c_list, which is passed as a void*:
aerospike_query_foreach(as, &err, NULL, &query, process_set, &c_list)
This crash does not happen all the time, in fact it is fairly rare, which makes me think there is something I am doing wrong, some sort of undefined behavior. Any help would be greatly appreciated!
The aerospike APi documentation says for the callback (i.e. process_set(), here):
Execute a query and call the callback function for each result item.
Multiple threads will likely be calling the callback in parallel.
Therefore, your callback implementation should be thread safe.
As several threads might insert at the same time in the same set (the one pointed to by your shared pointer), you'll get race conditions and hence undefined behaviour !
So I think that you should protect at least your set insertion block with a lock_guard on a mutex.
Important edit: boost::shared_ptr<> can't assumed to be thread safe. In the examples on boost.org, they suggest that a shared pointer going out of scope might cause a race. It may therefore be highly advisable to follow Matthew Moss's suggestion in the comments and use a raw pointer to the set within the bounds of process_set().
I am intrigued by how V8's scopes work.
How can a scope object on the stack find other scope objects and contexts further up the stack?
Digging into how HandleScopes worked I found that they rely on thread locals. This has left me wondering how these work in C++, I've found the implementation but still don't feel I understand what's going on.
api.cc -- HandleScope looks for the current Isolate
HandleScope::HandleScope() {
i::Isolate* isolate = i::Isolate::Current();
API_ENTRY_CHECK(isolate, "HandleScope::HandleScope");
v8::ImplementationUtilities::HandleScopeData* current =
isolate->handle_scope_data();
isolate_ = isolate;
prev_next_ = current->next;
prev_limit_ = current->limit;
is_closed_ = false;
current->level++;
}
isolate.cc -- static method looks for the current isolate as thread local
// Returns the isolate inside which the current thread is running.
INLINE(static Isolate* Current()) {
const Thread::LocalStorageKey key = isolate_key();
Isolate* isolate = reinterpret_cast<Isolate*>(
Thread::GetExistingThreadLocal(key));
if (!isolate) {
EnsureDefaultIsolate();
isolate = reinterpret_cast<Isolate*>(
Thread::GetExistingThreadLocal(key));
}
ASSERT(isolate != NULL);
return isolate;
}
platform.h -- calls a low level method to retrieve thread local
static inline void* GetExistingThreadLocal(LocalStorageKey key) {
void* result = reinterpret_cast<void*>(
InternalGetExistingThreadLocal(static_cast<intptr_t>(key)));
ASSERT(result == GetThreadLocal(key));
return result;
}
platform-tls-win32.h -- the magic happens
inline intptr_t InternalGetExistingThreadLocal(intptr_t index) {
const intptr_t kTibInlineTlsOffset = 0xE10;
const intptr_t kTibExtraTlsOffset = 0xF94;
const intptr_t kMaxInlineSlots = 64;
const intptr_t kMaxSlots = kMaxInlineSlots + 1024;
ASSERT(0 <= index && index < kMaxSlots);
if (index < kMaxInlineSlots) {
return static_cast<intptr_t>(__readfsdword(kTibInlineTlsOffset +
kPointerSize * index));
}
intptr_t extra = static_cast<intptr_t>(__readfsdword(kTibExtraTlsOffset));
ASSERT(extra != 0);
return *reinterpret_cast<intptr_t*>(extra +
kPointerSize * (index - kMaxInlineSlots));
}
How exactly is this last method working?
How does it know where to look?
What is the structure of the stack?
You can view InternalGetExistingThreadLocal as an inline version of TlsGetValue WinAPI call.
On Windows in user mode fs segment register allows code to access Thread Information Block (TIB) which contains thread specific information, for example Thread Local Storage structures.
Layout of TIB and the way TLS is stored inside TIB is exposed in DDK (see http://en.wikipedia.org/wiki/Win32_Thread_Information_Block for quick overview of the TIB layout).
Given this knowledge and ability to read data from TIB via __readfsdword(offs) (which is equivalent of reading dword ptr fs:[offs]) one can directly and efficiently access TLS without calling TlsGetValue.