Using Google's v8 C++ library, I have tried creating a v8::UniquePersistent instance on the heap which holds an internal pointer to some memory that was allocated in C++, and called SetWeak() on the Persistent to specify a callback, which would do the necessary cleanup by deallocating the memory that the internal pointer in the Persistent refers to when that handle is no longer needed by the javascript engine. Although the creation of the Persistent seems to work, I am finding that my callback function is never getting invoked.
I am completely stumped here. Any ideas what might I be doing wrong?
The callback is invoked only when the object is garbage collected. Try this example:
void callback(v8::WeakCallbackData<v8::Object, int> data)
{
Local<Object> val = data.GetValue();
int* ptr = reinterpret_cast<int*>(val->GetAlignedPointerFromINternalField(0));
delete ptr;
fprintf(stdout, "Deleted internal object!\n");
}
void main()
{
using namespace v8;
// Enable garbage collection
const char* flags = "--expose_gc";
v8::V8::SetFlagsFromStrings(flags, strlen(flags));
Isolate isolate = Isolate::New();
isolate->Enter();
HandleScope scope(isolate);
Eternal<v8::Context> context;
Local<ObjectTemplate> ctx_template ObjectTemplate::New(isolate);
context.Set(isolate, Context::New(isolate, nullptr, ctx_template);
Local<ObjectTemplate> obj_template = ObjectTemplate::New(isolate);
obj_template->SetInternalFieldCount(1);
Local<Object> obj = Local<Object>(isolate, obj_template->NewInstance());
obj->SetAlignedPointerInInternalField(0, new int);
UniquePersistent<Object> persistent(isolate, obj);
persistent->SetWeak(nullptr, callback);
//...do something with persistent
persistent.Reset();
isolate->RequestGarbageCollectionForTesting(v8::Isolate::kFullGarbageCollection);
// You should see output from the callback
// i need to add more words to pass edit word num check...
}
Related
I am not completely sure how to best title this question since I am not completely sure what the nature of the problem actually is (I guess "how fix segfault" is not a good title).
The situation is, I have written this code:
template <typename T> class LatchedSubscriber {
private:
ros::Subscriber sub;
std::shared_ptr<T> last_received_msg;
std::shared_ptr<std::mutex> mutex;
int test;
void callback(T msg) {
std::shared_ptr<std::mutex> thread_local_mutex = mutex;
std::shared_ptr<T> thread_local_msg = last_received_msg;
if (!thread_local_mutex) {
ROS_INFO("Mutex pointer is null in callback");
}
if (!thread_local_msg) {
ROS_INFO("lrm: pointer is null in callback");
}
ROS_INFO("Test is %d", test);
std::lock_guard<std::mutex> guard(*thread_local_mutex);
*thread_local_msg = msg;
}
public:
LatchedSubscriber() {
last_received_msg = std::make_shared<T>();
mutex = std::make_shared<std::mutex>();
test = 42;
if (!mutex) {
ROS_INFO("Mutex pointer is null in constructor");
}
else {
ROS_INFO("Mutex pointer is not null in constructor");
}
}
void start(ros::NodeHandle &nh, const std::string &topic) {
sub = nh.subscribe(topic, 1000, &LatchedSubscriber<T>::callback, this);
}
T get_last_msg() {
std::lock_guard<std::mutex> guard(*mutex);
return *last_received_msg;
}
};
Essentially what it is doing is subscribing to a topic (channel), meaning that a callback function is called each time a message arrives. The job of this class is to store the last received message so the user of the class can always access it.
In the constructor I allocate a shared_ptr to the message and for a mutex to synchronize access to this message. The reason for using heap memory here is so the LatchedSubscriber can be copied and the same latched message can still be read. (the Subscriber already implements this kind of behavior where copying it doesn't do anything except for the fact that the callback stops being called once the last instance goes out of scope).
The problem is basically that the code segfaults. I am pretty sure the reason for this is that my shared pointers become null in the callback function, despite not being null in the constructor.
The ROS_INFO calls print:
Mutex pointer is not null in constructor
Mutex pointer is null in callback
lrm: pointer is null in callback
Test is 42
I don't understand how this can happen. I guess I have either misunderstood something about shared pointers, ros topic subscriptions, or both.
Things I have done:
At first I had the subscribe call happening in the constructor. I think giving the this pointer to another thread before the constructor has returned can be bad, so I moved this into a start function which is called after the object has been constructed.
There are many aspects to the thread safety of shared_ptrs it seems. At first I used mutex and last_received_msg directly in the callback. Now I have copied them into local variables hoping this would help. But it doesn't seem to make a difference.
I have added a local integer variable. I can read the integer I assigned to this variable in the constructor from the callback. Just a sanity check to make sure that the callback is actually called on an instance created by my constructor.
I think I have figured out the problem.
When subscribing I am passing the this pointer to the subscribe function along with the callback. If the LatchedSubscriber is ever copied and the original deleted, that this pointer becomes invalid, but the sub still exists so the callback keeps being called.
I didn't think this happened anywhere in my code, but the LatcedSubscriber was stored as a member inside an object which was owned by a unique pointer. It looks like make_unique might be doing some copying internally? In any case it is wrong to use the this pointer for the callback.
I ended up doing the following instead
void start(ros::NodeHandle &nh, const std::string &topic) {
auto l_mutex = mutex;
auto l_last_received_msg = last_received_msg;
boost::function<void(const T)> callback =
[l_mutex, l_last_received_msg](const T msg) {
std::lock_guard<std::mutex> guard(*l_mutex);
*l_last_received_msg = msg;
};
sub = nh.subscribe<T>(topic, 1000, callback);
}
This way copies of the two smart pointers are used with the callback instead.
Assigning the closure to a variable of type boost::function<void(const T)> seems to be necessary. Probably due to the way the subscribe function is.
This appears to have fixed the issue. I might also move the subscription into the constructor again and get rid of the start method.
Until now I've only implemented synchronous node-addon-api methods, i.e., a JavaScript function makes a call, work is done, and the addon returns. I have big gaps in knowledge when it comes to the inner workings of v8, libuv, and node, so please correct any obvious misconceptions.
The goal is to call a JavaScript callback when C++ garbage collection callbacks are called from v8. I originally just called the JavaScript callback from the v8 garbage collection callback but that ended up with a segv after a couple calls. It seems that just making a call into JavaScript while being called from a v8 callback has some problems (v8 docs the callbacks shouldn't allocate objects). So I looked around and found a Nan-based example that uses libuv and Nan's AsyncResource to make the callback. The following approach works using node-nan:
NAN_GC_CALLBACK(afterGC) {
uint64_t et = uv_hrtime() - gcStartTime;
// other bookkeeping for GCData_t raw.
if (doCallbacks) {
uv_async_t* async = new uv_async_t;
GCData_t* data = new GCData_t;
*data = raw;
data->gcTime = et;
async->data = data;
uv_async_init(uv_default_loop(), async, asyncCB);
uv_async_send(async);
}
}
class GCResponseResource : public Nan::AsyncResource {
public:
GCResponseResource(Local<Function> callback_)
: Nan::AsyncResource("nan:gcstats.DeferredCallback") {
callback.Reset(callback_);
}
~GCResponseResource() {
callback.Reset();
}
Nan::Persistent<Function> callback;
};
static GCResponseResource* asyncResource;
static void closeCB(uv_handle_t *handle) {
delete handle;
}
static void asyncCB(uv_async_t *handle) {
Nan::HandleScope scope;
GCData_t* data = static_cast<GCData_t*>(handle->data);
Local<Object> obj = Nan::New<Object>();
Nan::Set(obj, Nan::New("gcCount").ToLocalChecked(),
Nan::New<Number>((data->gcCount));
Nan::Set(obj, Nan::New("gcTime").ToLocalChecked(),
Nan::New<Number>(data->gcTime));
Local<Object> counts = Nan::New<v8::Object>();
for (int i = 0; i < maxTypeCount; i++) {
if (data->typeCounts[i] != 0) {
Nan::Set(counts, i, Nan::New<Number>(data->typeCounts[i]));
}
}
Nan::Set(obj, Nan::New("gcTypeCounts").ToLocalChecked(), counts);
Local<Value> arguments[] = {obj};
Local<Function> callback = Nan::New(asyncResource->callback);
v8::Local<v8::Object> target = Nan::New<v8::Object>();
asyncResource->runInAsyncScope(target, callback, 1, arguments);
delete data;
uv_close((uv_handle_t*) handle, closeCB);
}
My question is how would I do this using the node-addon-api instead of nan?
It's not clear to me what the node-addon-api equivalent of uv_async_init, uv_async_send, etc are. This is partially because it's not clear to me what underlying N-API (as opposed to node-addon-api) functions are required.
I have been unable to find an example like this. The callback example is completely synchronous. The async pi example uses a worker thread to perform a task but that seems overkill compared to the approach in the nan-based code using the uv primitives.
Your example is not really asynchronous, because the GC callbacks run in the main thread. However when the JS world is stopped because of the GC, this does not mean that it is stopped in a way allowing a callback to run - as the GC can stop it in the middle of a function.
You need a ThreadSafeFunction to do this. Look here for an example:
https://github.com/nodejs/node-addon-api/blob/main/doc/threadsafe_function.md
(I'm very unsure about the phrasing of the question title. I'm hoping it's not misleading because I really don't know how to summarize this. But I'll try to explain my problem as well as I can.)
In a project, there is something like this (written from memory and simplified):
Class A {
private:
boost::weak_ptr<SomeClassB> b;
public:
static boost::shared_ptr<SomeClassB> StopSomeProcesses () {
boost::shared_ptr<SomeClassB> temp (new SomeClassB());
b = temp;
return temp;
}
}
Now in another project, I need to do something similar to the following:
boost::shared_ptr<SomeClassB> obj;
void someFunction () {
obj = A::StopSomeProcesses();
auto callback = [](){
//some other stuff here
obj.reset();
};
NamespaceFromYetAnotherProject::DoSomething(callback);
}
What this basically does is while b holds a valid object from A::StopSomeProcesses, as the name implies, some processes will be stopped. In this case, the processes are stopped while DoSomething is executed. At the end, DoSomething will call callback where obj is reset and the stopped processes can now finally continue.
I've done this and it works. However, as much as possible, I'd like to avoid using global variables. I tried doing the following:
void someFunction () {
boost::shared_ptr<SomeClassB> obj;
obj = A::StopSomeProcesses();
auto callback = [&obj](){
//some other stuff here
obj.reset();
};
NamespaceFromYetAnotherProject::DoSomething(callback);
}
The above code works. But I'm not sure if I was already in "undefined behavior" territory and just got lucky. Doesn't obj's scope end already? Or does the fact that the lambda was passed as an argument help extend its "life"? If this is safe to do, is that safety lost if callback is run on another thread?
I also tried doing this:
void someFunction () {
boost::shared_ptr<SomeClassB> obj;
obj = A::StopSomeProcesses();
auto callback = [obj](){
//some other stuff here
boost::shared_ptr<SomeClassB> tempObj (new SomeClassB(*obj));
tempObj.reset();
};
NamespaceFromYetAnotherProject::DoSomething(callback);
}
But this was something I tried randomly. I wrote it while completely focused on just deleting the object held by the shared pointer. It worked, but I'm not even sure if it's just roundabout or even valid.
Are these attempts going anywhere? Or am I completely going the wrong way? Or should I just stick to using a global variable? Would appreciate any help on how to go about this problem. Thanks!
You are using a shared_ptr and StopSomeProcesses will internally allocate the memory it points to. Pointers are passed by value so the lifetime of obj is irelevant. Every function call makes a new copy of it as does the binding in the lambda. What matters is what the pointer points too and that was allocated with new and lives on.
My program has a callback function which is called to handle notifications that are received in the form of objects. Because we can handle hundreds a second, this callback function handles the events by spawning a separate thread to handle each one. This is the callback function:
void _OnEvent(LPCTSTR eventID, CNotification cNotificaton) {
if (_pActiveDoc) {
Param_Event* p = new Param_Event;
p->pDoc = _pActiveDoc;
p->lpszEventID = eventID;
p->cNotification = cNotification;
AfxBeginThread(ProcessEvent,p);
}
}
My query comes from the fact that is passed to the callback method is initially created on the stack, and is therefore (according to my understanding) limited to the scope of the calling method:
void CallingMethod(CString strEventID) {
CNotification cNotification;
// Fill in the details of the notification
_OnEvent(strEventID,cNotification);
}
CNotification has a full copy constructor, and since the Param_Event object is created on the heap, my belief was that this would allow the original CNotification object to fall out of scope safely, with the spawned thread working from its own "private" CNotification object that exists until the Param_Event object is deleted with delete. The fact is, however, that we are getting (rare but occasional) crashing, and I am wondering if perhaps my belief here is incorrect: is it possible that the spawned thread is still accessing the original object somehow? If this was the case, this would explain the crashing by the rare occurrence of the object both falling out of scope and being overwritten in memory, thus creating a memory access exception.
Could I be right? Is there anything actually wrong with the method I am using? Would it be safer create the notification object on the heap initially (this would mean changing a lot of our code), or building a new object on the heap to pass to the spawned thread?
For reference, here is my ProcessEvent() method:
Param_TelephoneEvent *p = (Param_TelephoneEvent*)lParam;
p->pDoc->OnTelephoneEvent(p->lpszEventID,p->cNotification);
delete p;
return 0;
All advice welcome. Thanks in advance!
Edit: Copy constructor:
CNotification& CNotification::operator=(const CNotification &rhs)
{
m_eamspeMostRecentEvent = rhs.m_eamspeMostRecentEvent;
m_eamtcsCallStatusAtEvent = rhs.m_eamtcsCallStatusAtEvent;
m_bInbound = rhs.m_bInbound;
strcpy(m_tcExtension , rhs.m_tcExtension);
strcpy(m_tcNumber, rhs.m_tcNumber);
strcpy(m_tcName,rhs.m_tcName);
strcpy(m_tcDDI,rhs.m_tcDDI);
strcpy(m_tcCallID,rhs.m_tcCallID);
strcpy(m_tcInterTelEvent,rhs.m_tcInterTelEvent);
m_dTimestamp = rhs.m_dTimestamp;
m_dStartTime = rhs.m_dStartTime;
m_nCallID = rhs.m_nCallID;
return *this;
}
I've decided to start learning C++ lately; coming from scripting background (Ruby, JavaScript, PHP). I've started building a very simple observer. What I am trying to achieve, is a custom callback for notify method as shown below:
#include <iostream>
#include <functional>
using namespace std;
template<class StateT>
class Observer{
typedef function<void(StateT)> LambdaT;
LambdaT callback;
~Observer(){
//delete callback;
};
public:
void setCallback(LambdaT &callback)
{
this->callback = callback;
};
void notify(StateT state){
this->callback(state);
};
};
int main(int argc, char** argv) {
Observer<string>* obs = new Observer<string>;
std::function<void(string)> customCallback = [](string state){
cout << state << endl;
};
obs->setCallback(customCallback);
obs->notify("State has changed");
return 0;
}
Then I have a Subject to observe, and when Subject instance is being destroyed, all of it's attached observers are being destroyed too, in order to free memory. This was just an idea, which could be wrong in terms of "ideal" implementation, also not sure if that's how I am supposed to clean memory, I haven't tried smart pointers yet.
~Subject(){
for(listIterator......){
delete (*it); //observer instances pointers are stored in a list
}
};
I was planning to pass callback for my observer, as a pointer to setCallback() function, so i can delete it as I delete observer instances from subject. Now my main question is how do I de-allocate memory, which has been occupied by my callback (which is now an observer property)?
You don't. The callback property is not dynamically allocated. You only need to make sure to deallocate your observer. As a rule of thumb, there should be one delete for each new. Since you never use new for a LambdaT, then there is no need for delete.
Since the setCallback method takes a reference to your callback, this method sees the same value as your main method. But, you do not store a reference to the callback, you store a copy of the callback. So to make this clear:
customCallback is the original value.
callback is a reference to customCallback.
Observer::callback is a copy of callback.