| // Copyright 2017 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #ifndef gc_JSCUSTOMALLOCATOR_H |
| #define gc_JSCUSTOMALLOCATOR_H |
| |
| #include <algorithm> |
| |
| #include "memory_allocator_reporter.h" |
| #include "starboard/memory.h" |
| #include "starboard/string.h" |
| |
| namespace js { |
| namespace oom { |
| |
| // Code imported from default allocator. These identifiers must be supplied |
| // to SpiderMonkey common code. |
| |
| /* |
| * To make testing OOM in certain helper threads more effective, |
| * allow restricting the OOM testing to a certain helper thread |
| * type. This allows us to fail e.g. in off-thread script parsing |
| * without causing an OOM in the main thread first. |
| */ |
| enum ThreadType { |
| THREAD_TYPE_NONE = 0, // 0 |
| THREAD_TYPE_MAIN, // 1 |
| THREAD_TYPE_ASMJS, // 2 |
| THREAD_TYPE_ION, // 3 |
| THREAD_TYPE_PARSE, // 4 |
| THREAD_TYPE_COMPRESS, // 5 |
| THREAD_TYPE_GCHELPER, // 6 |
| THREAD_TYPE_GCPARALLEL, // 7 |
| THREAD_TYPE_MAX // Used to check shell function arguments |
| }; |
| |
| /* |
| * Getter/Setter functions to encapsulate mozilla::ThreadLocal, |
| * implementation is in jsutil.cpp. |
| */ |
| # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) |
| extern bool InitThreadType(void); |
| extern void SetThreadType(ThreadType); |
| extern uint32_t GetThreadType(void); |
| # else |
| inline bool InitThreadType(void) { return true; } |
| inline void SetThreadType(ThreadType t) {}; |
| inline uint32_t GetThreadType(void) { return 0; } |
| # endif |
| |
| } // namespace oom |
| } // namespace js |
| |
| # if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) |
| |
| /* |
| * In order to test OOM conditions, when the testing function |
| * oomAfterAllocations COUNT is passed, we fail continuously after the NUM'th |
| * allocation from now. |
| */ |
| extern JS_PUBLIC_DATA(uint32_t) OOM_maxAllocations; /* set in builtin/TestingFunctions.cpp */ |
| extern JS_PUBLIC_DATA(uint32_t) OOM_counter; /* data race, who cares. */ |
| extern JS_PUBLIC_DATA(bool) OOM_failAlways; |
| |
| #define JS_OOM_CALL_BP_FUNC() do {} while(0) |
| |
| namespace js { |
| namespace oom { |
| |
| extern JS_PUBLIC_DATA(uint32_t) targetThread; |
| |
| static inline bool |
| IsThreadSimulatingOOM() |
| { |
| return false; |
| } |
| |
| static inline bool |
| IsSimulatedOOMAllocation() |
| { |
| return false; |
| } |
| |
| static inline bool |
| ShouldFailWithOOM() |
| { |
| return false; |
| } |
| |
| } // namespace oom |
| } // namespace js |
| |
| # define JS_OOM_POSSIBLY_FAIL() \ |
| do { \ |
| if (js::oom::ShouldFailWithOOM()) \ |
| return nullptr; \ |
| } while (0) |
| |
| # define JS_OOM_POSSIBLY_FAIL_BOOL() \ |
| do { \ |
| if (js::oom::ShouldFailWithOOM()) \ |
| return false; \ |
| } while (0) |
| |
| # else // defined(DEBUG) || defined(JS_OOM_BREAKPOINT) |
| |
| # define JS_OOM_POSSIBLY_FAIL() do {} while(0) |
| # define JS_OOM_POSSIBLY_FAIL_BOOL() do {} while(0) |
| namespace js { |
| namespace oom { |
| static inline bool IsSimulatedOOMAllocation() { return false; } |
| static inline bool ShouldFailWithOOM() { return false; } |
| } // namespace oom |
| } // namespace js |
| |
| # endif // defined(DEBUG) || defined(JS_OOM_BREAKPOINT) |
| |
| namespace js { |
| |
| struct MOZ_RAII AutoEnterOOMUnsafeRegion |
| { |
| MOZ_NORETURN MOZ_COLD void crash(const char* reason); |
| }; |
| |
| } // namespace js |
| |
| static inline void* js_malloc(size_t bytes) |
| { |
| size_t reservation_bytes = AllocationMetadata::GetReservationBytes(bytes); |
| MemoryAllocatorReporter::Get()->UpdateAllocatedBytes(reservation_bytes); |
| void* metadata = SbMemoryAllocate(reservation_bytes); |
| AllocationMetadata::SetSizeToBaseAddress(metadata, reservation_bytes); |
| return AllocationMetadata::GetUserAddressFromBaseAddress(metadata); |
| } |
| |
| static inline void* js_calloc(size_t nmemb, size_t size) |
| { |
| size_t total_size = nmemb * size; |
| void* memory = js_malloc(total_size); |
| if (memory) { |
| SbMemorySet(memory, 0, total_size); |
| } |
| return memory; |
| } |
| |
| static inline void* js_calloc(size_t bytes) |
| { |
| return js_calloc(bytes, 1); |
| } |
| |
| static inline void* js_realloc(void* p, size_t bytes) |
| { |
| AllocationMetadata* metadata = |
| AllocationMetadata::GetMetadataFromUserAddress(p); |
| size_t current_size = |
| AllocationMetadata::GetSizeOfAllocationFromMetadata(metadata); |
| size_t adjusted_size = AllocationMetadata::GetReservationBytes(bytes); |
| |
| MemoryAllocatorReporter::Get()->UpdateAllocatedBytes( |
| static_cast<ssize_t>(adjusted_size - current_size)); |
| void* new_ptr = SbMemoryReallocate(metadata, adjusted_size); |
| AllocationMetadata::SetSizeToBaseAddress(new_ptr, adjusted_size); |
| return AllocationMetadata::GetUserAddressFromBaseAddress(new_ptr); |
| } |
| |
| static inline void js_free(void* p) |
| { |
| if (p == NULL) { |
| return; |
| } |
| AllocationMetadata* metadata = |
| AllocationMetadata::GetMetadataFromUserAddress(p); |
| MemoryAllocatorReporter::Get()->UpdateAllocatedBytes(-static_cast<ssize_t>( |
| AllocationMetadata::GetSizeOfAllocationFromMetadata(metadata))); |
| SbMemoryDeallocate(metadata); |
| } |
| |
| static inline char* js_strdup(const char* s) |
| { |
| size_t length = SbStringGetLength(s) + 1; |
| char* new_ptr = reinterpret_cast<char*>(js_malloc(length)); |
| SbStringCopy(new_ptr, s, length); |
| return new_ptr; |
| } |
| |
| #endif // gc_JSCUSTOMALLOCATOR_H |