Skip to content

Commit 3332c6f

Browse files
committed
[MERGE #5982 @MikeHolman] Add JSRT API for externalizing ArrayBuffers
Merge pull request #5982 from MikeHolman:externalizearraypr This API gives the host control of an ArrayBuffer's lifetime
2 parents 9360fcf + 6885486 commit 3332c6f

File tree

9 files changed

+98
-32
lines changed

9 files changed

+98
-32
lines changed

bin/ch/ChakraRtInterface.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ bool ChakraRTInterface::LoadChakraDll(ArgInfo* argInfo, HINSTANCE *outLibrary)
200200

201201
m_jsApiHooks.pfJsrtDetachArrayBuffer = (JsAPIHooks::JsrtDetachArrayBufferPtr)GetChakraCoreSymbol(library, "JsDetachArrayBuffer");
202202
m_jsApiHooks.pfJsrtGetArrayBufferFreeFunction = (JsAPIHooks::JsrtGetArrayBufferFreeFunction)GetChakraCoreSymbol(library, "JsGetArrayBufferFreeFunction");
203+
m_jsApiHooks.pfJsrtExternalizeArrayBuffer = (JsAPIHooks::JsrtExternalizeArrayBufferPtr)GetChakraCoreSymbol(library, "JsExternalizeArrayBuffer");
203204

204205
#ifdef _WIN32
205206
m_jsApiHooks.pfJsrtConnectJITProcess = (JsAPIHooks::JsrtConnectJITProcess)GetChakraCoreSymbol(library, "JsConnectJITProcess");

bin/ch/ChakraRtInterface.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,8 @@ struct JsAPIHooks
138138
typedef JsErrorCode(WINAPI *JsrtVarDeserializerFreePtr)(JsVarDeserializerHandle deserializerHandle);
139139

140140
typedef JsErrorCode(WINAPI *JsrtDetachArrayBufferPtr)(JsValueRef buffer);
141-
typedef JsErrorCode(WINAPI* JsrtGetArrayBufferFreeFunction)(JsValueRef buffer, ArrayBufferFreeFn** freeFn);
141+
typedef JsErrorCode(WINAPI* JsrtGetArrayBufferFreeFunction)(JsValueRef buffer, ArrayBufferFreeFn* freeFn);
142+
typedef JsErrorCode(WINAPI* JsrtExternalizeArrayBufferPtr)(JsValueRef buffer);
142143

143144
JsrtCreateRuntimePtr pfJsrtCreateRuntime;
144145
JsrtCreateContextPtr pfJsrtCreateContext;
@@ -270,6 +271,7 @@ struct JsAPIHooks
270271

271272
JsrtDetachArrayBufferPtr pfJsrtDetachArrayBuffer;
272273
JsrtGetArrayBufferFreeFunction pfJsrtGetArrayBufferFreeFunction;
274+
JsrtExternalizeArrayBufferPtr pfJsrtExternalizeArrayBuffer;
273275
#ifdef _WIN32
274276
JsrtConnectJITProcess pfJsrtConnectJITProcess;
275277
#endif
@@ -512,7 +514,8 @@ class ChakraRTInterface
512514
static JsErrorCode WINAPI JsConnectJITProcess(HANDLE processHandle, void* serverSecurityDescriptor, UUID connectionId) { return HOOK_JS_API(ConnectJITProcess(processHandle, serverSecurityDescriptor, connectionId)); }
513515
#endif
514516

515-
static JsErrorCode WINAPI JsGetArrayBufferFreeFunction(JsValueRef buffer, ArrayBufferFreeFn** freeFn) { return HOOK_JS_API(GetArrayBufferFreeFunction(buffer, freeFn)); }
517+
static JsErrorCode WINAPI JsGetArrayBufferFreeFunction(JsValueRef buffer, ArrayBufferFreeFn* freeFn) { return HOOK_JS_API(GetArrayBufferFreeFunction(buffer, freeFn)); }
518+
static JsErrorCode WINAPI JsExternalizeArrayBuffer(JsValueRef buffer) { return HOOK_JS_API(ExternalizeArrayBuffer(buffer)); }
516519
};
517520

518521
class AutoRestoreContext

bin/ch/WScriptJsrt.cpp

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,16 @@
4444
#define INTL_LIBRARY_TEXT ""
4545
#endif
4646

47+
struct ArrayBufferTransferInfo {
48+
byte* buffer;
49+
uint length;
50+
ArrayBufferFreeFn freeFn;
51+
};
4752
struct SerializerBlob
4853
{
4954
void *data;
5055
size_t dataLength;
51-
std::vector< std::pair<void *, uint32> > transferableArrays;
56+
std::vector<ArrayBufferTransferInfo> transferableArrays;
5257
};
5358

5459
MessageQueue* WScriptJsrt::messageQueue = nullptr;
@@ -371,13 +376,11 @@ JsValueRef __stdcall WScriptJsrt::SerializeObject(JsValueRef callee, bool isCons
371376
for (int i = 0; i < transferVarsCount; i++)
372377
{
373378
JsValueRef arrayBuffer = transferVarsArray[i];
374-
BYTE *buffer = nullptr;
375-
uint32 bufferLength = 0;
376-
IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferStorage(arrayBuffer, &buffer, &bufferLength));
377-
ArrayBufferFreeFn* freeFn = nullptr;
378-
IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferFreeFunction(arrayBuffer, &freeFn));
379-
380-
blob->transferableArrays.push_back(std::make_pair((void*)buffer, bufferLength));
379+
ArrayBufferTransferInfo bufferInfo;
380+
IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferStorage(arrayBuffer, &bufferInfo.buffer, &bufferInfo.length));
381+
IfJsrtErrorSetGo(ChakraRTInterface::JsExternalizeArrayBuffer(arrayBuffer));
382+
IfJsrtErrorSetGo(ChakraRTInterface::JsGetArrayBufferFreeFunction(arrayBuffer, &bufferInfo.freeFn));
383+
blob->transferableArrays.push_back(bufferInfo);
381384
IfJsrtErrorSetGo(ChakraRTInterface::JsDetachArrayBuffer(arrayBuffer));
382385
}
383386

@@ -412,6 +415,25 @@ JsValueRef CHAKRA_CALLBACK GetWasmModuleFromId(void * state, uint32_t transfer_i
412415
return nullptr;
413416
}
414417

418+
struct BufferFreeFunctionState {
419+
ArrayBufferFreeFn freeFn;
420+
void* buffer;
421+
};
422+
423+
void CHAKRA_CALLBACK BufferFreeFunction(void * state)
424+
{
425+
BufferFreeFunctionState* bufferState = (BufferFreeFunctionState*)state;
426+
if (!bufferState)
427+
{
428+
return;
429+
}
430+
if (bufferState->freeFn)
431+
{
432+
bufferState->freeFn(bufferState->buffer);
433+
}
434+
delete bufferState;
435+
}
436+
415437
JsValueRef __stdcall WScriptJsrt::Deserialize(JsValueRef callee, bool isConstructCall, JsValueRef *arguments, unsigned short argumentCount, void *callbackState)
416438
{
417439
JsErrorCode errorCode = JsNoError;
@@ -442,7 +464,10 @@ JsValueRef __stdcall WScriptJsrt::Deserialize(JsValueRef callee, bool isConstruc
442464
for (size_t i = 0; i < arraySize; ++i)
443465
{
444466
JsValueRef result = nullptr;
445-
IfJsrtErrorSetGo(ChakraRTInterface::JsCreateExternalArrayBuffer(blob->transferableArrays[i].first, blob->transferableArrays[i].second, nullptr, nullptr, &result));
467+
BufferFreeFunctionState* bufferFreeState = new BufferFreeFunctionState();
468+
bufferFreeState->buffer = blob->transferableArrays[i].buffer;
469+
bufferFreeState->freeFn = blob->transferableArrays[i].freeFn;
470+
IfJsrtErrorSetGo(ChakraRTInterface::JsCreateExternalArrayBuffer(blob->transferableArrays[i].buffer, blob->transferableArrays[i].length, BufferFreeFunction, bufferFreeState, &result));
446471
transferables[i] = result;
447472
}
448473

lib/Jsrt/ChakraCore.h

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1887,7 +1887,7 @@ CHAKRA_API
18871887
JsDetachArrayBuffer(
18881888
_In_ JsValueRef arrayBuffer);
18891889

1890-
typedef void __cdecl ArrayBufferFreeFn(void* ptr);
1890+
typedef void(__cdecl *ArrayBufferFreeFn)(void*);
18911891

18921892
/// <summary>
18931893
/// Returns the function which free the underlying buffer of ArrayBuffer
@@ -1901,8 +1901,18 @@ typedef void __cdecl ArrayBufferFreeFn(void* ptr);
19011901
CHAKRA_API
19021902
JsGetArrayBufferFreeFunction(
19031903
_In_ JsValueRef arrayBuffer,
1904-
_Out_ ArrayBufferFreeFn** freeFn);
1904+
_Out_ ArrayBufferFreeFn* freeFn);
19051905

1906+
/// <summary>
1907+
/// Take ownership of current ArrayBuffer
1908+
/// </summary>
1909+
/// <param name="arrayBuffer">An ArrayBuffer to take ownership of</param>
1910+
/// <returns>
1911+
/// The code <c>JsNoError</c> if the operation succeeded, a failure code otherwise.
1912+
/// </returns>
1913+
CHAKRA_API
1914+
JsExternalizeArrayBuffer(
1915+
_In_ JsValueRef arrayBuffer);
19061916

19071917
#ifdef _WIN32
19081918
#include "ChakraCoreWindows.h"

lib/Jsrt/Core/JsrtCore.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -497,6 +497,25 @@ JsSetArrayBufferExtraInfo(
497497
END_JSRT_NO_EXCEPTION
498498
}
499499

500+
CHAKRA_API
501+
JsExternalizeArrayBuffer(
502+
_In_ JsValueRef arrayBufferVar)
503+
{
504+
VALIDATE_JSREF(arrayBufferVar);
505+
return ContextAPINoScriptWrapper_NoRecord([&](Js::ScriptContext *scriptContext) -> JsErrorCode {
506+
507+
Js::ArrayBuffer* arrayBuffer = Js::JavascriptOperators::TryFromVar<Js::ArrayBuffer>(arrayBufferVar);
508+
if (!arrayBuffer)
509+
{
510+
return JsErrorInvalidArgument;
511+
}
512+
513+
arrayBuffer->Externalize();
514+
515+
return JsNoError;
516+
});
517+
}
518+
500519
CHAKRA_API
501520
JsDetachArrayBuffer(
502521
_In_ JsValueRef arrayBuffer)
@@ -1419,7 +1438,7 @@ JsConnectJITProcess(_In_ HANDLE processHandle, _In_opt_ void* serverSecurityDesc
14191438
CHAKRA_API
14201439
JsGetArrayBufferFreeFunction(
14211440
_In_ JsValueRef arrayBuffer,
1422-
_Out_ ArrayBufferFreeFn** freeFn)
1441+
_Out_ ArrayBufferFreeFn* freeFn)
14231442
{
14241443
VALIDATE_JSREF(arrayBuffer);
14251444
PARAM_NOT_NULL(freeFn);
@@ -1430,7 +1449,7 @@ JsGetArrayBufferFreeFunction(
14301449
return JsErrorInvalidArgument;
14311450
}
14321451

1433-
*freeFn = (ArrayBufferFreeFn*)Js::VarTo<Js::ArrayBuffer>(arrayBuffer)->GetArrayBufferFreeFn();
1452+
*freeFn = Js::VarTo<Js::ArrayBuffer>(arrayBuffer)->GetArrayBufferFreeFn();
14341453
return JsNoError;
14351454
});
14361455
}

lib/Jsrt/JsrtCommonExports.inc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,9 @@
122122
JsCreateWeakReference
123123
JsDetachArrayBuffer
124124
JsGetArrayBufferExtraInfo
125+
JsExternalizeArrayBuffer
125126
JsGetAndClearExceptionWithMetadata
127+
JsGetArrayBufferFreeFunction
126128
JsGetArrayEntriesFunction
127129
JsGetArrayForEachFunction
128130
JsGetArrayKeysFunction
@@ -168,5 +170,4 @@
168170
JsVarSerializerSetTransferableVars
169171
JsVarSerializerWriteRawBytes
170172
JsVarSerializerWriteValue
171-
JsGetArrayBufferFreeFunction
172173
#endif

lib/Runtime/Library/ArrayBuffer.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ namespace Js
637637
ArrayBufferContentForDelayedFreeBase* ArrayBuffer::CopyBufferContentForDelayedFree(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength)
638638
{
639639
Assert(content != nullptr);
640-
FreeFn* freeFn = nullptr;
640+
FreeFn freeFn = nullptr;
641641
#if ENABLE_FAST_ARRAYBUFFER
642642
if (IsValidVirtualBufferLength(bufferLength))
643643
{
@@ -831,7 +831,7 @@ namespace Js
831831
return result;
832832
}
833833

834-
ArrayBuffer::FreeFn* JavascriptArrayBuffer::GetArrayBufferFreeFn()
834+
ArrayBuffer::FreeFn JavascriptArrayBuffer::GetArrayBufferFreeFn()
835835
{
836836
#if ENABLE_FAST_ARRAYBUFFER
837837
if (IsValidVirtualBufferLength(bufferLength))
@@ -847,7 +847,7 @@ namespace Js
847847

848848
ArrayBufferDetachedStateBase* JavascriptArrayBuffer::CreateDetachedState(RefCountedBuffer * content, uint32 bufferLength)
849849
{
850-
FreeFn* freeFn = nullptr;
850+
FreeFn freeFn = nullptr;
851851
ArrayBufferAllocationType allocationType;
852852
#if ENABLE_FAST_ARRAYBUFFER
853853
if (IsValidVirtualBufferLength(bufferLength))
@@ -914,14 +914,14 @@ namespace Js
914914
if (refCount == 0)
915915
{
916916
BYTE * buffer = content->GetBuffer();
917-
if (buffer)
917+
if (buffer && !this->externalized)
918918
{
919919
// Recycler may not be available at Dispose. We need to
920920
// free the memory and report that it has been freed at the same
921921
// time. Otherwise, AllocationPolicyManager is unable to provide correct feedback
922922
#if ENABLE_FAST_ARRAYBUFFER
923923
//AsmJS Virtual Free
924-
if (buffer && IsValidVirtualBufferLength(this->bufferLength))
924+
if (IsValidVirtualBufferLength(this->bufferLength))
925925
{
926926
FreeMemAlloc(buffer);
927927
}

lib/Runtime/Library/ArrayBuffer.h

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,13 @@ namespace Js
6060
return AllocWrapper(length, MaxVirtualSize);
6161
}
6262

63-
static void FreeMemAlloc(Var ptr)
63+
static void __cdecl FreeMemAlloc(Var ptr)
6464
{
6565
BOOL fSuccess = VirtualFree((LPVOID)ptr, 0, MEM_RELEASE);
6666
Assert(fSuccess);
6767
}
6868
#else
69-
static void FreeMemAlloc(Var ptr)
69+
static void __cdecl FreeMemAlloc(Var ptr)
7070
{
7171
// This free function should never be used
7272
Js::Throw::FatalInternalError();
@@ -77,7 +77,12 @@ namespace Js
7777

7878
virtual void MarshalToScriptContext(Js::ScriptContext * scriptContext) = 0;
7979

80-
ArrayBufferBase(DynamicType *type) : DynamicObject(type), isDetached(false), infoBits(0) { }
80+
ArrayBufferBase(DynamicType *type) :
81+
DynamicObject(type),
82+
isDetached(false),
83+
infoBits(0),
84+
externalized(false) { }
85+
8186
bool IsDetached() { return isDetached; }
8287

8388
#if ENABLE_TTD
@@ -99,8 +104,10 @@ namespace Js
99104

100105
static int GetIsDetachedOffset() { return offsetof(ArrayBufferBase, isDetached); }
101106

107+
void Externalize() { this->externalized = true; }
102108
protected:
103109
Field(bool) isDetached;
110+
Field(bool) externalized;
104111
Field(char) infoBits;
105112
};
106113

@@ -142,9 +149,9 @@ namespace Js
142149
class ArrayBufferDetachedState : public ArrayBufferDetachedStateBase
143150
{
144151
public:
145-
FreeFN* freeFunction;
152+
FreeFN freeFunction;
146153
Recycler* recycler;
147-
ArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength, FreeFN* freeFunction, Recycler* r, ArrayBufferAllocationType allocationType)
154+
ArrayBufferDetachedState(RefCountedBuffer* buffer, uint32 bufferLength, FreeFN freeFunction, Recycler* r, ArrayBufferAllocationType allocationType)
148155
: ArrayBufferDetachedStateBase(TypeIds_ArrayBuffer, buffer, bufferLength, allocationType),
149156
recycler(r),
150157
freeFunction(freeFunction)
@@ -206,8 +213,8 @@ namespace Js
206213
RefCountedBuffer *GetBufferContent() { return bufferContent; }
207214
static int GetBufferContentsOffset() { return offsetof(ArrayBuffer, bufferContent); }
208215

209-
typedef void __cdecl FreeFn(void* ptr);
210-
virtual FreeFn* GetArrayBufferFreeFn() { return nullptr; }
216+
typedef void(__cdecl *FreeFn)(void*);
217+
virtual FreeFn GetArrayBufferFreeFn() { return nullptr; }
211218
static int GetByteLengthOffset() { return offsetof(ArrayBuffer, bufferLength); }
212219
virtual void AddParent(ArrayBufferParent* parent) override;
213220

@@ -310,7 +317,7 @@ namespace Js
310317
virtual bool IsValidAsmJsBufferLength(uint length, bool forceCheck = false) override;
311318
virtual bool IsValidVirtualBufferLength(uint length) const override;
312319

313-
virtual FreeFn* GetArrayBufferFreeFn() override;
320+
virtual FreeFn GetArrayBufferFreeFn() override;
314321

315322
protected:
316323
JavascriptArrayBuffer(DynamicType * type);
@@ -358,7 +365,7 @@ namespace Js
358365
DEFINE_VTABLE_CTOR(ProjectionArrayBuffer, ArrayBuffer);
359366
DEFINE_MARSHAL_OBJECT_TO_SCRIPT_CONTEXT(ProjectionArrayBuffer);
360367

361-
typedef void __stdcall FreeFn(LPVOID ptr);
368+
typedef void (__stdcall *FreeFn)(LPVOID ptr);
362369
virtual ArrayBufferDetachedStateBase* CreateDetachedState(RefCountedBuffer * content, DECLSPEC_GUARD_OVERFLOW uint32 bufferLength) override
363370
{
364371
return HeapNew(ArrayBufferDetachedState<FreeFn>, content, bufferLength, CoTaskMemFree, GetScriptContext()->GetRecycler(), ArrayBufferAllocationType::CoTask);

lib/Runtime/Library/DelayFreeArrayBufferHelper.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,10 @@ namespace Js
9393
template <typename FreeFN>
9494
class ArrayBufferContentForDelayedFree : public ArrayBufferContentForDelayedFreeBase
9595
{
96-
FreeFN* freeFunction;
96+
FreeFN freeFunction;
9797

9898
public:
99-
ArrayBufferContentForDelayedFree(RefCountedBuffer *content, uint32 len, Recycler *r, FreeFN * freeFunction)
99+
ArrayBufferContentForDelayedFree(RefCountedBuffer *content, uint32 len, Recycler *r, FreeFN freeFunction)
100100
: ArrayBufferContentForDelayedFreeBase( content, len, r), freeFunction(freeFunction)
101101
{}
102102

0 commit comments

Comments
 (0)