1 |
schoenebeck |
53 |
/*************************************************************************** |
2 |
|
|
* * |
3 |
|
|
* LinuxSampler - modular, streaming capable sampler * |
4 |
|
|
* * |
5 |
schoenebeck |
56 |
* Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck * |
6 |
schoenebeck |
1040 |
* Copyright (C) 2005 - 2007 Christian Schoenebeck * |
7 |
schoenebeck |
53 |
* * |
8 |
|
|
* This program is free software; you can redistribute it and/or modify * |
9 |
|
|
* it under the terms of the GNU General Public License as published by * |
10 |
|
|
* the Free Software Foundation; either version 2 of the License, or * |
11 |
|
|
* (at your option) any later version. * |
12 |
|
|
* * |
13 |
|
|
* This program is distributed in the hope that it will be useful, * |
14 |
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of * |
15 |
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * |
16 |
|
|
* GNU General Public License for more details. * |
17 |
|
|
* * |
18 |
|
|
* You should have received a copy of the GNU General Public License * |
19 |
|
|
* along with this program; if not, write to the Free Software * |
20 |
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, * |
21 |
|
|
* MA 02111-1307 USA * |
22 |
|
|
***************************************************************************/ |
23 |
|
|
|
24 |
|
|
#ifndef __RESOURCE_MANAGER__ |
25 |
|
|
#define __RESOURCE_MANAGER__ |
26 |
|
|
|
27 |
|
|
#include <set> |
28 |
|
|
#include <map> |
29 |
schoenebeck |
947 |
#include <vector> |
30 |
schoenebeck |
53 |
|
31 |
schoenebeck |
947 |
#include "Exception.h" |
32 |
schoenebeck |
970 |
#include "Mutex.h" |
33 |
schoenebeck |
947 |
|
34 |
|
|
namespace LinuxSampler { |
35 |
|
|
|
36 |
|
|
/** @brief Interface class for Resource Consumers. |
37 |
|
|
* |
38 |
schoenebeck |
53 |
* Interface class for consumer classes which use a resource managed |
39 |
schoenebeck |
947 |
* by the ResourceManager class. All classes which use the ResourceManager |
40 |
|
|
* to aquire resources have to derive from this interface class and |
41 |
|
|
* implement the abstract methods. |
42 |
schoenebeck |
53 |
*/ |
43 |
|
|
template<class T_res> |
44 |
|
|
class ResourceConsumer { |
45 |
|
|
public: |
46 |
|
|
/** |
47 |
|
|
* Will be called by the ResourceManager to inform the |
48 |
|
|
* consumer that a resource currently used by him is going |
49 |
|
|
* to be updated. The consumer can then react by stopping |
50 |
|
|
* usage until resource is updated. The ResourceManager will |
51 |
|
|
* not update the resource until this method returns. This |
52 |
|
|
* method needs to be implemented by the consumer. |
53 |
|
|
* |
54 |
|
|
* @param pResource - resource going to be updated |
55 |
|
|
* @param pUpdateArg - pointer the consumer might use to store |
56 |
|
|
* informations he might need when update |
57 |
|
|
* process was completed |
58 |
|
|
*/ |
59 |
|
|
virtual void ResourceToBeUpdated(T_res* pResource, void*& pUpdateArg) = 0; |
60 |
|
|
|
61 |
|
|
/** |
62 |
|
|
* Will be called by the ResourceManager to inform the |
63 |
|
|
* consumer that resource update was completed. This method |
64 |
|
|
* needs to be implemented by the consumer. |
65 |
|
|
* |
66 |
|
|
* @param pOldResource - (now invalid) pointer to the old |
67 |
|
|
* resource |
68 |
|
|
* @param pNewResource - (valid) pointer to the updated |
69 |
|
|
* resource |
70 |
|
|
* @param pUpdateArg - pointer the consumer might have used when |
71 |
|
|
* ResourceToBeUpdated() was called |
72 |
|
|
*/ |
73 |
|
|
virtual void ResourceUpdated(T_res* pOldResource, T_res* pNewResource, void* pUpdateArg) = 0; |
74 |
schoenebeck |
517 |
|
75 |
|
|
/** |
76 |
|
|
* Might be called by the ResourceManager periodically during an |
77 |
|
|
* update / creation of a resource to inform the consumer about the |
78 |
|
|
* current progress of that process. This method needs to be |
79 |
|
|
* implemented by the consumer. |
80 |
|
|
* |
81 |
|
|
* @param fProgress - current progress as value between 0.0 and 1.0 |
82 |
|
|
*/ |
83 |
|
|
virtual void OnResourceProgress(float fProgress) = 0; |
84 |
schoenebeck |
53 |
}; |
85 |
|
|
|
86 |
schoenebeck |
947 |
/** @brief Manager for sharing resources. |
87 |
|
|
* |
88 |
schoenebeck |
53 |
* Abstract base class for sharing resources between multiple consumers. |
89 |
|
|
* A consumer can borrow a resource from the ResourceManager, if the |
90 |
|
|
* resource doesn't exist yet it will be created. Other consumers will |
91 |
|
|
* just be given the same pointer to the resource then. When all consumers |
92 |
schoenebeck |
947 |
* gave back their pointer to the resource, the resource will (by default) |
93 |
|
|
* be destroyed. |
94 |
|
|
* |
95 |
schoenebeck |
53 |
* Descendants of this base class have to implement the (protected) |
96 |
|
|
* Create() and Destroy() methods to create and destroy a resource. |
97 |
schoenebeck |
958 |
* |
98 |
schoenebeck |
970 |
* This class is thread safe (by default). Its methods should however not |
99 |
|
|
* be called in a realtime context due to this! Alternatively one can |
100 |
|
|
* call the respective methods with bLock = false, in that case thread |
101 |
|
|
* safety mechanisms will be omitted - use with care ! |
102 |
schoenebeck |
53 |
*/ |
103 |
|
|
template<class T_key, class T_res> |
104 |
|
|
class ResourceManager { |
105 |
schoenebeck |
947 |
public: |
106 |
|
|
/** |
107 |
|
|
* Defines life-time strategy for resources. |
108 |
|
|
*/ |
109 |
|
|
enum mode_t { |
110 |
|
|
ON_DEMAND = 0, ///< Create resource when needed, free it once not needed anymore (default behavior). |
111 |
|
|
ON_DEMAND_HOLD = 1, ///< Create resource when needed and keep it even if not needed anymore. |
112 |
|
|
PERSISTENT = 2 ///< Immediately create resource and keep it. |
113 |
|
|
}; |
114 |
|
|
|
115 |
schoenebeck |
53 |
private: |
116 |
|
|
typedef std::set<ResourceConsumer<T_res>*> ConsumerSet; |
117 |
|
|
struct resource_entry_t { |
118 |
|
|
T_key key; |
119 |
|
|
T_res* resource; ///< pointer to the resource |
120 |
schoenebeck |
947 |
mode_t mode; ///< When should the resource be created? When should it be destroyed? |
121 |
schoenebeck |
53 |
ConsumerSet consumers; ///< list of all consumers who currently use the resource |
122 |
schoenebeck |
947 |
void* lifearg; ///< optional pointer the descendant might use to store informations about a created resource |
123 |
|
|
void* entryarg; ///< optional pointer the descendant might use to store informations about an entry |
124 |
schoenebeck |
53 |
}; |
125 |
|
|
typedef std::map<T_key, resource_entry_t> ResourceMap; |
126 |
|
|
ResourceMap ResourceEntries; |
127 |
schoenebeck |
970 |
Mutex ResourceEntriesMutex; // Mutex for protecting the ResourceEntries map |
128 |
schoenebeck |
53 |
|
129 |
|
|
public: |
130 |
|
|
/** |
131 |
schoenebeck |
947 |
* Returns (the keys of) all current entries of this |
132 |
|
|
* ResourceManager instance. |
133 |
schoenebeck |
970 |
* |
134 |
|
|
* @param bLock - use thread safety mechanisms |
135 |
schoenebeck |
947 |
*/ |
136 |
schoenebeck |
970 |
std::vector<T_key> Entries(bool bLock = true) { |
137 |
schoenebeck |
947 |
std::vector<T_key> result; |
138 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Lock(); |
139 |
schoenebeck |
947 |
for (typename ResourceMap::iterator iter = ResourceEntries.begin(); |
140 |
|
|
iter != ResourceEntries.end(); iter++) |
141 |
|
|
{ |
142 |
|
|
result.push_back(iter->first); |
143 |
|
|
} |
144 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
145 |
schoenebeck |
947 |
return result; |
146 |
|
|
} |
147 |
|
|
|
148 |
|
|
/** |
149 |
schoenebeck |
53 |
* Borrow a resource identified by \a Key. The ResourceManager will |
150 |
|
|
* mark the resource as in usage by the consumer given with |
151 |
|
|
* \a pConsumer. If the Resource doesn't exist yet it will be |
152 |
|
|
* created. |
153 |
|
|
* |
154 |
|
|
* @param Key - resource identifier |
155 |
|
|
* @param pConsumer - identifier of the consumer who borrows it |
156 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
157 |
schoenebeck |
53 |
* @returns pointer to resource |
158 |
|
|
*/ |
159 |
schoenebeck |
970 |
T_res* Borrow(T_key Key, ResourceConsumer<T_res>* pConsumer, bool bLock = true) { |
160 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
161 |
schoenebeck |
947 |
// search for an entry for this resource |
162 |
schoenebeck |
53 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
163 |
schoenebeck |
947 |
if (iterEntry == ResourceEntries.end()) { // entry doesn't exist yet |
164 |
schoenebeck |
517 |
// already create an entry for the resource |
165 |
schoenebeck |
53 |
resource_entry_t entry; |
166 |
|
|
entry.key = Key; |
167 |
schoenebeck |
517 |
entry.resource = NULL; |
168 |
schoenebeck |
947 |
entry.mode = ON_DEMAND; // default mode |
169 |
|
|
entry.lifearg = NULL; |
170 |
|
|
entry.entryarg = NULL; |
171 |
schoenebeck |
517 |
entry.consumers.insert(pConsumer); |
172 |
|
|
ResourceEntries[Key] = entry; |
173 |
schoenebeck |
925 |
try { |
174 |
|
|
// actually create the resource |
175 |
schoenebeck |
947 |
entry.resource = Create(Key, pConsumer, entry.lifearg); |
176 |
schoenebeck |
925 |
} catch (...) { |
177 |
|
|
// creating the resource failed, so remove the entry |
178 |
|
|
ResourceEntries.erase(Key); |
179 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
180 |
schoenebeck |
925 |
// rethrow the same exception |
181 |
|
|
throw; |
182 |
|
|
} |
183 |
schoenebeck |
517 |
// now update the entry with the created resource |
184 |
|
|
ResourceEntries[Key] = entry; |
185 |
schoenebeck |
947 |
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
186 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
187 |
schoenebeck |
53 |
return entry.resource; |
188 |
schoenebeck |
947 |
} else { // entry already exists |
189 |
|
|
resource_entry_t& entry = iterEntry->second; |
190 |
|
|
if (!entry.resource) { // create resource if not created already |
191 |
|
|
try { |
192 |
|
|
entry.resource = Create(Key, pConsumer, entry.lifearg); |
193 |
|
|
} catch (...) { |
194 |
|
|
entry.resource = NULL; |
195 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
196 |
schoenebeck |
947 |
throw; // rethrow the same exception |
197 |
|
|
} |
198 |
|
|
} |
199 |
|
|
entry.consumers.insert(pConsumer); |
200 |
|
|
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
201 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
202 |
schoenebeck |
947 |
return entry.resource; |
203 |
schoenebeck |
53 |
} |
204 |
|
|
} |
205 |
|
|
|
206 |
|
|
/** |
207 |
|
|
* Give back a resource. This tells the ResourceManager that the |
208 |
|
|
* consumer given by \a pConsumer doesn't need the resource anymore. |
209 |
schoenebeck |
947 |
* If the resource is not needed by any consumer anymore and the |
210 |
|
|
* resource has a life-time strategy of ON_DEMAND (which is the |
211 |
|
|
* default setting) then the resource will be destroyed. |
212 |
schoenebeck |
53 |
* |
213 |
|
|
* @param pResource - pointer to resource |
214 |
|
|
* @param pConsumer - identifier of the consumer who borrowed the |
215 |
|
|
* resource |
216 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
217 |
schoenebeck |
53 |
*/ |
218 |
schoenebeck |
970 |
void HandBack(T_res* pResource, ResourceConsumer<T_res>* pConsumer, bool bLock = true) { |
219 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
220 |
schoenebeck |
947 |
// search for the entry associated with the given resource |
221 |
schoenebeck |
53 |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
222 |
|
|
typename ResourceMap::iterator end = ResourceEntries.end(); |
223 |
|
|
for (; iter != end; iter++) { |
224 |
schoenebeck |
947 |
if (iter->second.resource == pResource) { // found entry for resource |
225 |
schoenebeck |
53 |
resource_entry_t& entry = iter->second; |
226 |
|
|
entry.consumers.erase(pConsumer); |
227 |
schoenebeck |
947 |
// remove entry if necessary |
228 |
|
|
if (entry.mode == ON_DEMAND && !entry.entryarg && entry.consumers.empty()) { |
229 |
persson |
835 |
T_res* resource = entry.resource; |
230 |
schoenebeck |
947 |
void* arg = entry.lifearg; |
231 |
schoenebeck |
53 |
ResourceEntries.erase(iter); |
232 |
schoenebeck |
947 |
// destroy resource if necessary |
233 |
|
|
if (resource) Destroy(resource, arg); |
234 |
schoenebeck |
53 |
} |
235 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
236 |
schoenebeck |
53 |
return; |
237 |
|
|
} |
238 |
|
|
} |
239 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
240 |
schoenebeck |
53 |
} |
241 |
|
|
|
242 |
|
|
/** |
243 |
schoenebeck |
947 |
* Request update of a resource. All consumers will be informed |
244 |
|
|
* about the pending update of the resource so they can safely react |
245 |
|
|
* by stopping its usage first, then the resource will be recreated |
246 |
|
|
* and finally the consumers will be informed once the update was |
247 |
|
|
* completed, so they can continue to use the resource. |
248 |
schoenebeck |
53 |
* |
249 |
|
|
* @param pResource - resource to be updated |
250 |
|
|
* @param pConsumer - consumer who requested the update |
251 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
252 |
schoenebeck |
53 |
*/ |
253 |
schoenebeck |
970 |
void Update(T_res* pResource, ResourceConsumer<T_res>* pConsumer, bool bLock = true) { |
254 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
255 |
schoenebeck |
53 |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
256 |
|
|
typename ResourceMap::iterator end = ResourceEntries.end(); |
257 |
|
|
for (; iter != end; iter++) { |
258 |
schoenebeck |
351 |
if (iter->second.resource == pResource) { |
259 |
schoenebeck |
53 |
resource_entry_t& entry = iter->second; |
260 |
|
|
// inform all consumers about pending update |
261 |
|
|
std::map<ResourceConsumer<T_res>*,void*> updateargs; |
262 |
|
|
typename ConsumerSet::iterator iterCons = entry.consumers.begin(); |
263 |
|
|
typename ConsumerSet::iterator endCons = entry.consumers.end(); |
264 |
|
|
for (; iterCons != endCons; iterCons++) { |
265 |
|
|
if (*iterCons == pConsumer) continue; |
266 |
|
|
void* updatearg = NULL; |
267 |
|
|
(*iterCons)->ResourceToBeUpdated(entry.resource, updatearg); |
268 |
|
|
if (updatearg) updateargs[*iterCons] = updatearg; |
269 |
|
|
} |
270 |
|
|
// update resource |
271 |
|
|
T_res* pOldResource = entry.resource; |
272 |
schoenebeck |
947 |
Destroy(entry.resource, entry.lifearg); |
273 |
|
|
entry.resource = Create(entry.key, pConsumer, entry.lifearg); |
274 |
schoenebeck |
53 |
// inform all consumers about update completed |
275 |
|
|
iterCons = entry.consumers.begin(); |
276 |
|
|
endCons = entry.consumers.end(); |
277 |
|
|
for (; iterCons != endCons; iterCons++) { |
278 |
|
|
if (*iterCons == pConsumer) continue; |
279 |
|
|
typename std::map<ResourceConsumer<T_res>*,void*>::iterator iterArg = updateargs.find(*iterCons); |
280 |
|
|
void* updatearg = (iterArg != updateargs.end()) ? iterArg->second : NULL; |
281 |
|
|
(*iterCons)->ResourceUpdated(pOldResource, entry.resource, updatearg); |
282 |
|
|
} |
283 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
284 |
schoenebeck |
53 |
return; |
285 |
|
|
} |
286 |
|
|
} |
287 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
288 |
schoenebeck |
53 |
} |
289 |
|
|
|
290 |
schoenebeck |
947 |
/** |
291 |
|
|
* Returns the life-time strategy of the given resource. |
292 |
|
|
* |
293 |
schoenebeck |
970 |
* @param Key - ID of the resource |
294 |
|
|
* @param bLock - use thread safety mechanisms |
295 |
schoenebeck |
947 |
*/ |
296 |
schoenebeck |
970 |
mode_t AvailabilityMode(T_key Key, bool bLock = true) { |
297 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
298 |
schoenebeck |
947 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
299 |
schoenebeck |
970 |
if (iterEntry == ResourceEntries.end()) { |
300 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
301 |
schoenebeck |
947 |
return ON_DEMAND; // resource entry doesn't exist, so we return the default mode |
302 |
schoenebeck |
970 |
} |
303 |
schoenebeck |
947 |
resource_entry_t& entry = iterEntry->second; |
304 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
305 |
schoenebeck |
947 |
return entry.mode; |
306 |
|
|
} |
307 |
|
|
|
308 |
|
|
/** |
309 |
|
|
* Change life-time strategy of the given resource. If a life-time |
310 |
|
|
* strategy of PERSISTENT was given and the resource was not created |
311 |
|
|
* yet, it will immediately be created and this method will block |
312 |
|
|
* until the resource creation was completed. |
313 |
|
|
* |
314 |
|
|
* @param Key - ID of the resource |
315 |
|
|
* @param Mode - life-time strategy of resource to be set |
316 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
317 |
schoenebeck |
947 |
* @throws Exception in case an invalid Mode was given |
318 |
|
|
*/ |
319 |
schoenebeck |
1040 |
void SetAvailabilityMode(T_key Key, mode_t Mode, bool bLock = true) { |
320 |
schoenebeck |
947 |
if (Mode != ON_DEMAND && Mode != ON_DEMAND_HOLD && Mode != PERSISTENT) |
321 |
|
|
throw Exception("ResourceManager::SetAvailabilityMode(): invalid mode"); |
322 |
|
|
|
323 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Lock(); |
324 |
schoenebeck |
947 |
// search for an entry for this resource |
325 |
|
|
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
326 |
|
|
resource_entry_t* pEntry = NULL; |
327 |
|
|
if (iterEntry == ResourceEntries.end()) { // resource entry doesn't exist |
328 |
schoenebeck |
970 |
if (Mode == ON_DEMAND) { |
329 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
330 |
|
|
return; // we don't create an entry for the default value |
331 |
|
|
} |
332 |
schoenebeck |
947 |
// create an entry for the resource |
333 |
|
|
pEntry = &ResourceEntries[Key]; |
334 |
|
|
pEntry->key = Key; |
335 |
|
|
pEntry->resource = NULL; |
336 |
|
|
pEntry->mode = Mode; |
337 |
|
|
pEntry->lifearg = NULL; |
338 |
|
|
pEntry->entryarg = NULL; |
339 |
|
|
} else { // resource entry exists |
340 |
|
|
pEntry = &iterEntry->second; |
341 |
|
|
// remove entry if necessary |
342 |
|
|
if (Mode == ON_DEMAND && !pEntry->entryarg && pEntry->consumers.empty()) { |
343 |
|
|
T_res* resource = pEntry->resource; |
344 |
|
|
void* arg = pEntry->lifearg; |
345 |
|
|
ResourceEntries.erase(iterEntry); |
346 |
|
|
// destroy resource if necessary |
347 |
|
|
if (resource) Destroy(resource, arg); |
348 |
schoenebeck |
970 |
// done |
349 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
350 |
|
|
return; |
351 |
schoenebeck |
947 |
} |
352 |
|
|
pEntry->mode = Mode; // apply new mode |
353 |
|
|
} |
354 |
|
|
|
355 |
|
|
// already create the resource if necessary |
356 |
|
|
if (pEntry->mode == PERSISTENT && !pEntry->resource) { |
357 |
|
|
try { |
358 |
|
|
// actually create the resource |
359 |
|
|
pEntry->resource = Create(Key, NULL /*no consumer yet*/, pEntry->lifearg); |
360 |
|
|
} catch (...) { |
361 |
|
|
// creating the resource failed, so skip it for now |
362 |
|
|
pEntry->resource = NULL; |
363 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
364 |
schoenebeck |
947 |
// rethrow the same exception |
365 |
|
|
throw; |
366 |
|
|
} |
367 |
|
|
} |
368 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
369 |
schoenebeck |
947 |
} |
370 |
|
|
|
371 |
|
|
/** |
372 |
schoenebeck |
958 |
* Returns true in case the resource associated with \a Key is |
373 |
|
|
* currently created / "alive". |
374 |
|
|
* |
375 |
|
|
* @param Key - ID of resource |
376 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
377 |
schoenebeck |
958 |
*/ |
378 |
schoenebeck |
970 |
bool IsCreated(T_key Key, bool bLock = true) { |
379 |
|
|
return Resource(Key, bLock) != NULL; |
380 |
schoenebeck |
958 |
} |
381 |
|
|
|
382 |
|
|
/** |
383 |
schoenebeck |
947 |
* Returns custom data sticked to the given resource previously by |
384 |
|
|
* a SetCustomData() call. |
385 |
|
|
* |
386 |
|
|
* @param Key - ID of resource |
387 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
388 |
schoenebeck |
947 |
*/ |
389 |
schoenebeck |
970 |
void* CustomData(T_key Key, bool bLock = true) { |
390 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
391 |
schoenebeck |
947 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
392 |
schoenebeck |
970 |
if (iterEntry == ResourceEntries.end()) { |
393 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
394 |
|
|
return NULL; // resource entry doesn't exist, so we return the default mode |
395 |
|
|
} |
396 |
|
|
resource_entry_t entry = iterEntry->second; |
397 |
|
|
void* res = entry.entryarg; |
398 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
399 |
|
|
return res; |
400 |
schoenebeck |
947 |
} |
401 |
|
|
|
402 |
|
|
/** |
403 |
|
|
* This method can be used to stick custom data to an resource |
404 |
|
|
* entry. In case the custom data is not needed anymore, you should |
405 |
|
|
* call this method again and set \a pData to NULL, so the |
406 |
|
|
* ResourceManager might safe space by removing the respective |
407 |
|
|
* entry if not needed anymore. |
408 |
|
|
* |
409 |
|
|
* @param Key - ID of resource |
410 |
|
|
* @param pData - pointer to custom data, or NULL if not needed anymore |
411 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
412 |
schoenebeck |
947 |
*/ |
413 |
schoenebeck |
970 |
void SetCustomData(T_key Key, void* pData, bool bLock = true) { |
414 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
415 |
schoenebeck |
947 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
416 |
|
|
if (pData) { |
417 |
|
|
if (iterEntry == ResourceEntries.end()) { // entry doesnt exist, so create one |
418 |
|
|
resource_entry_t* pEntry = &ResourceEntries[Key]; |
419 |
|
|
pEntry->key = Key; |
420 |
|
|
pEntry->resource = NULL; |
421 |
|
|
pEntry->mode = ON_DEMAND; |
422 |
|
|
pEntry->lifearg = NULL; |
423 |
|
|
pEntry->entryarg = pData; // set custom data |
424 |
|
|
} else { // entry exists, so just update its custom data |
425 |
|
|
iterEntry->second.entryarg = pData; |
426 |
|
|
} |
427 |
|
|
} else { // !pData |
428 |
schoenebeck |
970 |
if (iterEntry == ResourceEntries.end()) { |
429 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
430 |
|
|
return; // entry doesnt exist, so nothing to do |
431 |
|
|
} |
432 |
schoenebeck |
947 |
// entry exists, remove it if necessary |
433 |
|
|
resource_entry_t* pEntry = &iterEntry->second; |
434 |
|
|
if (pEntry->mode == ON_DEMAND && pEntry->consumers.empty()) { |
435 |
|
|
ResourceEntries.erase(iterEntry); |
436 |
|
|
} else iterEntry->second.entryarg = NULL; |
437 |
|
|
} |
438 |
schoenebeck |
970 |
if (bLock) ResourceEntriesMutex.Unlock(); |
439 |
schoenebeck |
947 |
} |
440 |
|
|
|
441 |
schoenebeck |
970 |
/** |
442 |
|
|
* Prevent this ResourceManager instance to be used by another |
443 |
|
|
* thread at the same time. All methods of this class are thread |
444 |
|
|
* safe by default. However sometimes you might need atomicity among |
445 |
|
|
* a sequence of method calls. In this case you would first call |
446 |
|
|
* this Lock() method, call the respective operational methods of |
447 |
|
|
* this class (<b>Important:</b> each one by setting bLock = false, |
448 |
|
|
* this avoids double locking). And once the required sequence of |
449 |
|
|
* atomic method calls is done, you have to call Unlock() to |
450 |
|
|
* reenable accessibility of this ResourceManager instance for other |
451 |
|
|
* threads. |
452 |
|
|
* |
453 |
|
|
* @see Mutex::Lock() |
454 |
|
|
*/ |
455 |
|
|
void Lock() { |
456 |
|
|
ResourceEntriesMutex.Lock(); |
457 |
|
|
} |
458 |
|
|
|
459 |
|
|
/** |
460 |
|
|
* Has to be called after a Lock() call to reenable this |
461 |
|
|
* ResourceManager instance to be accessible by another thread |
462 |
|
|
* again. |
463 |
|
|
* |
464 |
|
|
* @see Mutex::Unlock() |
465 |
|
|
*/ |
466 |
|
|
void Unlock() { |
467 |
|
|
ResourceEntriesMutex.Unlock(); |
468 |
|
|
} |
469 |
|
|
|
470 |
schoenebeck |
947 |
virtual ~ResourceManager() {} // due to C++'s nature we cannot destroy created resources here |
471 |
|
|
|
472 |
schoenebeck |
53 |
protected: |
473 |
|
|
/** |
474 |
|
|
* Has to be implemented by the descendant to create (allocate) a |
475 |
|
|
* resource identified by \a Key. |
476 |
|
|
* |
477 |
|
|
* @param Key - identifier of the resource |
478 |
|
|
* @param pConsumer - identifier of the consumer who borrows the |
479 |
|
|
* resource |
480 |
|
|
* @param pArg - pointer the descendant can use to store |
481 |
|
|
* informations he might need for destruction of |
482 |
|
|
* the resource |
483 |
|
|
* @returns pointer to new resource |
484 |
|
|
*/ |
485 |
|
|
virtual T_res* Create(T_key Key, ResourceConsumer<T_res>* pConsumer, void*& pArg) = 0; |
486 |
|
|
|
487 |
|
|
/** |
488 |
|
|
* Has to be implemented by the descendant to destroy (free) a |
489 |
|
|
* resource pointed by \a pResource. |
490 |
|
|
* |
491 |
|
|
* @param pResource - pointer to the resource |
492 |
|
|
* @param pArg - pointer the descendant might have used when |
493 |
|
|
* Create() was called to store informations |
494 |
|
|
* about the resource |
495 |
|
|
*/ |
496 |
|
|
virtual void Destroy(T_res* pResource, void* pArg) = 0; |
497 |
|
|
|
498 |
|
|
/** |
499 |
|
|
* Has to be implemented by the descendant to react when a consumer |
500 |
|
|
* borrows a resource (no matter if freshly created or an already |
501 |
|
|
* created one). Of course reacting is optional, but the descendant |
502 |
|
|
* at least has to provide a method with empty body. |
503 |
|
|
* |
504 |
|
|
* @param pResource - pointer to the resource |
505 |
|
|
* @param pConsumer - identifier of the consumer who borrows the |
506 |
|
|
* resource |
507 |
|
|
* @param pArg - pointer the descendant might have used when |
508 |
|
|
* Create() was called to store informations |
509 |
|
|
* about the resource, this information can be |
510 |
|
|
* updated by the descendant here |
511 |
|
|
*/ |
512 |
|
|
virtual void OnBorrow(T_res* pResource, ResourceConsumer<T_res>* pConsumer, void*& pArg) = 0; |
513 |
schoenebeck |
517 |
|
514 |
|
|
/** |
515 |
|
|
* Dispatcher method which should be periodically called by the |
516 |
|
|
* descendant during update or creation of the resource associated |
517 |
|
|
* with \a Key. This method will inform all associated consumers |
518 |
|
|
* of the given resource about the current progress. |
519 |
|
|
* |
520 |
|
|
* @param Key - unique identifier of the resource which is |
521 |
|
|
* currently creating or updating |
522 |
|
|
* @param fProgress - current progress of that creation / update |
523 |
|
|
* process as value between 0.0 and 1.0 |
524 |
|
|
*/ |
525 |
|
|
void DispatchResourceProgressEvent(T_key Key, float fProgress) { |
526 |
schoenebeck |
970 |
// no Mutex here, since Create() is already protected |
527 |
schoenebeck |
517 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
528 |
|
|
if (iterEntry != ResourceEntries.end()) { |
529 |
|
|
resource_entry_t& entry = iterEntry->second; |
530 |
|
|
// inform all consumers of that resource about current progress |
531 |
|
|
typename ConsumerSet::iterator iterCons = entry.consumers.begin(); |
532 |
|
|
typename ConsumerSet::iterator endCons = entry.consumers.end(); |
533 |
|
|
for (; iterCons != endCons; iterCons++) { |
534 |
|
|
(*iterCons)->OnResourceProgress(fProgress); |
535 |
|
|
} |
536 |
|
|
} |
537 |
|
|
} |
538 |
schoenebeck |
958 |
|
539 |
|
|
/** |
540 |
|
|
* Returns pointer to the resource associated with \a Key if |
541 |
|
|
* currently created / "alive", NULL otherwise. This method |
542 |
|
|
* should be taken with great care in multi-threaded scenarios, |
543 |
|
|
* since the returned resource might be destroyed by a concurrent |
544 |
|
|
* HandBack() call. |
545 |
|
|
* |
546 |
|
|
* @param Key - ID of resource |
547 |
schoenebeck |
970 |
* @param bLock - use thread safety mechanisms |
548 |
schoenebeck |
958 |
*/ |
549 |
schoenebeck |
970 |
T_res* Resource(T_key Key, bool bLock = true) { |
550 |
|
|
if (bLock) ResourceEntriesMutex.Lock(); |
551 |
schoenebeck |
958 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
552 |
schoenebeck |
970 |
T_res* result = (iterEntry == ResourceEntries.end()) ? NULL : iterEntry->second.resource; |
553 |
|
|
if (bLock) ResourceEntriesMutex.Unlock(); |
554 |
|
|
return result; |
555 |
schoenebeck |
958 |
} |
556 |
schoenebeck |
53 |
}; |
557 |
|
|
|
558 |
schoenebeck |
947 |
} // namespace LinuxSampler |
559 |
|
|
|
560 |
schoenebeck |
53 |
#endif // __RESOURCE_MANAGER__ |