| // Copyright 2017 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/allocator/malloc_zone_functions_mac.h" | |
| #include "base/atomicops.h" | |
| #include "base/synchronization/lock.h" | |
| namespace base { | |
| namespace allocator { | |
| MallocZoneFunctions g_malloc_zones[kMaxZoneCount]; | |
| static_assert(std::is_pod<MallocZoneFunctions>::value, | |
| "MallocZoneFunctions must be POD"); | |
| void StoreZoneFunctions(const ChromeMallocZone* zone, | |
| MallocZoneFunctions* functions) { | |
| memset(functions, 0, sizeof(MallocZoneFunctions)); | |
| functions->malloc = zone->malloc; | |
| functions->calloc = zone->calloc; | |
| functions->valloc = zone->valloc; | |
| functions->free = zone->free; | |
| functions->realloc = zone->realloc; | |
| functions->size = zone->size; | |
| CHECK(functions->malloc && functions->calloc && functions->valloc && | |
| functions->free && functions->realloc && functions->size); | |
| // These functions might be nullptr. | |
| functions->batch_malloc = zone->batch_malloc; | |
| functions->batch_free = zone->batch_free; | |
| if (zone->version >= 5) { | |
| // Not all custom malloc zones have a memalign. | |
| functions->memalign = zone->memalign; | |
| } | |
| if (zone->version >= 6) { | |
| // This may be nullptr. | |
| functions->free_definite_size = zone->free_definite_size; | |
| } | |
| functions->context = zone; | |
| } | |
| namespace { | |
| // All modifications to g_malloc_zones are gated behind this lock. | |
| // Dispatch to a malloc zone does not need to acquire this lock. | |
| base::Lock& GetLock() { | |
| static base::Lock* g_lock = new base::Lock; | |
| return *g_lock; | |
| } | |
| void EnsureMallocZonesInitializedLocked() { | |
| GetLock().AssertAcquired(); | |
| } | |
| int g_zone_count = 0; | |
| bool IsMallocZoneAlreadyStoredLocked(ChromeMallocZone* zone) { | |
| EnsureMallocZonesInitializedLocked(); | |
| GetLock().AssertAcquired(); | |
| for (int i = 0; i < g_zone_count; ++i) { | |
| if (g_malloc_zones[i].context == reinterpret_cast<void*>(zone)) | |
| return true; | |
| } | |
| return false; | |
| } | |
| } // namespace | |
| bool StoreMallocZone(ChromeMallocZone* zone) { | |
| base::AutoLock l(GetLock()); | |
| EnsureMallocZonesInitializedLocked(); | |
| if (IsMallocZoneAlreadyStoredLocked(zone)) | |
| return false; | |
| if (g_zone_count == kMaxZoneCount) | |
| return false; | |
| StoreZoneFunctions(zone, &g_malloc_zones[g_zone_count]); | |
| ++g_zone_count; | |
| // No other thread can possibly see these stores at this point. The code that | |
| // reads these values is triggered after this function returns. so we want to | |
| // guarantee that they are committed at this stage" | |
| base::subtle::MemoryBarrier(); | |
| return true; | |
| } | |
| bool IsMallocZoneAlreadyStored(ChromeMallocZone* zone) { | |
| base::AutoLock l(GetLock()); | |
| return IsMallocZoneAlreadyStoredLocked(zone); | |
| } | |
| bool DoesMallocZoneNeedReplacing(ChromeMallocZone* zone, | |
| const MallocZoneFunctions* functions) { | |
| return IsMallocZoneAlreadyStored(zone) && zone->malloc != functions->malloc; | |
| } | |
| int GetMallocZoneCountForTesting() { | |
| base::AutoLock l(GetLock()); | |
| return g_zone_count; | |
| } | |
| void ClearAllMallocZonesForTesting() { | |
| base::AutoLock l(GetLock()); | |
| EnsureMallocZonesInitializedLocked(); | |
| memset(g_malloc_zones, 0, kMaxZoneCount * sizeof(MallocZoneFunctions)); | |
| g_zone_count = 0; | |
| } | |
| } // namespace allocator | |
| } // namespace base |