26 |
|
|
27 |
#include <set> |
#include <set> |
28 |
#include <map> |
#include <map> |
29 |
|
#include <vector> |
30 |
|
|
31 |
/** |
#include "Exception.h" |
32 |
|
|
33 |
|
namespace LinuxSampler { |
34 |
|
|
35 |
|
/** @brief Interface class for Resource Consumers. |
36 |
|
* |
37 |
* Interface class for consumer classes which use a resource managed |
* Interface class for consumer classes which use a resource managed |
38 |
* by the ResourceManager class. |
* by the ResourceManager class. All classes which use the ResourceManager |
39 |
|
* to aquire resources have to derive from this interface class and |
40 |
|
* implement the abstract methods. |
41 |
*/ |
*/ |
42 |
template<class T_res> |
template<class T_res> |
43 |
class ResourceConsumer { |
class ResourceConsumer { |
82 |
virtual void OnResourceProgress(float fProgress) = 0; |
virtual void OnResourceProgress(float fProgress) = 0; |
83 |
}; |
}; |
84 |
|
|
85 |
/** |
/** @brief Manager for sharing resources. |
86 |
|
* |
87 |
* Abstract base class for sharing resources between multiple consumers. |
* Abstract base class for sharing resources between multiple consumers. |
88 |
* A consumer can borrow a resource from the ResourceManager, if the |
* A consumer can borrow a resource from the ResourceManager, if the |
89 |
* resource doesn't exist yet it will be created. Other consumers will |
* resource doesn't exist yet it will be created. Other consumers will |
90 |
* just be given the same pointer to the resource then. When all consumers |
* just be given the same pointer to the resource then. When all consumers |
91 |
* gave back their pointer to the resource, the resource will be destroyed. |
* gave back their pointer to the resource, the resource will (by default) |
92 |
|
* be destroyed. |
93 |
|
* |
94 |
* Descendants of this base class have to implement the (protected) |
* Descendants of this base class have to implement the (protected) |
95 |
* Create() and Destroy() methods to create and destroy a resource. |
* Create() and Destroy() methods to create and destroy a resource. |
96 |
*/ |
*/ |
97 |
template<class T_key, class T_res> |
template<class T_key, class T_res> |
98 |
class ResourceManager { |
class ResourceManager { |
99 |
|
public: |
100 |
|
/** |
101 |
|
* Defines life-time strategy for resources. |
102 |
|
*/ |
103 |
|
enum mode_t { |
104 |
|
ON_DEMAND = 0, ///< Create resource when needed, free it once not needed anymore (default behavior). |
105 |
|
ON_DEMAND_HOLD = 1, ///< Create resource when needed and keep it even if not needed anymore. |
106 |
|
PERSISTENT = 2 ///< Immediately create resource and keep it. |
107 |
|
}; |
108 |
|
|
109 |
private: |
private: |
110 |
typedef std::set<ResourceConsumer<T_res>*> ConsumerSet; |
typedef std::set<ResourceConsumer<T_res>*> ConsumerSet; |
111 |
struct resource_entry_t { |
struct resource_entry_t { |
112 |
T_key key; |
T_key key; |
113 |
T_res* resource; ///< pointer to the resource |
T_res* resource; ///< pointer to the resource |
114 |
|
mode_t mode; ///< When should the resource be created? When should it be destroyed? |
115 |
ConsumerSet consumers; ///< list of all consumers who currently use the resource |
ConsumerSet consumers; ///< list of all consumers who currently use the resource |
116 |
void* arg; ///< optional pointer the descendant might use to store informations about the resource |
void* lifearg; ///< optional pointer the descendant might use to store informations about a created resource |
117 |
|
void* entryarg; ///< optional pointer the descendant might use to store informations about an entry |
118 |
}; |
}; |
119 |
typedef std::map<T_key, resource_entry_t> ResourceMap; |
typedef std::map<T_key, resource_entry_t> ResourceMap; |
120 |
ResourceMap ResourceEntries; |
ResourceMap ResourceEntries; |
121 |
|
|
122 |
public: |
public: |
123 |
virtual ~ResourceManager() {} |
/** |
124 |
|
* Returns (the keys of) all current entries of this |
125 |
|
* ResourceManager instance. |
126 |
|
*/ |
127 |
|
std::vector<T_key> Entries() { |
128 |
|
std::vector<T_key> result; |
129 |
|
for (typename ResourceMap::iterator iter = ResourceEntries.begin(); |
130 |
|
iter != ResourceEntries.end(); iter++) |
131 |
|
{ |
132 |
|
result.push_back(iter->first); |
133 |
|
} |
134 |
|
return result; |
135 |
|
} |
136 |
|
|
137 |
/** |
/** |
138 |
* Borrow a resource identified by \a Key. The ResourceManager will |
* Borrow a resource identified by \a Key. The ResourceManager will |
139 |
* mark the resource as in usage by the consumer given with |
* mark the resource as in usage by the consumer given with |
145 |
* @returns pointer to resource |
* @returns pointer to resource |
146 |
*/ |
*/ |
147 |
T_res* Borrow(T_key Key, ResourceConsumer<T_res>* pConsumer) { |
T_res* Borrow(T_key Key, ResourceConsumer<T_res>* pConsumer) { |
148 |
|
// search for an entry for this resource |
149 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
150 |
if (iterEntry == ResourceEntries.end()) { |
if (iterEntry == ResourceEntries.end()) { // entry doesn't exist yet |
151 |
// already create an entry for the resource |
// already create an entry for the resource |
152 |
resource_entry_t entry; |
resource_entry_t entry; |
153 |
entry.key = Key; |
entry.key = Key; |
154 |
entry.resource = NULL; |
entry.resource = NULL; |
155 |
|
entry.mode = ON_DEMAND; // default mode |
156 |
|
entry.lifearg = NULL; |
157 |
|
entry.entryarg = NULL; |
158 |
entry.consumers.insert(pConsumer); |
entry.consumers.insert(pConsumer); |
159 |
ResourceEntries[Key] = entry; |
ResourceEntries[Key] = entry; |
160 |
try { |
try { |
161 |
// actually create the resource |
// actually create the resource |
162 |
entry.resource = Create(Key, pConsumer, entry.arg); |
entry.resource = Create(Key, pConsumer, entry.lifearg); |
163 |
} catch (...) { |
} catch (...) { |
164 |
// creating the resource failed, so remove the entry |
// creating the resource failed, so remove the entry |
165 |
ResourceEntries.erase(Key); |
ResourceEntries.erase(Key); |
168 |
} |
} |
169 |
// now update the entry with the created resource |
// now update the entry with the created resource |
170 |
ResourceEntries[Key] = entry; |
ResourceEntries[Key] = entry; |
171 |
OnBorrow(entry.resource, pConsumer, entry.arg); |
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
172 |
|
return entry.resource; |
173 |
|
} else { // entry already exists |
174 |
|
resource_entry_t& entry = iterEntry->second; |
175 |
|
if (!entry.resource) { // create resource if not created already |
176 |
|
try { |
177 |
|
entry.resource = Create(Key, pConsumer, entry.lifearg); |
178 |
|
} catch (...) { |
179 |
|
entry.resource = NULL; |
180 |
|
throw; // rethrow the same exception |
181 |
|
} |
182 |
|
} |
183 |
|
entry.consumers.insert(pConsumer); |
184 |
|
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
185 |
return entry.resource; |
return entry.resource; |
186 |
} |
} |
|
resource_entry_t& entry = iterEntry->second; |
|
|
entry.consumers.insert(pConsumer); |
|
|
OnBorrow(entry.resource, pConsumer, entry.arg); |
|
|
return entry.resource; |
|
187 |
} |
} |
188 |
|
|
189 |
/** |
/** |
190 |
* Give back a resource. This tells the ResourceManager that the |
* Give back a resource. This tells the ResourceManager that the |
191 |
* consumer given by \a pConsumer doesn't need the resource anymore. |
* consumer given by \a pConsumer doesn't need the resource anymore. |
192 |
* If the resource is not needed by any consumer anymore then the |
* If the resource is not needed by any consumer anymore and the |
193 |
* resource will be destroyed. |
* resource has a life-time strategy of ON_DEMAND (which is the |
194 |
|
* default setting) then the resource will be destroyed. |
195 |
* |
* |
196 |
* @param pResource - pointer to resource |
* @param pResource - pointer to resource |
197 |
* @param pConsumer - identifier of the consumer who borrowed the |
* @param pConsumer - identifier of the consumer who borrowed the |
198 |
* resource |
* resource |
199 |
*/ |
*/ |
200 |
void HandBack(T_res* pResource, ResourceConsumer<T_res>* pConsumer) { |
void HandBack(T_res* pResource, ResourceConsumer<T_res>* pConsumer) { |
201 |
|
// search for the entry associated with the given resource |
202 |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
203 |
typename ResourceMap::iterator end = ResourceEntries.end(); |
typename ResourceMap::iterator end = ResourceEntries.end(); |
204 |
for (; iter != end; iter++) { |
for (; iter != end; iter++) { |
205 |
if (iter->second.resource == pResource) { |
if (iter->second.resource == pResource) { // found entry for resource |
206 |
resource_entry_t& entry = iter->second; |
resource_entry_t& entry = iter->second; |
207 |
entry.consumers.erase(pConsumer); |
entry.consumers.erase(pConsumer); |
208 |
if (entry.consumers.empty()) { |
// remove entry if necessary |
209 |
|
if (entry.mode == ON_DEMAND && !entry.entryarg && entry.consumers.empty()) { |
210 |
T_res* resource = entry.resource; |
T_res* resource = entry.resource; |
211 |
void* arg = entry.arg; |
void* arg = entry.lifearg; |
212 |
ResourceEntries.erase(iter); |
ResourceEntries.erase(iter); |
213 |
Destroy(resource, arg); |
// destroy resource if necessary |
214 |
|
if (resource) Destroy(resource, arg); |
215 |
} |
} |
216 |
return; |
return; |
217 |
} |
} |
219 |
} |
} |
220 |
|
|
221 |
/** |
/** |
222 |
* Request update of a resource. |
* Request update of a resource. All consumers will be informed |
223 |
|
* about the pending update of the resource so they can safely react |
224 |
|
* by stopping its usage first, then the resource will be recreated |
225 |
|
* and finally the consumers will be informed once the update was |
226 |
|
* completed, so they can continue to use the resource. |
227 |
* |
* |
228 |
* @param pResource - resource to be updated |
* @param pResource - resource to be updated |
229 |
* @param pConsumer - consumer who requested the update |
* @param pConsumer - consumer who requested the update |
246 |
} |
} |
247 |
// update resource |
// update resource |
248 |
T_res* pOldResource = entry.resource; |
T_res* pOldResource = entry.resource; |
249 |
Destroy(entry.resource, entry.arg); |
Destroy(entry.resource, entry.lifearg); |
250 |
entry.resource = Create(entry.key, pConsumer, entry.arg); |
entry.resource = Create(entry.key, pConsumer, entry.lifearg); |
251 |
// inform all consumers about update completed |
// inform all consumers about update completed |
252 |
iterCons = entry.consumers.begin(); |
iterCons = entry.consumers.begin(); |
253 |
endCons = entry.consumers.end(); |
endCons = entry.consumers.end(); |
262 |
} |
} |
263 |
} |
} |
264 |
|
|
265 |
|
/** |
266 |
|
* Returns the life-time strategy of the given resource. |
267 |
|
* |
268 |
|
* @param Key - ID of the resource |
269 |
|
*/ |
270 |
|
mode_t AvailabilityMode(T_key Key) { |
271 |
|
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
272 |
|
if (iterEntry == ResourceEntries.end()) |
273 |
|
return ON_DEMAND; // resource entry doesn't exist, so we return the default mode |
274 |
|
resource_entry_t& entry = iterEntry->second; |
275 |
|
return entry.mode; |
276 |
|
} |
277 |
|
|
278 |
|
/** |
279 |
|
* Change life-time strategy of the given resource. If a life-time |
280 |
|
* strategy of PERSISTENT was given and the resource was not created |
281 |
|
* yet, it will immediately be created and this method will block |
282 |
|
* until the resource creation was completed. |
283 |
|
* |
284 |
|
* @param Key - ID of the resource |
285 |
|
* @param Mode - life-time strategy of resource to be set |
286 |
|
* @throws Exception in case an invalid Mode was given |
287 |
|
*/ |
288 |
|
void SetAvailabilityMode(T_key Key, mode_t Mode) throw (Exception) { |
289 |
|
if (Mode != ON_DEMAND && Mode != ON_DEMAND_HOLD && Mode != PERSISTENT) |
290 |
|
throw Exception("ResourceManager::SetAvailabilityMode(): invalid mode"); |
291 |
|
|
292 |
|
// search for an entry for this resource |
293 |
|
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
294 |
|
resource_entry_t* pEntry = NULL; |
295 |
|
if (iterEntry == ResourceEntries.end()) { // resource entry doesn't exist |
296 |
|
if (Mode == ON_DEMAND) return; // we don't create an entry for the default value |
297 |
|
// create an entry for the resource |
298 |
|
pEntry = &ResourceEntries[Key]; |
299 |
|
pEntry->key = Key; |
300 |
|
pEntry->resource = NULL; |
301 |
|
pEntry->mode = Mode; |
302 |
|
pEntry->lifearg = NULL; |
303 |
|
pEntry->entryarg = NULL; |
304 |
|
} else { // resource entry exists |
305 |
|
pEntry = &iterEntry->second; |
306 |
|
// remove entry if necessary |
307 |
|
if (Mode == ON_DEMAND && !pEntry->entryarg && pEntry->consumers.empty()) { |
308 |
|
T_res* resource = pEntry->resource; |
309 |
|
void* arg = pEntry->lifearg; |
310 |
|
ResourceEntries.erase(iterEntry); |
311 |
|
// destroy resource if necessary |
312 |
|
if (resource) Destroy(resource, arg); |
313 |
|
return; // done |
314 |
|
} |
315 |
|
pEntry->mode = Mode; // apply new mode |
316 |
|
} |
317 |
|
|
318 |
|
// already create the resource if necessary |
319 |
|
if (pEntry->mode == PERSISTENT && !pEntry->resource) { |
320 |
|
try { |
321 |
|
// actually create the resource |
322 |
|
pEntry->resource = Create(Key, NULL /*no consumer yet*/, pEntry->lifearg); |
323 |
|
} catch (...) { |
324 |
|
// creating the resource failed, so skip it for now |
325 |
|
pEntry->resource = NULL; |
326 |
|
// rethrow the same exception |
327 |
|
throw; |
328 |
|
} |
329 |
|
} |
330 |
|
} |
331 |
|
|
332 |
|
/** |
333 |
|
* Returns custom data sticked to the given resource previously by |
334 |
|
* a SetCustomData() call. |
335 |
|
* |
336 |
|
* @param Key - ID of resource |
337 |
|
*/ |
338 |
|
void* CustomData(T_key Key) { |
339 |
|
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
340 |
|
if (iterEntry == ResourceEntries.end()) return NULL; // resource entry doesn't exist, so we return the default mode |
341 |
|
resource_entry_t& entry = iterEntry->second; |
342 |
|
return entry.entryarg; |
343 |
|
} |
344 |
|
|
345 |
|
/** |
346 |
|
* This method can be used to stick custom data to an resource |
347 |
|
* entry. In case the custom data is not needed anymore, you should |
348 |
|
* call this method again and set \a pData to NULL, so the |
349 |
|
* ResourceManager might safe space by removing the respective |
350 |
|
* entry if not needed anymore. |
351 |
|
* |
352 |
|
* @param Key - ID of resource |
353 |
|
* @param pData - pointer to custom data, or NULL if not needed anymore |
354 |
|
*/ |
355 |
|
void SetCustomData(T_key Key, void* pData) { |
356 |
|
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
357 |
|
if (pData) { |
358 |
|
if (iterEntry == ResourceEntries.end()) { // entry doesnt exist, so create one |
359 |
|
resource_entry_t* pEntry = &ResourceEntries[Key]; |
360 |
|
pEntry->key = Key; |
361 |
|
pEntry->resource = NULL; |
362 |
|
pEntry->mode = ON_DEMAND; |
363 |
|
pEntry->lifearg = NULL; |
364 |
|
pEntry->entryarg = pData; // set custom data |
365 |
|
} else { // entry exists, so just update its custom data |
366 |
|
iterEntry->second.entryarg = pData; |
367 |
|
} |
368 |
|
} else { // !pData |
369 |
|
if (iterEntry == ResourceEntries.end()) return; // entry doesnt exist, so nothing to do |
370 |
|
// entry exists, remove it if necessary |
371 |
|
resource_entry_t* pEntry = &iterEntry->second; |
372 |
|
if (pEntry->mode == ON_DEMAND && pEntry->consumers.empty()) { |
373 |
|
ResourceEntries.erase(iterEntry); |
374 |
|
} else iterEntry->second.entryarg = NULL; |
375 |
|
} |
376 |
|
} |
377 |
|
|
378 |
|
virtual ~ResourceManager() {} // due to C++'s nature we cannot destroy created resources here |
379 |
|
|
380 |
protected: |
protected: |
381 |
/** |
/** |
382 |
* Has to be implemented by the descendant to create (allocate) a |
* Has to be implemented by the descendant to create (allocate) a |
444 |
} |
} |
445 |
}; |
}; |
446 |
|
|
447 |
|
} // namespace LinuxSampler |
448 |
|
|
449 |
#endif // __RESOURCE_MANAGER__ |
#endif // __RESOURCE_MANAGER__ |