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) 54 { 55 static const size_t signatureSize = 512; 56 char signature[signatureSize]; 57 FontCacheEntry::GenerateSignature(signature, signatureSize, font); 58 59 AutoReadLocker readLocker(this); 60 61 FontCacheEntry* entry = fFontCacheEntries.Get(signature); 62 63 if (entry) { 64 // the entry was already there 65 entry->AcquireReference(); 66 //printf("FontCacheEntryFor(%ld): %p\n", font.GetFamilyAndStyle(), entry); 67 return entry; 68 } 69 70 readLocker.Unlock(); 71 72 AutoWriteLocker locker(this); 73 if (!locker.IsLocked()) 74 return NULL; 75 76 // prevent getting screwed by a race condition: 77 // when we released the readlock above, another thread might have 78 // gotten the writelock before we have, and might have already 79 // inserted a cache entry for this font. So we look again if there 80 // is an entry now, and only then create it if it's still not there, 81 // all while holding the writelock 82 entry = fFontCacheEntries.Get(signature); 83 84 if (!entry) { 85 // remove old entries, keep entries below certain count 86 _ConstrainEntryCount(); 87 entry = new (nothrow) FontCacheEntry(); 88 if (!entry || !entry->Init(font) 89 || fFontCacheEntries.Put(signature, entry) < B_OK) { 90 fprintf(stderr, "FontCache::FontCacheEntryFor() - " 91 "out of memory or no font file\n"); 92 delete entry; 93 return NULL; 94 } 95 } 96 //printf("FontCacheEntryFor(%ld): %p (insert)\n", font.GetFamilyAndStyle(), entry); 97 98 entry->AcquireReference(); 99 return entry; 100 } 101 102 // Recycle 103 void 104 FontCache::Recycle(FontCacheEntry* entry) 105 { 106 //printf("Recycle(%p)\n", entry); 107 if (!entry) 108 return; 109 entry->UpdateUsage(); 110 entry->ReleaseReference(); 111 } 112 113 static const int32 kMaxEntryCount = 30; 114 115 static inline double 116 usage_index(uint64 useCount, bigtime_t age) 117 { 118 return 100.0 * useCount / age; 119 } 120 121 // _ConstrainEntryCount 122 void 123 FontCache::_ConstrainEntryCount() 124 { 125 // this function is only ever called with the WriteLock held 126 if (fFontCacheEntries.Size() < kMaxEntryCount) 127 return; 128 //printf("FontCache::_ConstrainEntryCount()\n"); 129 130 FontMap::Iterator iterator = fFontCacheEntries.GetIterator(); 131 132 // NOTE: if kMaxEntryCount has a sane value, there has got to be 133 // some entries, so using the iterator like that should be ok 134 FontCacheEntry* leastUsedEntry = iterator.Next().value; 135 bigtime_t now = system_time(); 136 bigtime_t age = now - leastUsedEntry->LastUsed(); 137 uint64 useCount = leastUsedEntry->UsedCount(); 138 double leastUsageIndex = usage_index(useCount, age); 139 //printf(" leastUsageIndex: %f\n", leastUsageIndex); 140 141 while (iterator.HasNext()) { 142 FontCacheEntry* entry = iterator.Next().value; 143 age = now - entry->LastUsed(); 144 useCount = entry->UsedCount(); 145 double usageIndex = usage_index(useCount, age); 146 //printf(" usageIndex: %f\n", usageIndex); 147 if (usageIndex < leastUsageIndex) { 148 leastUsedEntry = entry; 149 leastUsageIndex = usageIndex; 150 } 151 } 152 153 iterator = fFontCacheEntries.GetIterator(); 154 while (iterator.HasNext()) { 155 if (iterator.Next().value == leastUsedEntry) { 156 iterator.Remove(); 157 leastUsedEntry->ReleaseReference(); 158 break; 159 } 160 } 161 } 162