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