Skip to content

Commit

Permalink
src: fix up smalloc weak persistent usage
Browse files Browse the repository at this point in the history
Fix a regression that was introduced in commit ce04c72 after the
upgrade to V8 3.24.

The new weak persistent handle API no longer gives you the original
persistent but still requires that you clear it inside your weak
callback.

Rearrange the code in src/smalloc.cc to keep track of the persistent
handle with the least amount of pain and try hard to share as much
code as possible between the 'just free it' and 'invoke my callback'
versions of the smalloc API.

Fixes nodejs#7309.
  • Loading branch information
bnoordhuis committed Mar 15, 2014
1 parent a2a2b8f commit 5bfb0de
Showing 1 changed file with 151 additions and 82 deletions.
233 changes: 151 additions & 82 deletions src/smalloc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,148 @@ using v8::WeakCallbackData;
using v8::kExternalUnsignedByteArray;


struct CallbackInfo {
void* hint;
FreeCallback cb;
Persistent<Object> p_obj;
template <typename Payload>
class CallbackInfo {
public:
static inline CallbackInfo* New(Isolate* isolate,
Handle<Object> object,
Payload payload);
inline void Dispose();
inline Persistent<Object>* persistent();

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

I guess it could be inline Persistent<Object>* persistent() const ?

This comment has been minimized.

Copy link
@bnoordhuis

bnoordhuis Mar 16, 2014

Author Owner

It can't, not without const_cast or mutable. It returns a pointer to a data member.

inline Payload* payload();

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

Ditto.

private:
static void WeakCallback(const WeakCallbackData<Object, CallbackInfo>&);
inline CallbackInfo(Isolate* isolate,
Handle<Object> object,
Payload payload);
~CallbackInfo();

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

I'd add empty line after this.

Persistent<Object> persistent_;
Payload payload_;
};

void TargetCallback(const WeakCallbackData<Object, char>& data);
void TargetFreeCallback(Isolate* isolate,
Persistent<Object>* target,
CallbackInfo* cb_info);
void TargetFreeCallback(const WeakCallbackData<Object, CallbackInfo>& data);

class Free {
public:
explicit Free(char* data);
void WeakCallback(Isolate* isolate,
Local<Object> object,
CallbackInfo<Free>* info);
private:
char* const data_;
};


class Dispose {
public:
typedef void (*Callback)(char* data, void* hint);
Dispose(Callback callback, void* hint);
void WeakCallback(Isolate* isolate,
Local<Object> object,
CallbackInfo<Dispose>* info);
private:
Callback const callback_;
void* const hint_;
};


template <typename Payload>
CallbackInfo<Payload>* CallbackInfo<Payload>::New(Isolate* isolate,
Handle<Object> object,
Payload payload) {
return new CallbackInfo(isolate, object, payload);

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

What's the point of this method?

}


template <typename Payload>
void CallbackInfo<Payload>::Dispose() {
delete this;

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

What's the point of this method?

This comment has been minimized.

Copy link
@bnoordhuis

bnoordhuis Mar 16, 2014

Author Owner

Enforce dynamic allocation.

}


template <typename Payload>
Persistent<Object>* CallbackInfo<Payload>::persistent() {
return &persistent_;

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

I'd suggest inlining it in the class declaration.

This comment has been minimized.

Copy link
@bnoordhuis

bnoordhuis Mar 16, 2014

Author Owner

Why? Splitting declarations and definitions is generally considered a good thing.

}


template <typename Payload>
Payload* CallbackInfo<Payload>::payload() {
return &payload_;

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

Ditto.

}


template <typename Payload>
CallbackInfo<Payload>::CallbackInfo(Isolate* isolate,
Handle<Object> object,
Payload payload)
: persistent_(isolate, object),
payload_(payload) {
persistent_.SetWeak(this, WeakCallback);
persistent_.SetWrapperClassId(ALLOC_ID);
persistent_.MarkIndependent();
}


template <typename Payload>
CallbackInfo<Payload>::~CallbackInfo() {
persistent_.Reset();
}


template <typename Payload>
void CallbackInfo<Payload>::WeakCallback(
const WeakCallbackData<Object, CallbackInfo>& data) {
CallbackInfo* info = data.GetParameter();
info->payload()->WeakCallback(data.GetIsolate(), data.GetValue(), info);
}


Free::Free(char* data) : data_(data) {

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

Why not inline it into class declaration?

}


void Free::WeakCallback(Isolate* isolate,
Local<Object> object,
CallbackInfo<Free>* info) {
free(data_);
size_t length = object->GetIndexedPropertiesExternalArrayDataLength();
enum ExternalArrayType array_type =
object->GetIndexedPropertiesExternalArrayDataType();
size_t array_size = ExternalArraySize(array_type);
CHECK_GT(array_size, 0);
CHECK_GE(array_size * length, length); // Overflow check.
length *= array_size;
int64_t change_in_bytes = -static_cast<int64_t>(length);
isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
info->Dispose();
}


Dispose::Dispose(Callback callback, void* hint)

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

Ditto.

: callback_(callback),
hint_(hint) {
}


void Dispose::WeakCallback(Isolate* isolate,
Local<Object> object,
CallbackInfo<Dispose>* info) {
char* data =
static_cast<char*>(object->GetIndexedPropertiesExternalArrayData());
size_t len = object->GetIndexedPropertiesExternalArrayDataLength();
enum ExternalArrayType array_type =
object->GetIndexedPropertiesExternalArrayDataType();
size_t array_size = ExternalArraySize(array_type);
CHECK_GT(array_size, 0);
if (array_size > 1) {
CHECK_GT(len * array_size, len); // Overflow check.
len *= array_size;
}
int64_t change_in_bytes = -static_cast<int64_t>(len + sizeof(*info));
isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
callback_(data, hint_);
info->Dispose();
}


// return size of external array type, or 0 if unrecognized
Expand Down Expand Up @@ -268,36 +399,10 @@ void Alloc(Environment* env,
size_t length,
enum ExternalArrayType type) {
assert(!obj->HasIndexedPropertiesInExternalArrayData());
Persistent<Object> p_obj(env->isolate(), obj);
env->isolate()->AdjustAmountOfExternalAllocatedMemory(length);
p_obj.SetWeak(data, TargetCallback);
p_obj.MarkIndependent();
p_obj.SetWrapperClassId(ALLOC_ID);
size_t size = length / ExternalArraySize(type);
obj->SetIndexedPropertiesToExternalArrayData(data, type, size);
}


void TargetCallback(const WeakCallbackData<Object, char>& data) {
HandleScope handle_scope(data.GetIsolate());
char* info = data.GetParameter();

Local<Object> obj = data.GetValue();
size_t len = obj->GetIndexedPropertiesExternalArrayDataLength();
enum ExternalArrayType array_type =
obj->GetIndexedPropertiesExternalArrayDataType();
size_t array_size = ExternalArraySize(array_type);
assert(array_size > 0);
assert(array_size * len >= len);
len *= array_size;
if (info != NULL && len > 0) {
int64_t change_in_bytes = -static_cast<int64_t>(len);
data.GetIsolate()->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
free(info);
}

// XXX: Persistent is no longer passed here
// persistent.Reset();
CallbackInfo<Free>::New(env->isolate(), obj, Free(data));

This comment has been minimized.

Copy link
@indutny

indutny Mar 16, 2014

Ah, I guess that's what you have needed it for...

}


Expand All @@ -315,8 +420,10 @@ void AllocDispose(Environment* env, Handle<Object> obj) {
Local<Value> ext_v = obj->GetHiddenValue(env->smalloc_p_string());
if (ext_v->IsExternal()) {
Local<External> ext = ext_v.As<External>();
CallbackInfo* cb_info = static_cast<CallbackInfo*>(ext->Value());
TargetFreeCallback(env->isolate(), &cb_info->p_obj, cb_info);
CallbackInfo<Free>* info = static_cast<CallbackInfo<Free>*>(ext->Value());
Local<Object> object = PersistentToLocal(env->isolate(),
*info->persistent());
info->payload()->WeakCallback(env->isolate(), object, info);
return;
}
}
Expand Down Expand Up @@ -373,56 +480,18 @@ void Alloc(Environment* env,
void* hint,
enum ExternalArrayType type) {
assert(!obj->HasIndexedPropertiesInExternalArrayData());

HandleScope handle_scope(env->isolate());
Isolate* isolate = env->isolate();
HandleScope handle_scope(isolate);
env->set_using_smalloc_alloc_cb(true);

CallbackInfo* cb_info = new CallbackInfo;
cb_info->cb = fn;
cb_info->hint = hint;
cb_info->p_obj.Reset(env->isolate(), obj);
obj->SetHiddenValue(env->smalloc_p_string(),
External::New(env->isolate(), cb_info));

env->isolate()->AdjustAmountOfExternalAllocatedMemory(length +
sizeof(*cb_info));
cb_info->p_obj.SetWeak(cb_info, TargetFreeCallback);
cb_info->p_obj.MarkIndependent();
cb_info->p_obj.SetWrapperClassId(ALLOC_ID);
CallbackInfo<Dispose>* info =
CallbackInfo<Dispose>::New(isolate, obj, Dispose(fn, hint));
obj->SetHiddenValue(env->smalloc_p_string(), External::New(isolate, info));
isolate->AdjustAmountOfExternalAllocatedMemory(length + sizeof(*info));
size_t size = length / ExternalArraySize(type);
obj->SetIndexedPropertiesToExternalArrayData(data, type, size);
}


void TargetFreeCallback(Isolate* isolate,
Persistent<Object>* target,
CallbackInfo* cb_info) {
HandleScope handle_scope(isolate);
Local<Object> obj = PersistentToLocal(isolate, *target);
char* data = static_cast<char*>(obj->GetIndexedPropertiesExternalArrayData());
size_t len = obj->GetIndexedPropertiesExternalArrayDataLength();
enum ExternalArrayType array_type =
obj->GetIndexedPropertiesExternalArrayDataType();
size_t array_size = ExternalArraySize(array_type);
assert(array_size > 0);
if (array_size > 1) {
assert(len * array_size > len);
len *= array_size;
}
int64_t change_in_bytes = -static_cast<int64_t>(len + sizeof(*cb_info));
isolate->AdjustAmountOfExternalAllocatedMemory(change_in_bytes);
cb_info->p_obj.Reset();
cb_info->cb(data, cb_info->hint);
delete cb_info;
}


void TargetFreeCallback(const WeakCallbackData<Object, CallbackInfo>& data) {
CallbackInfo* cb_info = data.GetParameter();
TargetFreeCallback(data.GetIsolate(), &cb_info->p_obj, cb_info);
}


void HasExternalData(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args.GetIsolate());
args.GetReturnValue().Set(args[0]->IsObject() &&
Expand Down

2 comments on commit 5bfb0de

@indutny
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hm... Actually, I thought that I have fixed it myself. Perhaps it was lost somewhere in rebase.

@indutny
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, LGTM

Please sign in to comment.