xref: /haiku/src/servers/app/font/FontCache.cpp (revision 002f37b0cca92e4cf72857c72ac95db5a8b09615)
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