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