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