/[svn]/linuxsampler/trunk/src/common/ResourceManager.h
ViewVC logotype

Annotation of /linuxsampler/trunk/src/common/ResourceManager.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 958 - (hide annotations) (download) (as text)
Wed Nov 29 19:48:38 2006 UTC (17 years, 5 months ago) by schoenebeck
File MIME type: text/x-c++hdr
File size: 22689 byte(s)
* store volume for each MIDI instrument mapper entry individually instead
  of managing the volume as shared setting with the respective instrument
  entry of its InstrumentManager

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 925 * Copyright (C) 2005, 2006 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    
33     namespace LinuxSampler {
34    
35     /** @brief Interface class for Resource Consumers.
36     *
37 schoenebeck 53 * Interface class for consumer classes which use a resource managed
38 schoenebeck 947 * 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 schoenebeck 53 */
42     template<class T_res>
43     class ResourceConsumer {
44     public:
45     /**
46     * Will be called by the ResourceManager to inform the
47     * consumer that a resource currently used by him is going
48     * to be updated. The consumer can then react by stopping
49     * usage until resource is updated. The ResourceManager will
50     * not update the resource until this method returns. This
51     * method needs to be implemented by the consumer.
52     *
53     * @param pResource - resource going to be updated
54     * @param pUpdateArg - pointer the consumer might use to store
55     * informations he might need when update
56     * process was completed
57     */
58     virtual void ResourceToBeUpdated(T_res* pResource, void*& pUpdateArg) = 0;
59    
60     /**
61     * Will be called by the ResourceManager to inform the
62     * consumer that resource update was completed. This method
63     * needs to be implemented by the consumer.
64     *
65     * @param pOldResource - (now invalid) pointer to the old
66     * resource
67     * @param pNewResource - (valid) pointer to the updated
68     * resource
69     * @param pUpdateArg - pointer the consumer might have used when
70     * ResourceToBeUpdated() was called
71     */
72     virtual void ResourceUpdated(T_res* pOldResource, T_res* pNewResource, void* pUpdateArg) = 0;
73 schoenebeck 517
74     /**
75     * Might be called by the ResourceManager periodically during an
76     * update / creation of a resource to inform the consumer about the
77     * current progress of that process. This method needs to be
78     * implemented by the consumer.
79     *
80     * @param fProgress - current progress as value between 0.0 and 1.0
81     */
82     virtual void OnResourceProgress(float fProgress) = 0;
83 schoenebeck 53 };
84    
85 schoenebeck 947 /** @brief Manager for sharing resources.
86     *
87 schoenebeck 53 * Abstract base class for sharing resources between multiple consumers.
88     * A consumer can borrow a resource from the ResourceManager, if the
89     * 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
91 schoenebeck 947 * gave back their pointer to the resource, the resource will (by default)
92     * be destroyed.
93     *
94 schoenebeck 53 * Descendants of this base class have to implement the (protected)
95     * Create() and Destroy() methods to create and destroy a resource.
96 schoenebeck 958 *
97     * Note: this template class is not thread safe, so if thread safety is
98     * needed the descendant has to add synchronization methods on its own.
99 schoenebeck 53 */
100     template<class T_key, class T_res>
101     class ResourceManager {
102 schoenebeck 947 public:
103     /**
104     * Defines life-time strategy for resources.
105     */
106     enum mode_t {
107     ON_DEMAND = 0, ///< Create resource when needed, free it once not needed anymore (default behavior).
108     ON_DEMAND_HOLD = 1, ///< Create resource when needed and keep it even if not needed anymore.
109     PERSISTENT = 2 ///< Immediately create resource and keep it.
110     };
111    
112 schoenebeck 53 private:
113     typedef std::set<ResourceConsumer<T_res>*> ConsumerSet;
114     struct resource_entry_t {
115     T_key key;
116     T_res* resource; ///< pointer to the resource
117 schoenebeck 947 mode_t mode; ///< When should the resource be created? When should it be destroyed?
118 schoenebeck 53 ConsumerSet consumers; ///< list of all consumers who currently use the resource
119 schoenebeck 947 void* lifearg; ///< optional pointer the descendant might use to store informations about a created resource
120     void* entryarg; ///< optional pointer the descendant might use to store informations about an entry
121 schoenebeck 53 };
122     typedef std::map<T_key, resource_entry_t> ResourceMap;
123     ResourceMap ResourceEntries;
124    
125     public:
126     /**
127 schoenebeck 947 * Returns (the keys of) all current entries of this
128     * ResourceManager instance.
129     */
130     std::vector<T_key> Entries() {
131     std::vector<T_key> result;
132     for (typename ResourceMap::iterator iter = ResourceEntries.begin();
133     iter != ResourceEntries.end(); iter++)
134     {
135     result.push_back(iter->first);
136     }
137     return result;
138     }
139    
140     /**
141 schoenebeck 53 * Borrow a resource identified by \a Key. The ResourceManager will
142     * mark the resource as in usage by the consumer given with
143     * \a pConsumer. If the Resource doesn't exist yet it will be
144     * created.
145     *
146     * @param Key - resource identifier
147     * @param pConsumer - identifier of the consumer who borrows it
148     * @returns pointer to resource
149     */
150     T_res* Borrow(T_key Key, ResourceConsumer<T_res>* pConsumer) {
151 schoenebeck 947 // search for an entry for this resource
152 schoenebeck 53 typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
153 schoenebeck 947 if (iterEntry == ResourceEntries.end()) { // entry doesn't exist yet
154 schoenebeck 517 // already create an entry for the resource
155 schoenebeck 53 resource_entry_t entry;
156     entry.key = Key;
157 schoenebeck 517 entry.resource = NULL;
158 schoenebeck 947 entry.mode = ON_DEMAND; // default mode
159     entry.lifearg = NULL;
160     entry.entryarg = NULL;
161 schoenebeck 517 entry.consumers.insert(pConsumer);
162     ResourceEntries[Key] = entry;
163 schoenebeck 925 try {
164     // actually create the resource
165 schoenebeck 947 entry.resource = Create(Key, pConsumer, entry.lifearg);
166 schoenebeck 925 } catch (...) {
167     // creating the resource failed, so remove the entry
168     ResourceEntries.erase(Key);
169     // rethrow the same exception
170     throw;
171     }
172 schoenebeck 517 // now update the entry with the created resource
173     ResourceEntries[Key] = entry;
174 schoenebeck 947 OnBorrow(entry.resource, pConsumer, entry.lifearg);
175 schoenebeck 53 return entry.resource;
176 schoenebeck 947 } else { // entry already exists
177     resource_entry_t& entry = iterEntry->second;
178     if (!entry.resource) { // create resource if not created already
179     try {
180     entry.resource = Create(Key, pConsumer, entry.lifearg);
181     } catch (...) {
182     entry.resource = NULL;
183     throw; // rethrow the same exception
184     }
185     }
186     entry.consumers.insert(pConsumer);
187     OnBorrow(entry.resource, pConsumer, entry.lifearg);
188     return entry.resource;
189 schoenebeck 53 }
190     }
191    
192     /**
193     * Give back a resource. This tells the ResourceManager that the
194     * consumer given by \a pConsumer doesn't need the resource anymore.
195 schoenebeck 947 * If the resource is not needed by any consumer anymore and the
196     * resource has a life-time strategy of ON_DEMAND (which is the
197     * default setting) then the resource will be destroyed.
198 schoenebeck 53 *
199     * @param pResource - pointer to resource
200     * @param pConsumer - identifier of the consumer who borrowed the
201     * resource
202     */
203     void HandBack(T_res* pResource, ResourceConsumer<T_res>* pConsumer) {
204 schoenebeck 947 // search for the entry associated with the given resource
205 schoenebeck 53 typename ResourceMap::iterator iter = ResourceEntries.begin();
206     typename ResourceMap::iterator end = ResourceEntries.end();
207     for (; iter != end; iter++) {
208 schoenebeck 947 if (iter->second.resource == pResource) { // found entry for resource
209 schoenebeck 53 resource_entry_t& entry = iter->second;
210     entry.consumers.erase(pConsumer);
211 schoenebeck 947 // remove entry if necessary
212     if (entry.mode == ON_DEMAND && !entry.entryarg && entry.consumers.empty()) {
213 persson 835 T_res* resource = entry.resource;
214 schoenebeck 947 void* arg = entry.lifearg;
215 schoenebeck 53 ResourceEntries.erase(iter);
216 schoenebeck 947 // destroy resource if necessary
217     if (resource) Destroy(resource, arg);
218 schoenebeck 53 }
219     return;
220     }
221     }
222     }
223    
224     /**
225 schoenebeck 947 * Request update of a resource. All consumers will be informed
226     * about the pending update of the resource so they can safely react
227     * by stopping its usage first, then the resource will be recreated
228     * and finally the consumers will be informed once the update was
229     * completed, so they can continue to use the resource.
230 schoenebeck 53 *
231     * @param pResource - resource to be updated
232     * @param pConsumer - consumer who requested the update
233     */
234     void Update(T_res* pResource, ResourceConsumer<T_res>* pConsumer) {
235     typename ResourceMap::iterator iter = ResourceEntries.begin();
236     typename ResourceMap::iterator end = ResourceEntries.end();
237     for (; iter != end; iter++) {
238 schoenebeck 351 if (iter->second.resource == pResource) {
239 schoenebeck 53 resource_entry_t& entry = iter->second;
240     // inform all consumers about pending update
241     std::map<ResourceConsumer<T_res>*,void*> updateargs;
242     typename ConsumerSet::iterator iterCons = entry.consumers.begin();
243     typename ConsumerSet::iterator endCons = entry.consumers.end();
244     for (; iterCons != endCons; iterCons++) {
245     if (*iterCons == pConsumer) continue;
246     void* updatearg = NULL;
247     (*iterCons)->ResourceToBeUpdated(entry.resource, updatearg);
248     if (updatearg) updateargs[*iterCons] = updatearg;
249     }
250     // update resource
251     T_res* pOldResource = entry.resource;
252 schoenebeck 947 Destroy(entry.resource, entry.lifearg);
253     entry.resource = Create(entry.key, pConsumer, entry.lifearg);
254 schoenebeck 53 // inform all consumers about update completed
255     iterCons = entry.consumers.begin();
256     endCons = entry.consumers.end();
257     for (; iterCons != endCons; iterCons++) {
258     if (*iterCons == pConsumer) continue;
259     typename std::map<ResourceConsumer<T_res>*,void*>::iterator iterArg = updateargs.find(*iterCons);
260     void* updatearg = (iterArg != updateargs.end()) ? iterArg->second : NULL;
261     (*iterCons)->ResourceUpdated(pOldResource, entry.resource, updatearg);
262     }
263     return;
264     }
265     }
266     }
267    
268 schoenebeck 947 /**
269     * Returns the life-time strategy of the given resource.
270     *
271     * @param Key - ID of the resource
272     */
273     mode_t AvailabilityMode(T_key Key) {
274     typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
275     if (iterEntry == ResourceEntries.end())
276     return ON_DEMAND; // resource entry doesn't exist, so we return the default mode
277     resource_entry_t& entry = iterEntry->second;
278     return entry.mode;
279     }
280    
281     /**
282     * Change life-time strategy of the given resource. If a life-time
283     * strategy of PERSISTENT was given and the resource was not created
284     * yet, it will immediately be created and this method will block
285     * until the resource creation was completed.
286     *
287     * @param Key - ID of the resource
288     * @param Mode - life-time strategy of resource to be set
289     * @throws Exception in case an invalid Mode was given
290     */
291     void SetAvailabilityMode(T_key Key, mode_t Mode) throw (Exception) {
292     if (Mode != ON_DEMAND && Mode != ON_DEMAND_HOLD && Mode != PERSISTENT)
293     throw Exception("ResourceManager::SetAvailabilityMode(): invalid mode");
294    
295     // search for an entry for this resource
296     typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
297     resource_entry_t* pEntry = NULL;
298     if (iterEntry == ResourceEntries.end()) { // resource entry doesn't exist
299     if (Mode == ON_DEMAND) return; // we don't create an entry for the default value
300     // create an entry for the resource
301     pEntry = &ResourceEntries[Key];
302     pEntry->key = Key;
303     pEntry->resource = NULL;
304     pEntry->mode = Mode;
305     pEntry->lifearg = NULL;
306     pEntry->entryarg = NULL;
307     } else { // resource entry exists
308     pEntry = &iterEntry->second;
309     // remove entry if necessary
310     if (Mode == ON_DEMAND && !pEntry->entryarg && pEntry->consumers.empty()) {
311     T_res* resource = pEntry->resource;
312     void* arg = pEntry->lifearg;
313     ResourceEntries.erase(iterEntry);
314     // destroy resource if necessary
315     if (resource) Destroy(resource, arg);
316     return; // done
317     }
318     pEntry->mode = Mode; // apply new mode
319     }
320    
321     // already create the resource if necessary
322     if (pEntry->mode == PERSISTENT && !pEntry->resource) {
323     try {
324     // actually create the resource
325     pEntry->resource = Create(Key, NULL /*no consumer yet*/, pEntry->lifearg);
326     } catch (...) {
327     // creating the resource failed, so skip it for now
328     pEntry->resource = NULL;
329     // rethrow the same exception
330     throw;
331     }
332     }
333     }
334    
335     /**
336 schoenebeck 958 * Returns true in case the resource associated with \a Key is
337     * currently created / "alive".
338     *
339     * @param Key - ID of resource
340     */
341     bool IsCreated(T_key Key) {
342     return Resource(Key) != NULL;
343     }
344    
345     /**
346 schoenebeck 947 * Returns custom data sticked to the given resource previously by
347     * a SetCustomData() call.
348     *
349     * @param Key - ID of resource
350     */
351     void* CustomData(T_key Key) {
352     typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
353     if (iterEntry == ResourceEntries.end()) return NULL; // resource entry doesn't exist, so we return the default mode
354     resource_entry_t& entry = iterEntry->second;
355     return entry.entryarg;
356     }
357    
358     /**
359     * This method can be used to stick custom data to an resource
360     * entry. In case the custom data is not needed anymore, you should
361     * call this method again and set \a pData to NULL, so the
362     * ResourceManager might safe space by removing the respective
363     * entry if not needed anymore.
364     *
365     * @param Key - ID of resource
366     * @param pData - pointer to custom data, or NULL if not needed anymore
367     */
368     void SetCustomData(T_key Key, void* pData) {
369     typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
370     if (pData) {
371     if (iterEntry == ResourceEntries.end()) { // entry doesnt exist, so create one
372     resource_entry_t* pEntry = &ResourceEntries[Key];
373     pEntry->key = Key;
374     pEntry->resource = NULL;
375     pEntry->mode = ON_DEMAND;
376     pEntry->lifearg = NULL;
377     pEntry->entryarg = pData; // set custom data
378     } else { // entry exists, so just update its custom data
379     iterEntry->second.entryarg = pData;
380     }
381     } else { // !pData
382     if (iterEntry == ResourceEntries.end()) return; // entry doesnt exist, so nothing to do
383     // entry exists, remove it if necessary
384     resource_entry_t* pEntry = &iterEntry->second;
385     if (pEntry->mode == ON_DEMAND && pEntry->consumers.empty()) {
386     ResourceEntries.erase(iterEntry);
387     } else iterEntry->second.entryarg = NULL;
388     }
389     }
390    
391     virtual ~ResourceManager() {} // due to C++'s nature we cannot destroy created resources here
392    
393 schoenebeck 53 protected:
394     /**
395     * Has to be implemented by the descendant to create (allocate) a
396     * resource identified by \a Key.
397     *
398     * @param Key - identifier of the resource
399     * @param pConsumer - identifier of the consumer who borrows the
400     * resource
401     * @param pArg - pointer the descendant can use to store
402     * informations he might need for destruction of
403     * the resource
404     * @returns pointer to new resource
405     */
406     virtual T_res* Create(T_key Key, ResourceConsumer<T_res>* pConsumer, void*& pArg) = 0;
407    
408     /**
409     * Has to be implemented by the descendant to destroy (free) a
410     * resource pointed by \a pResource.
411     *
412     * @param pResource - pointer to the resource
413     * @param pArg - pointer the descendant might have used when
414     * Create() was called to store informations
415     * about the resource
416     */
417     virtual void Destroy(T_res* pResource, void* pArg) = 0;
418    
419     /**
420     * Has to be implemented by the descendant to react when a consumer
421     * borrows a resource (no matter if freshly created or an already
422     * created one). Of course reacting is optional, but the descendant
423     * at least has to provide a method with empty body.
424     *
425     * @param pResource - pointer to the resource
426     * @param pConsumer - identifier of the consumer who borrows the
427     * resource
428     * @param pArg - pointer the descendant might have used when
429     * Create() was called to store informations
430     * about the resource, this information can be
431     * updated by the descendant here
432     */
433     virtual void OnBorrow(T_res* pResource, ResourceConsumer<T_res>* pConsumer, void*& pArg) = 0;
434 schoenebeck 517
435     /**
436     * Dispatcher method which should be periodically called by the
437     * descendant during update or creation of the resource associated
438     * with \a Key. This method will inform all associated consumers
439     * of the given resource about the current progress.
440     *
441     * @param Key - unique identifier of the resource which is
442     * currently creating or updating
443     * @param fProgress - current progress of that creation / update
444     * process as value between 0.0 and 1.0
445     */
446     void DispatchResourceProgressEvent(T_key Key, float fProgress) {
447     typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
448     if (iterEntry != ResourceEntries.end()) {
449     resource_entry_t& entry = iterEntry->second;
450     // inform all consumers of that resource about current progress
451     typename ConsumerSet::iterator iterCons = entry.consumers.begin();
452     typename ConsumerSet::iterator endCons = entry.consumers.end();
453     for (; iterCons != endCons; iterCons++) {
454     (*iterCons)->OnResourceProgress(fProgress);
455     }
456     }
457     }
458 schoenebeck 958
459     /**
460     * Returns pointer to the resource associated with \a Key if
461     * currently created / "alive", NULL otherwise. This method
462     * should be taken with great care in multi-threaded scenarios,
463     * since the returned resource might be destroyed by a concurrent
464     * HandBack() call.
465     *
466     * @param Key - ID of resource
467     */
468     T_res* Resource(T_key Key) {
469     typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
470     return (iterEntry == ResourceEntries.end()) ? NULL : iterEntry->second.resource;
471     }
472 schoenebeck 53 };
473    
474 schoenebeck 947 } // namespace LinuxSampler
475    
476 schoenebeck 53 #endif // __RESOURCE_MANAGER__

  ViewVC Help
Powered by ViewVC