1 /* 2 * Copyright 2007, Haiku. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Stephan Aßmus <superstippi@gmx.de> 7 */ 8 9 #include "FontCache.h" 10 11 #include <new> 12 #include <stdio.h> 13 #include <string.h> 14 15 #include <Entry.h> 16 #include <Path.h> 17 18 #include "AutoLocker.h" 19 20 21 using std::nothrow; 22 23 24 FontCache 25 FontCache::sDefaultInstance; 26 27 // #pragma mark - 28 29 // constructor 30 FontCache::FontCache() 31 : MultiLocker("FontCache lock") 32 , fFontCacheEntries() 33 { 34 } 35 36 // destructor 37 FontCache::~FontCache() 38 { 39 FontMap::Iterator iterator = fFontCacheEntries.GetIterator(); 40 while (iterator.HasNext()) 41 iterator.Next().value->ReleaseReference(); 42 } 43 44 // Default 45 /*static*/ FontCache* 46 FontCache::Default() 47 { 48 return &sDefaultInstance; 49 } 50 51 // FontCacheEntryFor 52 FontCacheEntry* 53 FontCache::FontCacheEntryFor(const ServerFont& font, bool forceVector) 54 { 55 static const size_t signatureSize = 512; 56 char signature[signatureSize]; 57 FontCacheEntry::GenerateSignature(signature, signatureSize, font, 58 forceVector); 59 60 AutoReadLocker readLocker(this); 61 62 FontCacheEntry* entry = fFontCacheEntries.Get(signature); 63 64 if (entry) { 65 // the entry was already there 66 entry->AcquireReference(); 67 //printf("FontCacheEntryFor(%ld): %p\n", font.GetFamilyAndStyle(), entry); 68 return entry; 69 } 70 71 readLocker.Unlock(); 72 73 AutoWriteLocker locker(this); 74 if (!locker.IsLocked()) 75 return NULL; 76 77 // prevent getting screwed by a race condition: 78 // when we released the readlock above, another thread might have 79 // gotten the writelock before we have, and might have already 80 // inserted a cache entry for this font. So we look again if there 81 // is an entry now, and only then create it if it's still not there, 82 // all while holding the writelock 83 entry = fFontCacheEntries.Get(signature); 84 85 if (!entry) { 86 // remove old entries, keep entries below certain count 87 _ConstrainEntryCount(); 88 entry = new (nothrow) FontCacheEntry(); 89 if (!entry || !entry->Init(font, forceVector) 90 || fFontCacheEntries.Put(signature, entry) < B_OK) { 91 fprintf(stderr, "FontCache::FontCacheEntryFor() - " 92 "out of memory or no font file\n"); 93 delete entry; 94 return NULL; 95 } 96 } 97 //printf("FontCacheEntryFor(%ld): %p (insert)\n", font.GetFamilyAndStyle(), entry); 98 99 entry->AcquireReference(); 100 return entry; 101 } 102 103 // Recycle 104 void 105 FontCache::Recycle(FontCacheEntry* entry) 106 { 107 //printf("Recycle(%p)\n", entry); 108 if (!entry) 109 return; 110 entry->UpdateUsage(); 111 entry->ReleaseReference(); 112 } 113 114 static const int32 kMaxEntryCount = 30; 115 116 static inline double 117 usage_index(uint64 useCount, bigtime_t age) 118 { 119 return 100.0 * useCount / age; 120 } 121 122 // _ConstrainEntryCount 123 void 124 FontCache::_ConstrainEntryCount() 125 { 126 // this function is only ever called with the WriteLock held 127 if (fFontCacheEntries.Size() < kMaxEntryCount) 128 return; 129 //printf("FontCache::_ConstrainEntryCount()\n"); 130 131 FontMap::Iterator iterator = fFontCacheEntries.GetIterator(); 132 133 // NOTE: if kMaxEntryCount has a sane value, there has got to be 134 // some entries, so using the iterator like that should be ok 135 FontCacheEntry* leastUsedEntry = iterator.Next().value; 136 bigtime_t now = system_time(); 137 bigtime_t age = now - leastUsedEntry->LastUsed(); 138 uint64 useCount = leastUsedEntry->UsedCount(); 139 double leastUsageIndex = usage_index(useCount, age); 140 //printf(" leastUsageIndex: %f\n", leastUsageIndex); 141 142 while (iterator.HasNext()) { 143 FontCacheEntry* entry = iterator.Next().value; 144 age = now - entry->LastUsed(); 145 useCount = entry->UsedCount(); 146 double usageIndex = usage_index(useCount, age); 147 //printf(" usageIndex: %f\n", usageIndex); 148 if (usageIndex < leastUsageIndex) { 149 leastUsedEntry = entry; 150 leastUsageIndex = usageIndex; 151 } 152 } 153 154 iterator = fFontCacheEntries.GetIterator(); 155 while (iterator.HasNext()) { 156 if (iterator.Next().value == leastUsedEntry) { 157 iterator.Remove(); 158 leastUsedEntry->ReleaseReference(); 159 break; 160 } 161 } 162 } 163