1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Sonic Visualiser
5 An audio file viewer and annotation editor.
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006 QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "StorageAdviser.h"
17
18 #include "Exceptions.h"
19 #include "TempDirectory.h"
20
21 #include "system/System.h"
22
23 #include <iostream>
24
25 QString
criteriaToString(int criteria)26 StorageAdviser::criteriaToString(int criteria)
27 {
28 QStringList labels;
29 if (criteria & SpeedCritical) labels.push_back("SpeedCritical");
30 if (criteria & PrecisionCritical) labels.push_back("PrecisionCritical");
31 if (criteria & LongRetentionLikely) labels.push_back("LongRetentionLikely");
32 if (criteria & FrequentLookupLikely) labels.push_back("FrequentLookupLikely");
33 if (labels.empty()) return "None";
34 else return labels.join("+");
35 }
36
37 QString
recommendationToString(int recommendation)38 StorageAdviser::recommendationToString(int recommendation)
39 {
40 QStringList labels;
41 if (recommendation & UseMemory) labels.push_back("UseMemory");
42 if (recommendation & PreferMemory) labels.push_back("PreferMemory");
43 if (recommendation & PreferDisc) labels.push_back("PreferDisc");
44 if (recommendation & UseDisc) labels.push_back("UseDisc");
45 if (recommendation & ConserveSpace) labels.push_back("ConserveSpace");
46 if (recommendation & UseAsMuchAsYouLike) labels.push_back("UseAsMuchAsYouLike");
47 if (labels.empty()) return "None";
48 else return labels.join("+");
49 }
50
51 QString
storageStatusToString(StorageStatus status)52 StorageAdviser::storageStatusToString(StorageStatus status)
53 {
54 if (status == Insufficient) return "Insufficient";
55 if (status == Marginal) return "Marginal";
56 if (status == Sufficient) return "Sufficient";
57 return "Unknown";
58 }
59
60 size_t StorageAdviser::m_discPlanned = 0;
61 size_t StorageAdviser::m_memoryPlanned = 0;
62
63 StorageAdviser::Recommendation
64 StorageAdviser::m_baseRecommendation = StorageAdviser::NoRecommendation;
65
66 StorageAdviser::Recommendation
recommend(Criteria criteria,size_t minimumSize,size_t maximumSize)67 StorageAdviser::recommend(Criteria criteria,
68 size_t minimumSize,
69 size_t maximumSize)
70 {
71 SVDEBUG << "StorageAdviser::recommend: criteria " << criteria
72 << " (" + criteriaToString(criteria) + ")"
73 << ", minimumSize " << minimumSize
74 << ", maximumSize " << maximumSize << endl;
75
76 if (m_baseRecommendation != NoRecommendation) {
77 SVDEBUG << "StorageAdviser::recommend: Returning fixed recommendation "
78 << m_baseRecommendation << " ("
79 << recommendationToString(m_baseRecommendation) << ")" << endl;
80 return m_baseRecommendation; // for now
81 }
82
83 QString path;
84 try {
85 path = TempDirectory::getInstance()->getPath();
86 } catch (const std::exception &e) {
87 SVDEBUG << "StorageAdviser::recommend: ERROR: Failed to get temporary directory path: " << e.what() << endl;
88 int r = UseMemory | ConserveSpace;
89 SVDEBUG << "StorageAdviser: returning fallback " << r
90 << " (" << recommendationToString(r) << ")" << endl;
91 return Recommendation(r);
92 }
93 ssize_t discFree = GetDiscSpaceMBAvailable(path.toLocal8Bit());
94 ssize_t memoryFree, memoryTotal;
95 GetRealMemoryMBAvailable(memoryFree, memoryTotal);
96
97 SVDEBUG << "StorageAdviser: disc space: " << discFree
98 << "M, memory free: " << memoryFree
99 << "M, memory total: " << memoryTotal << "M" << endl;
100
101 // In 32-bit addressing mode we can't address more than 4Gb.
102 // If the total memory is reported as more than 4Gb, we should
103 // reduce the available amount by the difference between 4Gb
104 // and the total. This won't give us an accurate idea of the
105 // amount of memory available any more, but it should be enough
106 // to prevent us from trying to allocate more for our own use
107 // than can be addressed at all!
108 if (sizeof(void *) < 8) {
109 if (memoryTotal > 4096) {
110 ssize_t excess = memoryTotal - 4096;
111 if (memoryFree > excess) {
112 memoryFree -= excess;
113 } else {
114 memoryFree = 0;
115 }
116 SVDEBUG << "StorageAdviser: more real memory found than we "
117 << "can address in a 32-bit process, reducing free "
118 << "estimate to " << memoryFree << "M accordingly" << endl;
119 }
120 }
121
122 SVDEBUG << "StorageAdviser: disc planned: " << (m_discPlanned / 1024)
123 << "K, memory planned: " << (m_memoryPlanned / 1024) << "K" << endl;
124 SVDEBUG << "StorageAdviser: min requested: " << minimumSize
125 << "K, max requested: " << maximumSize << "K" << endl;
126
127 if (discFree > ssize_t(m_discPlanned / 1024 + 1)) {
128 discFree -= m_discPlanned / 1024 + 1;
129 } else if (discFree > 0) { // can also be -1 for unknown
130 discFree = 0;
131 }
132
133 if (memoryFree > ssize_t(m_memoryPlanned / 1024 + 1)) {
134 memoryFree -= m_memoryPlanned / 1024 + 1;
135 } else if (memoryFree > 0) { // can also be -1 for unknown
136 memoryFree = 0;
137 }
138
139 //!!! We have a potentially serious problem here if multiple
140 //recommendations are made in advance of any of the resulting
141 //allocations, as the allocations that have been recommended for
142 //won't be taken into account in subsequent recommendations.
143
144 StorageStatus memoryStatus = Unknown;
145 StorageStatus discStatus = Unknown;
146
147 ssize_t minmb = ssize_t(minimumSize / 1024 + 1);
148 ssize_t maxmb = ssize_t(maximumSize / 1024 + 1);
149
150 if (memoryFree == -1) memoryStatus = Unknown;
151 else if (memoryFree < memoryTotal / 3 && memoryFree < 512) memoryStatus = Insufficient;
152 else if (minmb > (memoryFree * 3) / 4) memoryStatus = Insufficient;
153 else if (maxmb > (memoryFree * 3) / 4) memoryStatus = Marginal;
154 else if (minmb > (memoryFree / 3)) memoryStatus = Marginal;
155 else if (memoryTotal == -1 ||
156 minmb > (memoryTotal / 10)) memoryStatus = Marginal;
157 else memoryStatus = Sufficient;
158
159 if (discFree == -1) discStatus = Unknown;
160 else if (minmb > (discFree * 3) / 4) discStatus = Insufficient;
161 else if (maxmb > (discFree / 4)) discStatus = Marginal;
162 else if (minmb > (discFree / 10)) discStatus = Marginal;
163 else discStatus = Sufficient;
164
165 SVDEBUG << "StorageAdviser: memory status: " << memoryStatus
166 << " (" << storageStatusToString(memoryStatus) << ")"
167 << ", disc status " << discStatus
168 << " (" << storageStatusToString(discStatus) << ")" << endl;
169
170 int recommendation = NoRecommendation;
171
172 if (memoryStatus == Insufficient || memoryStatus == Unknown) {
173
174 recommendation |= UseDisc;
175
176 if (discStatus == Insufficient && minmb > discFree) {
177 throw InsufficientDiscSpace(path, minmb, discFree);
178 }
179
180 if (discStatus == Insufficient || discStatus == Marginal) {
181 recommendation |= ConserveSpace;
182 } else if (discStatus == Unknown && !(criteria & PrecisionCritical)) {
183 recommendation |= ConserveSpace;
184 } else {
185 recommendation |= UseAsMuchAsYouLike;
186 }
187
188 } else if (memoryStatus == Marginal) {
189
190 if (((criteria & SpeedCritical) ||
191 (criteria & FrequentLookupLikely)) &&
192 !(criteria & PrecisionCritical) &&
193 !(criteria & LongRetentionLikely)) {
194
195 // requirements suggest a preference for memory
196
197 if (discStatus != Insufficient) {
198 recommendation |= PreferMemory;
199 } else {
200 recommendation |= UseMemory;
201 }
202
203 recommendation |= ConserveSpace;
204
205 } else {
206
207 if (discStatus == Insufficient) {
208 recommendation |= (UseMemory | ConserveSpace);
209 } else if (discStatus == Marginal) {
210 recommendation |= (PreferMemory | ConserveSpace);
211 } else if (discStatus == Unknown) {
212 recommendation |= (PreferDisc | ConserveSpace);
213 } else {
214 recommendation |= (UseDisc | UseAsMuchAsYouLike);
215 }
216 }
217
218 } else {
219
220 if (discStatus == Insufficient) {
221 recommendation |= (UseMemory | ConserveSpace);
222 } else if (discStatus != Sufficient) {
223 recommendation |= (PreferMemory | ConserveSpace);
224 } else {
225
226 if ((criteria & SpeedCritical) ||
227 (criteria & FrequentLookupLikely)) {
228 recommendation |= PreferMemory;
229 if (criteria & PrecisionCritical) {
230 recommendation |= UseAsMuchAsYouLike;
231 } else {
232 recommendation |= ConserveSpace;
233 }
234 } else {
235 recommendation |= PreferDisc;
236 recommendation |= UseAsMuchAsYouLike;
237 }
238 }
239 }
240
241 SVDEBUG << "StorageAdviser: returning recommendation " << recommendation
242 << " (" << recommendationToString(recommendation) << ")" << endl;
243
244 return Recommendation(recommendation);
245 }
246
247 void
notifyPlannedAllocation(AllocationArea area,size_t size)248 StorageAdviser::notifyPlannedAllocation(AllocationArea area, size_t size)
249 {
250 if (area == MemoryAllocation) m_memoryPlanned += size;
251 else if (area == DiscAllocation) m_discPlanned += size;
252 SVDEBUG << "StorageAdviser: storage planned up: now memory: " << m_memoryPlanned << ", disc "
253 << m_discPlanned << endl;
254 }
255
256 void
notifyDoneAllocation(AllocationArea area,size_t size)257 StorageAdviser::notifyDoneAllocation(AllocationArea area, size_t size)
258 {
259 if (area == MemoryAllocation) {
260 if (m_memoryPlanned > size) m_memoryPlanned -= size;
261 else m_memoryPlanned = 0;
262 } else if (area == DiscAllocation) {
263 if (m_discPlanned > size) m_discPlanned -= size;
264 else m_discPlanned = 0;
265 }
266 SVDEBUG << "StorageAdviser: storage planned down: now memory: " << m_memoryPlanned << ", disc "
267 << m_discPlanned << endl;
268 }
269
270 void
setFixedRecommendation(Recommendation recommendation)271 StorageAdviser::setFixedRecommendation(Recommendation recommendation)
272 {
273 m_baseRecommendation = recommendation;
274 }
275
276