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
FontCache()30 FontCache::FontCache()
31 : MultiLocker("FontCache lock")
32 , fFontCacheEntries()
33 {
34 }
35
36 // destructor
~FontCache()37 FontCache::~FontCache()
38 {
39 }
40
41 // Default
42 /*static*/ FontCache*
Default()43 FontCache::Default()
44 {
45 return &sDefaultInstance;
46 }
47
48 // FontCacheEntryFor
49 FontCacheEntry*
FontCacheEntryFor(const ServerFont & font,bool forceVector)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
Recycle(FontCacheEntry * entry)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
usage_index(uint64 useCount,bigtime_t age)111 usage_index(uint64 useCount, bigtime_t age)
112 {
113 return 100.0 * useCount / age;
114 }
115
116 // _ConstrainEntryCount
117 void
_ConstrainEntryCount()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