29 |
#include <vector> |
#include <vector> |
30 |
|
|
31 |
#include "Exception.h" |
#include "Exception.h" |
32 |
|
#include "Mutex.h" |
33 |
|
|
34 |
namespace LinuxSampler { |
namespace LinuxSampler { |
35 |
|
|
95 |
* Descendants of this base class have to implement the (protected) |
* Descendants of this base class have to implement the (protected) |
96 |
* Create() and Destroy() methods to create and destroy a resource. |
* Create() and Destroy() methods to create and destroy a resource. |
97 |
* |
* |
98 |
* Note: this template class is not thread safe, so if thread safety is |
* This class is thread safe (by default). Its methods should however not |
99 |
* needed the descendant has to add synchronization methods on its own. |
* 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 |
*/ |
*/ |
103 |
template<class T_key, class T_res> |
template<class T_key, class T_res> |
104 |
class ResourceManager { |
class ResourceManager { |
124 |
}; |
}; |
125 |
typedef std::map<T_key, resource_entry_t> ResourceMap; |
typedef std::map<T_key, resource_entry_t> ResourceMap; |
126 |
ResourceMap ResourceEntries; |
ResourceMap ResourceEntries; |
127 |
|
Mutex ResourceEntriesMutex; // Mutex for protecting the ResourceEntries map |
128 |
|
|
129 |
public: |
public: |
130 |
/** |
/** |
131 |
* Returns (the keys of) all current entries of this |
* Returns (the keys of) all current entries of this |
132 |
* ResourceManager instance. |
* ResourceManager instance. |
133 |
|
* |
134 |
|
* @param bLock - use thread safety mechanisms |
135 |
*/ |
*/ |
136 |
std::vector<T_key> Entries() { |
std::vector<T_key> Entries(bool bLock = true) { |
137 |
std::vector<T_key> result; |
std::vector<T_key> result; |
138 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
139 |
for (typename ResourceMap::iterator iter = ResourceEntries.begin(); |
for (typename ResourceMap::iterator iter = ResourceEntries.begin(); |
140 |
iter != ResourceEntries.end(); iter++) |
iter != ResourceEntries.end(); iter++) |
141 |
{ |
{ |
142 |
result.push_back(iter->first); |
result.push_back(iter->first); |
143 |
} |
} |
144 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
145 |
return result; |
return result; |
146 |
} |
} |
147 |
|
|
153 |
* |
* |
154 |
* @param Key - resource identifier |
* @param Key - resource identifier |
155 |
* @param pConsumer - identifier of the consumer who borrows it |
* @param pConsumer - identifier of the consumer who borrows it |
156 |
|
* @param bLock - use thread safety mechanisms |
157 |
* @returns pointer to resource |
* @returns pointer to resource |
158 |
*/ |
*/ |
159 |
T_res* Borrow(T_key Key, ResourceConsumer<T_res>* pConsumer) { |
T_res* Borrow(T_key Key, ResourceConsumer<T_res>* pConsumer, bool bLock = true) { |
160 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
161 |
// search for an entry for this resource |
// search for an entry for this resource |
162 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
163 |
if (iterEntry == ResourceEntries.end()) { // entry doesn't exist yet |
if (iterEntry == ResourceEntries.end()) { // entry doesn't exist yet |
176 |
} catch (...) { |
} catch (...) { |
177 |
// creating the resource failed, so remove the entry |
// creating the resource failed, so remove the entry |
178 |
ResourceEntries.erase(Key); |
ResourceEntries.erase(Key); |
179 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
180 |
// rethrow the same exception |
// rethrow the same exception |
181 |
throw; |
throw; |
182 |
} |
} |
183 |
// now update the entry with the created resource |
// now update the entry with the created resource |
184 |
ResourceEntries[Key] = entry; |
ResourceEntries[Key] = entry; |
185 |
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
186 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
187 |
return entry.resource; |
return entry.resource; |
188 |
} else { // entry already exists |
} else { // entry already exists |
189 |
resource_entry_t& entry = iterEntry->second; |
resource_entry_t& entry = iterEntry->second; |
192 |
entry.resource = Create(Key, pConsumer, entry.lifearg); |
entry.resource = Create(Key, pConsumer, entry.lifearg); |
193 |
} catch (...) { |
} catch (...) { |
194 |
entry.resource = NULL; |
entry.resource = NULL; |
195 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
196 |
throw; // rethrow the same exception |
throw; // rethrow the same exception |
197 |
} |
} |
198 |
} |
} |
199 |
entry.consumers.insert(pConsumer); |
entry.consumers.insert(pConsumer); |
200 |
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
OnBorrow(entry.resource, pConsumer, entry.lifearg); |
201 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
202 |
return entry.resource; |
return entry.resource; |
203 |
} |
} |
204 |
} |
} |
213 |
* @param pResource - pointer to resource |
* @param pResource - pointer to resource |
214 |
* @param pConsumer - identifier of the consumer who borrowed the |
* @param pConsumer - identifier of the consumer who borrowed the |
215 |
* resource |
* resource |
216 |
|
* @param bLock - use thread safety mechanisms |
217 |
*/ |
*/ |
218 |
void HandBack(T_res* pResource, ResourceConsumer<T_res>* pConsumer) { |
void HandBack(T_res* pResource, ResourceConsumer<T_res>* pConsumer, bool bLock = true) { |
219 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
220 |
// search for the entry associated with the given resource |
// search for the entry associated with the given resource |
221 |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
222 |
typename ResourceMap::iterator end = ResourceEntries.end(); |
typename ResourceMap::iterator end = ResourceEntries.end(); |
232 |
// destroy resource if necessary |
// destroy resource if necessary |
233 |
if (resource) Destroy(resource, arg); |
if (resource) Destroy(resource, arg); |
234 |
} |
} |
235 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
236 |
return; |
return; |
237 |
} |
} |
238 |
} |
} |
239 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
240 |
} |
} |
241 |
|
|
242 |
/** |
/** |
248 |
* |
* |
249 |
* @param pResource - resource to be updated |
* @param pResource - resource to be updated |
250 |
* @param pConsumer - consumer who requested the update |
* @param pConsumer - consumer who requested the update |
251 |
|
* @param bLock - use thread safety mechanisms |
252 |
*/ |
*/ |
253 |
void Update(T_res* pResource, ResourceConsumer<T_res>* pConsumer) { |
void Update(T_res* pResource, ResourceConsumer<T_res>* pConsumer, bool bLock = true) { |
254 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
255 |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
typename ResourceMap::iterator iter = ResourceEntries.begin(); |
256 |
typename ResourceMap::iterator end = ResourceEntries.end(); |
typename ResourceMap::iterator end = ResourceEntries.end(); |
257 |
for (; iter != end; iter++) { |
for (; iter != end; iter++) { |
280 |
void* updatearg = (iterArg != updateargs.end()) ? iterArg->second : NULL; |
void* updatearg = (iterArg != updateargs.end()) ? iterArg->second : NULL; |
281 |
(*iterCons)->ResourceUpdated(pOldResource, entry.resource, updatearg); |
(*iterCons)->ResourceUpdated(pOldResource, entry.resource, updatearg); |
282 |
} |
} |
283 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
284 |
return; |
return; |
285 |
} |
} |
286 |
} |
} |
287 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
288 |
} |
} |
289 |
|
|
290 |
/** |
/** |
291 |
* Returns the life-time strategy of the given resource. |
* Returns the life-time strategy of the given resource. |
292 |
* |
* |
293 |
* @param Key - ID of the resource |
* @param Key - ID of the resource |
294 |
|
* @param bLock - use thread safety mechanisms |
295 |
*/ |
*/ |
296 |
mode_t AvailabilityMode(T_key Key) { |
mode_t AvailabilityMode(T_key Key, bool bLock = true) { |
297 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
298 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
299 |
if (iterEntry == ResourceEntries.end()) |
if (iterEntry == ResourceEntries.end()) { |
300 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
301 |
return ON_DEMAND; // resource entry doesn't exist, so we return the default mode |
return ON_DEMAND; // resource entry doesn't exist, so we return the default mode |
302 |
|
} |
303 |
resource_entry_t& entry = iterEntry->second; |
resource_entry_t& entry = iterEntry->second; |
304 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
305 |
return entry.mode; |
return entry.mode; |
306 |
} |
} |
307 |
|
|
313 |
* |
* |
314 |
* @param Key - ID of the resource |
* @param Key - ID of the resource |
315 |
* @param Mode - life-time strategy of resource to be set |
* @param Mode - life-time strategy of resource to be set |
316 |
|
* @param bLock - use thread safety mechanisms |
317 |
* @throws Exception in case an invalid Mode was given |
* @throws Exception in case an invalid Mode was given |
318 |
*/ |
*/ |
319 |
void SetAvailabilityMode(T_key Key, mode_t Mode) throw (Exception) { |
void SetAvailabilityMode(T_key Key, mode_t Mode, bool bLock = true) throw (Exception) { |
320 |
if (Mode != ON_DEMAND && Mode != ON_DEMAND_HOLD && Mode != PERSISTENT) |
if (Mode != ON_DEMAND && Mode != ON_DEMAND_HOLD && Mode != PERSISTENT) |
321 |
throw Exception("ResourceManager::SetAvailabilityMode(): invalid mode"); |
throw Exception("ResourceManager::SetAvailabilityMode(): invalid mode"); |
322 |
|
|
323 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
324 |
// search for an entry for this resource |
// search for an entry for this resource |
325 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
326 |
resource_entry_t* pEntry = NULL; |
resource_entry_t* pEntry = NULL; |
327 |
if (iterEntry == ResourceEntries.end()) { // resource entry doesn't exist |
if (iterEntry == ResourceEntries.end()) { // resource entry doesn't exist |
328 |
if (Mode == ON_DEMAND) return; // we don't create an entry for the default value |
if (Mode == ON_DEMAND) { |
329 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
330 |
|
return; // we don't create an entry for the default value |
331 |
|
} |
332 |
// create an entry for the resource |
// create an entry for the resource |
333 |
pEntry = &ResourceEntries[Key]; |
pEntry = &ResourceEntries[Key]; |
334 |
pEntry->key = Key; |
pEntry->key = Key; |
345 |
ResourceEntries.erase(iterEntry); |
ResourceEntries.erase(iterEntry); |
346 |
// destroy resource if necessary |
// destroy resource if necessary |
347 |
if (resource) Destroy(resource, arg); |
if (resource) Destroy(resource, arg); |
348 |
return; // done |
// done |
349 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
350 |
|
return; |
351 |
} |
} |
352 |
pEntry->mode = Mode; // apply new mode |
pEntry->mode = Mode; // apply new mode |
353 |
} |
} |
360 |
} catch (...) { |
} catch (...) { |
361 |
// creating the resource failed, so skip it for now |
// creating the resource failed, so skip it for now |
362 |
pEntry->resource = NULL; |
pEntry->resource = NULL; |
363 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
364 |
// rethrow the same exception |
// rethrow the same exception |
365 |
throw; |
throw; |
366 |
} |
} |
367 |
} |
} |
368 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
369 |
} |
} |
370 |
|
|
371 |
/** |
/** |
373 |
* currently created / "alive". |
* currently created / "alive". |
374 |
* |
* |
375 |
* @param Key - ID of resource |
* @param Key - ID of resource |
376 |
|
* @param bLock - use thread safety mechanisms |
377 |
*/ |
*/ |
378 |
bool IsCreated(T_key Key) { |
bool IsCreated(T_key Key, bool bLock = true) { |
379 |
return Resource(Key) != NULL; |
return Resource(Key, bLock) != NULL; |
380 |
} |
} |
381 |
|
|
382 |
/** |
/** |
384 |
* a SetCustomData() call. |
* a SetCustomData() call. |
385 |
* |
* |
386 |
* @param Key - ID of resource |
* @param Key - ID of resource |
387 |
|
* @param bLock - use thread safety mechanisms |
388 |
*/ |
*/ |
389 |
void* CustomData(T_key Key) { |
void* CustomData(T_key Key, bool bLock = true) { |
390 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
391 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
392 |
if (iterEntry == ResourceEntries.end()) return NULL; // resource entry doesn't exist, so we return the default mode |
if (iterEntry == ResourceEntries.end()) { |
393 |
resource_entry_t& entry = iterEntry->second; |
if (bLock) ResourceEntriesMutex.Unlock(); |
394 |
return entry.entryarg; |
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 |
} |
} |
401 |
|
|
402 |
/** |
/** |
408 |
* |
* |
409 |
* @param Key - ID of resource |
* @param Key - ID of resource |
410 |
* @param pData - pointer to custom data, or NULL if not needed anymore |
* @param pData - pointer to custom data, or NULL if not needed anymore |
411 |
|
* @param bLock - use thread safety mechanisms |
412 |
*/ |
*/ |
413 |
void SetCustomData(T_key Key, void* pData) { |
void SetCustomData(T_key Key, void* pData, bool bLock = true) { |
414 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
415 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
416 |
if (pData) { |
if (pData) { |
417 |
if (iterEntry == ResourceEntries.end()) { // entry doesnt exist, so create one |
if (iterEntry == ResourceEntries.end()) { // entry doesnt exist, so create one |
425 |
iterEntry->second.entryarg = pData; |
iterEntry->second.entryarg = pData; |
426 |
} |
} |
427 |
} else { // !pData |
} else { // !pData |
428 |
if (iterEntry == ResourceEntries.end()) return; // entry doesnt exist, so nothing to do |
if (iterEntry == ResourceEntries.end()) { |
429 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
430 |
|
return; // entry doesnt exist, so nothing to do |
431 |
|
} |
432 |
// entry exists, remove it if necessary |
// entry exists, remove it if necessary |
433 |
resource_entry_t* pEntry = &iterEntry->second; |
resource_entry_t* pEntry = &iterEntry->second; |
434 |
if (pEntry->mode == ON_DEMAND && pEntry->consumers.empty()) { |
if (pEntry->mode == ON_DEMAND && pEntry->consumers.empty()) { |
435 |
ResourceEntries.erase(iterEntry); |
ResourceEntries.erase(iterEntry); |
436 |
} else iterEntry->second.entryarg = NULL; |
} else iterEntry->second.entryarg = NULL; |
437 |
} |
} |
438 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
439 |
|
} |
440 |
|
|
441 |
|
/** |
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 |
virtual ~ResourceManager() {} // due to C++'s nature we cannot destroy created resources here |
virtual ~ResourceManager() {} // due to C++'s nature we cannot destroy created resources here |
523 |
* process as value between 0.0 and 1.0 |
* process as value between 0.0 and 1.0 |
524 |
*/ |
*/ |
525 |
void DispatchResourceProgressEvent(T_key Key, float fProgress) { |
void DispatchResourceProgressEvent(T_key Key, float fProgress) { |
526 |
|
// no Mutex here, since Create() is already protected |
527 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
528 |
if (iterEntry != ResourceEntries.end()) { |
if (iterEntry != ResourceEntries.end()) { |
529 |
resource_entry_t& entry = iterEntry->second; |
resource_entry_t& entry = iterEntry->second; |
544 |
* HandBack() call. |
* HandBack() call. |
545 |
* |
* |
546 |
* @param Key - ID of resource |
* @param Key - ID of resource |
547 |
|
* @param bLock - use thread safety mechanisms |
548 |
*/ |
*/ |
549 |
T_res* Resource(T_key Key) { |
T_res* Resource(T_key Key, bool bLock = true) { |
550 |
|
if (bLock) ResourceEntriesMutex.Lock(); |
551 |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key); |
552 |
return (iterEntry == ResourceEntries.end()) ? NULL : iterEntry->second.resource; |
T_res* result = (iterEntry == ResourceEntries.end()) ? NULL : iterEntry->second.resource; |
553 |
|
if (bLock) ResourceEntriesMutex.Unlock(); |
554 |
|
return result; |
555 |
} |
} |
556 |
}; |
}; |
557 |
|
|