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 21using std::nothrow; 22 23 24FontCache 25FontCache::sDefaultInstance; 26 27// #pragma mark - 28 29// constructor 30FontCache::FontCache() 31 : MultiLocker("FontCache lock") 32 , fFontCacheEntries() 33{ 34} 35 36// destructor 37FontCache::~FontCache() 38{ 39 FontMap::Iterator iterator = fFontCacheEntries.GetIterator(); 40 while (iterator.HasNext()) 41 iterator.Next().value->ReleaseReference(); 42} 43 44// Default 45/*static*/ FontCache* 46FontCache::Default() 47{ 48 return &sDefaultInstance; 49} 50 51// FontCacheEntryFor 52FontCacheEntry* 53FontCache::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 103void 104FontCache::Recycle(FontCacheEntry* entry) 105{ 106//printf("Recycle(%p)\n", entry); 107 if (!entry) 108 return; 109 entry->UpdateUsage(); 110 entry->ReleaseReference(); 111} 112 113static const int32 kMaxEntryCount = 30; 114 115static inline double 116usage_index(uint64 useCount, bigtime_t age) 117{ 118 return 100.0 * useCount / age; 119} 120 121// _ConstrainEntryCount 122void 123FontCache::_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