| // 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 |