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}
40
41// Default
42/*static*/ FontCache*
43FontCache::Default()
44{
45	return &sDefaultInstance;
46}
47
48// FontCacheEntryFor
49FontCacheEntry*
50FontCache::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
98void
99FontCache::Recycle(FontCacheEntry* entry)
100{
101//printf("Recycle(%p)\n", entry);
102	if (!entry)
103		return;
104	entry->UpdateUsage();
105	entry->ReleaseReference();
106}
107
108static const int32 kMaxEntryCount = 30;
109
110static inline double
111usage_index(uint64 useCount, bigtime_t age)
112{
113	return 100.0 * useCount / age;
114}
115
116// _ConstrainEntryCount
117void
118FontCache::_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