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