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

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

Parent Directory Parent Directory | Revision Log Revision Log


Revision 958 - (show annotations) (download) (as text)
Wed Nov 29 19:48:38 2006 UTC (17 years, 4 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 /***************************************************************************
2 * *
3 * LinuxSampler - modular, streaming capable sampler *
4 * *
5 * Copyright (C) 2003, 2004 by Benno Senoner and Christian Schoenebeck *
6 * Copyright (C) 2005, 2006 Christian Schoenebeck *
7 * *
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 #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
38 * 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>
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
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 };
84
85 /** @brief Manager for sharing resources.
86 *
87 * 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 * 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)
95 * Create() and Destroy() methods to create and destroy a resource.
96 *
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 */
100 template<class T_key, class T_res>
101 class ResourceManager {
102 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 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 mode_t mode; ///< When should the resource be created? When should it be destroyed?
118 ConsumerSet consumers; ///< list of all consumers who currently use the resource
119 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 };
122 typedef std::map<T_key, resource_entry_t> ResourceMap;
123 ResourceMap ResourceEntries;
124
125 public:
126 /**
127 * 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 * 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 // search for an entry for this resource
152 typename ResourceMap::iterator iterEntry = ResourceEntries.find(Key);
153 if (iterEntry == ResourceEntries.end()) { // entry doesn't exist yet
154 // already create an entry for the resource
155 resource_entry_t entry;
156 entry.key = Key;
157 entry.resource = NULL;
158 entry.mode = ON_DEMAND; // default mode
159 entry.lifearg = NULL;
160 entry.entryarg = NULL;
161 entry.consumers.insert(pConsumer);
162 ResourceEntries[Key] = entry;
163 try {
164 // actually create the resource
165 entry.resource = Create(Key, pConsumer, entry.lifearg);
166 } catch (...) {
167 // creating the resource failed, so remove the entry
168 ResourceEntries.erase(Key);
169 // rethrow the same exception
170 throw;
171 }
172 // now update the entry with the created resource
173 ResourceEntries[Key] = entry;
174 OnBorrow(entry.resource, pConsumer, entry.lifearg);
175 return entry.resource;
176 } 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 }
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 * 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 *
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 // search for the entry associated with the given resource
205 typename ResourceMap::iterator iter = ResourceEntries.begin();
206 typename ResourceMap::iterator end = ResourceEntries.end();
207 for (; iter != end; iter++) {
208 if (iter->second.resource == pResource) { // found entry for resource
209 resource_entry_t& entry = iter->second;
210 entry.consumers.erase(pConsumer);
211 // remove entry if necessary
212 if (entry.mode == ON_DEMAND && !entry.entryarg && entry.consumers.empty()) {
213 T_res* resource = entry.resource;
214 void* arg = entry.lifearg;
215 ResourceEntries.erase(iter);
216 // destroy resource if necessary
217 if (resource) Destroy(resource, arg);
218 }
219 return;
220 }
221 }
222 }
223
224 /**
225 * 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 *
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 if (iter->second.resource == pResource) {
239 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 Destroy(entry.resource, entry.lifearg);
253 entry.resource = Create(entry.key, pConsumer, entry.lifearg);
254 // 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 /**
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 * 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 * 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 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
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
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 };
473
474 } // namespace LinuxSampler
475
476 #endif // __RESOURCE_MANAGER__

  ViewVC Help
Powered by ViewVC