SharedArrayBuffer.cpp 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757
  1. //-------------------------------------------------------------------------------------------------------
  2. // Copyright (C) Microsoft. All rights reserved.
  3. // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
  4. //-------------------------------------------------------------------------------------------------------
  5. #include "RuntimeLibraryPch.h"
  6. namespace Js
  7. {
  8. #if DBG
  9. void SharedContents::AddAgent(DWORD_PTR agent)
  10. {
  11. AutoCriticalSection autoCS(&csAgent);
  12. if (allowedAgents == nullptr)
  13. {
  14. allowedAgents = HeapNew(SharableAgents, &HeapAllocator::Instance);
  15. }
  16. allowedAgents->Add(agent);
  17. }
  18. bool SharedContents::IsValidAgent(DWORD_PTR agent)
  19. {
  20. AutoCriticalSection autoCS(&csAgent);
  21. return allowedAgents != nullptr && allowedAgents->Contains(agent);
  22. }
  23. #endif
  24. long SharedContents::AddRef()
  25. {
  26. return InterlockedIncrement(&refCount);
  27. }
  28. long SharedContents::Release()
  29. {
  30. long ret = InterlockedDecrement(&refCount);
  31. AssertOrFailFastMsg(ret >= 0, "Buffer already freed");
  32. return ret;
  33. }
  34. void SharedContents::Cleanup()
  35. {
  36. Assert(refCount == 0);
  37. buffer = nullptr;
  38. bufferLength = 0;
  39. #if DBG
  40. {
  41. AutoCriticalSection autoCS(&csAgent);
  42. if (allowedAgents != nullptr)
  43. {
  44. HeapDelete(allowedAgents);
  45. allowedAgents = nullptr;
  46. }
  47. }
  48. #endif
  49. if (indexToWaiterList != nullptr)
  50. {
  51. // TODO: the map should be empty here?
  52. // or we need to wake all the waiters from current context?
  53. indexToWaiterList->Map([](uint index, WaiterList *waiters)
  54. {
  55. if (waiters != nullptr)
  56. {
  57. waiters->Cleanup();
  58. HeapDelete(waiters);
  59. waiters = nullptr;
  60. }
  61. });
  62. HeapDelete(indexToWaiterList);
  63. indexToWaiterList = nullptr;
  64. }
  65. }
  66. Var SharedArrayBuffer::NewInstance(RecyclableObject* function, CallInfo callInfo, ...)
  67. {
  68. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  69. ARGUMENTS(args, callInfo);
  70. ScriptContext* scriptContext = function->GetScriptContext();
  71. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  72. Var newTarget = args.GetNewTarget();
  73. bool isCtorSuperCall = JavascriptOperators::GetAndAssertIsConstructorSuperCall(args);
  74. if (!(callInfo.Flags & CallFlags_New) || (newTarget && JavascriptOperators::IsUndefinedObject(newTarget)))
  75. {
  76. JavascriptError::ThrowTypeError(scriptContext, JSERR_ClassConstructorCannotBeCalledWithoutNew, _u("SharedArrayBuffer"));
  77. }
  78. uint32 byteLength = 0;
  79. if (args.Info.Count > 1)
  80. {
  81. byteLength = ArrayBuffer::ToIndex(args[1], JSERR_ArrayLengthConstructIncorrect, scriptContext, MaxSharedArrayBufferLength);
  82. }
  83. RecyclableObject* newArr = scriptContext->GetLibrary()->CreateSharedArrayBuffer(byteLength);
  84. return isCtorSuperCall ?
  85. JavascriptOperators::OrdinaryCreateFromConstructor(VarTo<RecyclableObject>(newTarget), newArr, nullptr, scriptContext) :
  86. newArr;
  87. }
  88. // SharedArrayBuffer.prototype.byteLength
  89. Var SharedArrayBuffer::EntryGetterByteLength(RecyclableObject* function, CallInfo callInfo, ...)
  90. {
  91. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  92. ARGUMENTS(args, callInfo);
  93. ScriptContext* scriptContext = function->GetScriptContext();
  94. Assert(!(callInfo.Flags & CallFlags_New));
  95. if (args.Info.Count == 0 || !VarIs<SharedArrayBuffer>(args[0]))
  96. {
  97. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  98. }
  99. SharedArrayBuffer* sharedArrayBuffer = VarTo<SharedArrayBuffer>(args[0]);
  100. return JavascriptNumber::ToVar(sharedArrayBuffer->GetByteLength(), scriptContext);
  101. }
  102. // SharedArrayBuffer.prototype.slice
  103. Var SharedArrayBuffer::EntrySlice(RecyclableObject* function, CallInfo callInfo, ...)
  104. {
  105. PROBE_STACK(function->GetScriptContext(), Js::Constants::MinStackDefault);
  106. ARGUMENTS(args, callInfo);
  107. ScriptContext* scriptContext = function->GetScriptContext();
  108. AssertMsg(args.Info.Count > 0, "Should always have implicit 'this'");
  109. Assert(!(callInfo.Flags & CallFlags_New));
  110. if (!VarIs<SharedArrayBuffer>(args[0]))
  111. {
  112. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  113. }
  114. JavascriptLibrary* library = scriptContext->GetLibrary();
  115. SharedArrayBuffer* currentBuffer = VarTo<SharedArrayBuffer>(args[0]);
  116. int64 currentLen = (int64)currentBuffer->GetByteLength();
  117. int64 start = 0, end = 0;
  118. int64 newLen = 0;
  119. // If no start or end arguments, use the entire length
  120. if (args.Info.Count < 2)
  121. {
  122. newLen = currentLen;
  123. }
  124. else
  125. {
  126. start = JavascriptArray::GetIndexFromVar(args[1], currentLen, scriptContext);
  127. // If no end argument, use length as the end
  128. if (args.Info.Count < 3 || args[2] == library->GetUndefined())
  129. {
  130. end = currentLen;
  131. }
  132. else
  133. {
  134. end = JavascriptArray::GetIndexFromVar(args[2], currentLen, scriptContext);
  135. }
  136. newLen = end > start ? end - start : 0;
  137. }
  138. // We can't have allocated an SharedArrayBuffer with byteLength > MaxArrayBufferLength.
  139. // start and end are clamped to valid indices, so the new length also cannot exceed MaxArrayBufferLength.
  140. // Therefore, should be safe to cast down newLen to uint32.
  141. Assert(newLen < MaxSharedArrayBufferLength);
  142. uint32 newbyteLength = static_cast<uint32>(newLen);
  143. SharedArrayBuffer* newBuffer = nullptr;
  144. JavascriptFunction* defaultConstructor = scriptContext->GetLibrary()->GetSharedArrayBufferConstructor();
  145. RecyclableObject* constructor = JavascriptOperators::SpeciesConstructor(currentBuffer, defaultConstructor, scriptContext);
  146. AssertOrFailFast(JavascriptOperators::IsConstructor(constructor));
  147. bool isDefaultConstructor = constructor == defaultConstructor;
  148. Js::Var newVar = JavascriptOperators::NewObjectCreationHelper_ReentrancySafe(constructor, isDefaultConstructor, scriptContext->GetThreadContext(), [=]()->Js::Var
  149. {
  150. Js::Var constructorArgs[] = { constructor, JavascriptNumber::ToVar(newbyteLength, scriptContext) };
  151. Js::CallInfo constructorCallInfo(Js::CallFlags_New, _countof(constructorArgs));
  152. return JavascriptOperators::NewScObject(constructor, Js::Arguments(constructorCallInfo, constructorArgs), scriptContext);
  153. });
  154. if (!VarIs<SharedArrayBuffer>(newVar))
  155. {
  156. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  157. }
  158. newBuffer = VarTo<SharedArrayBuffer>(newVar);
  159. if (newBuffer == currentBuffer)
  160. {
  161. JavascriptError::ThrowTypeError(scriptContext, JSERR_NeedSharedArrayBufferObject);
  162. }
  163. if (newBuffer->GetByteLength() < newbyteLength)
  164. {
  165. JavascriptError::ThrowTypeError(scriptContext, JSERR_ArgumentOutOfRange, _u("SharedArrayBuffer.prototype.slice"));
  166. }
  167. Assert(newBuffer);
  168. Assert(newBuffer->GetByteLength() >= newbyteLength);
  169. // Don't bother doing memcpy if we aren't copying any elements
  170. if (newbyteLength > 0)
  171. {
  172. AssertMsg(currentBuffer->GetBuffer() != nullptr, "buffer must not be null when we copy from it");
  173. js_memcpy_s(newBuffer->GetBuffer(), newbyteLength, currentBuffer->GetBuffer() + start, newbyteLength);
  174. }
  175. return newBuffer;
  176. }
  177. Var SharedArrayBuffer::EntryGetterSymbolSpecies(RecyclableObject* function, CallInfo callInfo, ...)
  178. {
  179. ARGUMENTS(args, callInfo);
  180. Assert(args.Info.Count > 0);
  181. return args[0];
  182. }
  183. BYTE* SharedArrayBuffer::AllocBuffer(uint32 length, uint32 maxLength)
  184. {
  185. Unused(maxLength); // WebAssembly only
  186. #if ENABLE_FAST_ARRAYBUFFER
  187. if (this->IsValidVirtualBufferLength(length))
  188. {
  189. return (BYTE*)AsmJsVirtualAllocator(length);
  190. }
  191. else
  192. #endif
  193. {
  194. return HeapNewNoThrowArray(BYTE, length);
  195. }
  196. }
  197. void SharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength)
  198. {
  199. Unused(maxLength); // WebAssembly only
  200. #if ENABLE_FAST_ARRAYBUFFER
  201. //AsmJS Virtual Free
  202. if (this->IsValidVirtualBufferLength(length))
  203. {
  204. FreeMemAlloc(buffer);
  205. }
  206. else
  207. #endif
  208. {
  209. HeapDeleteArray(length, buffer);
  210. }
  211. }
  212. void SharedArrayBuffer::Init(uint32 length, uint32 maxLength)
  213. {
  214. AssertOrFailFast(!sharedContents && length <= maxLength);
  215. BYTE * buffer = nullptr;
  216. if (length > MaxSharedArrayBufferLength)
  217. {
  218. // http://tc39.github.io/ecmascript_sharedmem/shmem.html#DataTypesValues.SpecTypes.DataBlocks.CreateSharedByteDataBlock
  219. // Let db be a new Shared Data Block value consisting of size bytes.
  220. // If it is impossible to create such a Shared Data Block, throw a RangeError exception.
  221. JavascriptError::ThrowRangeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
  222. }
  223. SharedContents* localSharedContents = HeapNewNoThrow(SharedContents, nullptr, length, maxLength);
  224. if (localSharedContents == nullptr)
  225. {
  226. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  227. }
  228. struct AutoCleanupSharedContents
  229. {
  230. SharedContents* sharedContents;
  231. bool allocationCompleted = false;
  232. AutoCleanupSharedContents(SharedContents* sharedContents) : sharedContents(sharedContents) {}
  233. ~AutoCleanupSharedContents()
  234. {
  235. if (!allocationCompleted)
  236. {
  237. HeapDelete(sharedContents);
  238. }
  239. }
  240. } autoCleanupSharedContents(localSharedContents);
  241. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  242. if (maxLength != 0)
  243. {
  244. if (recycler->RequestExternalMemoryAllocation(length))
  245. {
  246. buffer = this->AllocBuffer(length, maxLength);
  247. if (buffer == nullptr)
  248. {
  249. recycler->CollectNow<CollectOnTypedArrayAllocation>();
  250. buffer = this->AllocBuffer(length, maxLength);
  251. if (buffer == nullptr)
  252. {
  253. recycler->ReportExternalMemoryFailure(length);
  254. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  255. }
  256. }
  257. }
  258. else
  259. {
  260. JavascriptError::ThrowOutOfMemoryError(GetScriptContext());
  261. }
  262. Assert(buffer != nullptr);
  263. ZeroMemory(buffer, length);
  264. }
  265. localSharedContents->buffer = buffer;
  266. #if DBG
  267. localSharedContents->AddAgent((DWORD_PTR)GetScriptContext());
  268. #endif
  269. sharedContents = localSharedContents;
  270. autoCleanupSharedContents.allocationCompleted = true;
  271. }
  272. SharedArrayBuffer::SharedArrayBuffer(DynamicType * type) :
  273. ArrayBufferBase(type), sharedContents(nullptr)
  274. {
  275. }
  276. SharedArrayBuffer::SharedArrayBuffer(SharedContents * contents, DynamicType * type) :
  277. ArrayBufferBase(type), sharedContents(nullptr)
  278. {
  279. if (contents == nullptr || contents->bufferLength > MaxSharedArrayBufferLength)
  280. {
  281. JavascriptError::ThrowTypeError(GetScriptContext(), JSERR_FunctionArgument_Invalid);
  282. }
  283. if (contents->AddRef() > 1)
  284. {
  285. sharedContents = contents;
  286. }
  287. else
  288. {
  289. Js::Throw::FatalInternalError();
  290. }
  291. #if DBG
  292. sharedContents->AddAgent((DWORD_PTR)GetScriptContext());
  293. #endif
  294. }
  295. SharedArrayBuffer * SharedArrayBuffer::GetAsSharedArrayBuffer()
  296. {
  297. AssertOrFailFast(VarIsCorrectType(this));
  298. return this;
  299. }
  300. CriticalSection SharedArrayBuffer::csSharedArrayBuffer;
  301. WaiterList *SharedArrayBuffer::GetWaiterList(uint index)
  302. {
  303. if (sharedContents != nullptr)
  304. {
  305. // REVIEW: only lock creating the map and pass the lock to the map?
  306. // use one lock per instance?
  307. AutoCriticalSection autoCS(&csSharedArrayBuffer);
  308. if (sharedContents->indexToWaiterList == nullptr)
  309. {
  310. sharedContents->indexToWaiterList = HeapNew(IndexToWaitersMap, &HeapAllocator::Instance);
  311. }
  312. WaiterList * waiters = nullptr;
  313. if (!sharedContents->indexToWaiterList->TryGetValue(index, &waiters))
  314. {
  315. waiters = HeapNew(WaiterList);
  316. sharedContents->indexToWaiterList->Add(index, waiters);
  317. }
  318. return waiters;
  319. }
  320. Assert(false);
  321. return nullptr;
  322. }
  323. uint32 SharedArrayBuffer::GetByteLength() const
  324. {
  325. return sharedContents != nullptr ? sharedContents->bufferLength : 0;
  326. }
  327. BYTE* SharedArrayBuffer::GetBuffer() const
  328. {
  329. return sharedContents != nullptr ? sharedContents->buffer : nullptr;
  330. }
  331. BOOL SharedArrayBuffer::GetDiagTypeString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  332. {
  333. stringBuilder->AppendCppLiteral(_u("Object, (SharedArrayBuffer)"));
  334. return TRUE;
  335. }
  336. BOOL SharedArrayBuffer::GetDiagValueString(StringBuilder<ArenaAllocator>* stringBuilder, ScriptContext* requestContext)
  337. {
  338. stringBuilder->AppendCppLiteral(_u("[object SharedArrayBuffer]"));
  339. return TRUE;
  340. }
  341. JavascriptSharedArrayBuffer::JavascriptSharedArrayBuffer(DynamicType * type) :
  342. SharedArrayBuffer(type)
  343. {
  344. }
  345. JavascriptSharedArrayBuffer::JavascriptSharedArrayBuffer(SharedContents *sharedContents, DynamicType * type) :
  346. SharedArrayBuffer(sharedContents, type)
  347. {
  348. }
  349. JavascriptSharedArrayBuffer* JavascriptSharedArrayBuffer::Create(uint32 length, DynamicType * type)
  350. {
  351. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  352. JavascriptSharedArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptSharedArrayBuffer, type);
  353. result->Init(length, length);
  354. recycler->AddExternalMemoryUsage(length);
  355. return result;
  356. }
  357. JavascriptSharedArrayBuffer* JavascriptSharedArrayBuffer::Create(SharedContents *sharedContents, DynamicType * type)
  358. {
  359. AssertOrFailFast(!sharedContents || !sharedContents->IsWebAssembly());
  360. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  361. JavascriptSharedArrayBuffer* result = RecyclerNewFinalized(recycler, JavascriptSharedArrayBuffer, sharedContents, type);
  362. return result;
  363. }
  364. bool SharedArrayBuffer::IsValidVirtualBufferLength(uint length) const
  365. {
  366. #if ENABLE_FAST_ARRAYBUFFER
  367. /*
  368. 1. length >= 2^16
  369. 2. length is power of 2 or (length > 2^24 and length is multiple of 2^24)
  370. 3. length is a multiple of 4K
  371. */
  372. return !PHASE_OFF1(Js::TypedArrayVirtualPhase) &&
  373. JavascriptArrayBuffer::IsValidAsmJsBufferLengthAlgo(length, true);
  374. #else
  375. return false;
  376. #endif
  377. }
  378. void JavascriptSharedArrayBuffer::Finalize(bool isShutdown)
  379. {
  380. if (sharedContents == nullptr)
  381. {
  382. return;
  383. }
  384. uint ref = sharedContents->Release();
  385. if (ref == 0)
  386. {
  387. this->FreeBuffer(sharedContents->buffer, sharedContents->bufferLength, sharedContents->maxBufferLength);
  388. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  389. recycler->ReportExternalMemoryFree(sharedContents->bufferLength);
  390. sharedContents->Cleanup();
  391. HeapDelete(sharedContents);
  392. }
  393. sharedContents = nullptr;
  394. }
  395. void JavascriptSharedArrayBuffer::Dispose(bool isShutdown)
  396. {
  397. /* See JavascriptArrayBuffer::Finalize */
  398. }
  399. #ifdef ENABLE_WASM_THREADS
  400. WebAssemblySharedArrayBuffer::WebAssemblySharedArrayBuffer(DynamicType * type):
  401. JavascriptSharedArrayBuffer(type)
  402. {
  403. AssertOrFailFast(Wasm::Threads::IsEnabled());
  404. }
  405. WebAssemblySharedArrayBuffer::WebAssemblySharedArrayBuffer(SharedContents *sharedContents, DynamicType * type) :
  406. JavascriptSharedArrayBuffer(sharedContents, type)
  407. {
  408. AssertOrFailFast(Wasm::Threads::IsEnabled());
  409. ValidateBuffer();
  410. }
  411. void WebAssemblySharedArrayBuffer::ValidateBuffer()
  412. {
  413. #if DBG && _WIN32
  414. if (CONFIG_FLAG(WasmSharedArrayVirtualBuffer))
  415. {
  416. MEMORY_BASIC_INFORMATION info = { 0 };
  417. size_t size = 0;
  418. size_t allocationSize = 0;
  419. // Make sure the beggining of the buffer is committed memory to the expected size
  420. if (sharedContents->bufferLength > 0)
  421. {
  422. size = VirtualQuery((LPCVOID)sharedContents->buffer, &info, sizeof(info));
  423. Assert(size > 0);
  424. allocationSize = info.RegionSize + ((uintptr_t)info.BaseAddress - (uintptr_t)info.AllocationBase);
  425. Assert(allocationSize == sharedContents->bufferLength && info.State == MEM_COMMIT && info.Type == MEM_PRIVATE);
  426. }
  427. // Make sure the end of the buffer is reserved memory to the expected size
  428. size_t expectedAllocationSize = sharedContents->maxBufferLength;
  429. #if ENABLE_FAST_ARRAYBUFFER
  430. if (CONFIG_FLAG(WasmFastArray))
  431. {
  432. expectedAllocationSize = MAX_WASM__ARRAYBUFFER_LENGTH;
  433. }
  434. #endif
  435. // If the whole buffer has been committed, no need to verify this
  436. if (expectedAllocationSize > sharedContents->bufferLength)
  437. {
  438. size = VirtualQuery((LPCVOID)(sharedContents->buffer + sharedContents->bufferLength), &info, sizeof(info));
  439. Assert(size > 0);
  440. allocationSize = info.RegionSize + ((uintptr_t)info.BaseAddress - (uintptr_t)info.AllocationBase);
  441. Assert(allocationSize == expectedAllocationSize && info.State == MEM_RESERVE && info.Type == MEM_PRIVATE);
  442. }
  443. }
  444. #endif
  445. }
  446. WebAssemblySharedArrayBuffer* WebAssemblySharedArrayBuffer::Create(uint32 length, uint32 maxLength, DynamicType * type)
  447. {
  448. AssertOrFailFast(Wasm::Threads::IsEnabled());
  449. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  450. WebAssemblySharedArrayBuffer* result = RecyclerNewFinalized(recycler, WebAssemblySharedArrayBuffer, type);
  451. result->Init(length, maxLength);
  452. result->sharedContents->SetIsWebAssembly();
  453. result->ValidateBuffer();
  454. recycler->AddExternalMemoryUsage(length);
  455. return result;
  456. }
  457. WebAssemblySharedArrayBuffer* WebAssemblySharedArrayBuffer::Create(SharedContents *sharedContents, DynamicType * type)
  458. {
  459. AssertOrFailFast(Wasm::Threads::IsEnabled());
  460. AssertOrFailFast(sharedContents && sharedContents->IsWebAssembly());
  461. Recycler* recycler = type->GetScriptContext()->GetRecycler();
  462. WebAssemblySharedArrayBuffer* result = RecyclerNewFinalized(recycler, WebAssemblySharedArrayBuffer, sharedContents, type);
  463. return result;
  464. }
  465. bool WebAssemblySharedArrayBuffer::IsValidVirtualBufferLength(uint length) const
  466. {
  467. #if ENABLE_FAST_ARRAYBUFFER
  468. if (CONFIG_FLAG(WasmFastArray))
  469. {
  470. return true;
  471. }
  472. #endif
  473. #ifdef _WIN32
  474. if (CONFIG_FLAG(WasmSharedArrayVirtualBuffer))
  475. {
  476. return true;
  477. }
  478. #endif
  479. return false;
  480. }
  481. _Must_inspect_result_ bool WebAssemblySharedArrayBuffer::GrowMemory(uint32 newBufferLength)
  482. {
  483. uint32 bufferLength = sharedContents->bufferLength;
  484. BYTE* buffer = sharedContents->buffer;
  485. if (newBufferLength < bufferLength || newBufferLength > sharedContents->maxBufferLength)
  486. {
  487. AssertMsg(newBufferLength <= sharedContents->maxBufferLength, "This shouldn't happen");
  488. Assert(UNREACHED);
  489. JavascriptError::ThrowTypeError(GetScriptContext(), WASMERR_BufferGrowOnly);
  490. }
  491. uint32 growSize = newBufferLength - bufferLength;
  492. // We're not growing the buffer, do nothing
  493. if (growSize == 0)
  494. {
  495. return true;
  496. }
  497. AssertOrFailFast(buffer);
  498. if (IsValidVirtualBufferLength(newBufferLength))
  499. {
  500. auto virtualAllocFunc = [=]
  501. {
  502. return !!VirtualAlloc(buffer + bufferLength, growSize, MEM_COMMIT, PAGE_READWRITE);
  503. };
  504. if (!this->GetRecycler()->DoExternalAllocation(growSize, virtualAllocFunc))
  505. {
  506. return false;
  507. }
  508. this->GetRecycler()->AddExternalMemoryUsage(growSize);
  509. }
  510. else
  511. {
  512. // We have already allocated maxLength in the heap if we're not using virtual alloc
  513. }
  514. ZeroMemory(buffer + bufferLength, growSize);
  515. sharedContents->bufferLength = newBufferLength;
  516. ValidateBuffer();
  517. return true;
  518. }
  519. BYTE* WebAssemblySharedArrayBuffer::AllocBuffer(uint32 length, uint32 maxLength)
  520. {
  521. #if ENABLE_FAST_ARRAYBUFFER
  522. if (CONFIG_FLAG(WasmFastArray))
  523. {
  524. return (BYTE*)WasmVirtualAllocator(length);
  525. }
  526. #endif
  527. #ifdef _WIN32
  528. if (CONFIG_FLAG(WasmSharedArrayVirtualBuffer))
  529. {
  530. return (BYTE*)AllocWrapper(length, maxLength);
  531. }
  532. #endif
  533. AssertOrFailFast(maxLength >= length);
  534. uint32 additionalSize = maxLength - length;
  535. if (additionalSize > 0)
  536. {
  537. // SharedArrayBuffer::Init already requested External Memory for `length`, we need to request the balance
  538. if (!this->GetRecycler()->RequestExternalMemoryAllocation(additionalSize))
  539. {
  540. // Failed to request for more memory
  541. return nullptr;
  542. }
  543. }
  544. // Allocate the full size of the buffer if we can't do VirtualAlloc
  545. return HeapNewNoThrowArray(BYTE, maxLength);
  546. }
  547. void WebAssemblySharedArrayBuffer::FreeBuffer(BYTE* buffer, uint32 length, uint32 maxLength)
  548. {
  549. if (IsValidVirtualBufferLength(length))
  550. {
  551. FreeMemAlloc(buffer);
  552. }
  553. else
  554. {
  555. HeapDeleteArray(maxLength, buffer);
  556. AssertOrFailFast(maxLength >= length);
  557. // JavascriptSharedArrayBuffer::Finalize will only report freeing `length`, we have to take care of the balance
  558. uint32 additionalSize = maxLength - length;
  559. if (additionalSize > 0)
  560. {
  561. Recycler* recycler = GetType()->GetLibrary()->GetRecycler();
  562. recycler->ReportExternalMemoryFree(additionalSize);
  563. }
  564. }
  565. }
  566. #endif
  567. WaiterList::WaiterList()
  568. : m_waiters(nullptr)
  569. {
  570. InitWaiterList();
  571. }
  572. void WaiterList::InitWaiterList()
  573. {
  574. Assert(m_waiters == nullptr);
  575. m_waiters = HeapNew(Waiters, &HeapAllocator::Instance);
  576. }
  577. void WaiterList::Cleanup()
  578. {
  579. if (m_waiters != nullptr)
  580. {
  581. HeapDelete(m_waiters);
  582. m_waiters = nullptr;
  583. }
  584. }
  585. bool WaiterList::Contains(DWORD_PTR agent)
  586. {
  587. Assert(m_waiters != nullptr);
  588. for (int i = 0; i < m_waiters->Count(); i++)
  589. {
  590. if (m_waiters->Item(i).identity == agent)
  591. {
  592. return true;
  593. }
  594. }
  595. return false;
  596. }
  597. bool _Requires_lock_held_(csForAccess.cs) WaiterList::AddAndSuspendWaiter(DWORD_PTR waiter, uint32 timeout)
  598. {
  599. #ifdef _WIN32
  600. Assert(m_waiters != nullptr);
  601. Assert(waiter != NULL);
  602. Assert(!Contains(waiter));
  603. AgentOfBuffer agent(waiter, CreateEvent(NULL, TRUE, FALSE, NULL));
  604. m_waiters->Add(agent);
  605. csForAccess.Leave();
  606. DWORD result = WaitForSingleObject(agent.event, timeout);
  607. csForAccess.Enter();
  608. return result == WAIT_OBJECT_0;
  609. #else
  610. // TODO for xplat
  611. return false;
  612. #endif
  613. }
  614. void WaiterList::RemoveWaiter(DWORD_PTR waiter)
  615. {
  616. #ifdef _WIN32
  617. Assert(m_waiters != nullptr);
  618. for (int i = m_waiters->Count() - 1; i >= 0; i--)
  619. {
  620. if (m_waiters->Item(i).identity == waiter)
  621. {
  622. CloseHandle(m_waiters->Item(i).event);
  623. m_waiters->RemoveAt(i);
  624. return;
  625. }
  626. }
  627. Assert(false);
  628. #endif
  629. // TODO for xplat
  630. }
  631. uint32 WaiterList::RemoveAndWakeWaiters(int32 count)
  632. {
  633. Assert(m_waiters != nullptr);
  634. Assert(count >= 0);
  635. uint32 removed = 0;
  636. #ifdef _WIN32
  637. while (count > 0 && m_waiters->Count() > 0)
  638. {
  639. AgentOfBuffer agent = m_waiters->Item(0);
  640. m_waiters->RemoveAt(0);
  641. count--; removed++;
  642. SetEvent(agent.event);
  643. // This agent will be closed when their respective call to wait has returned
  644. }
  645. #endif
  646. return removed;
  647. }
  648. bool AgentOfBuffer::AgentCanSuspend(ScriptContext *scriptContext)
  649. {
  650. // Currently we are allowing every agent (including main page) to suspend. If we want only web-worker to suspend un-comment below code.
  651. // return scriptContext != nullptr && scriptContext->webWorkerId != Constants::NonWebWorkerContextId;
  652. return true;
  653. }
  654. }