From b3c42f22ac80ec7fb356afd875663aef680d98ea Mon Sep 17 00:00:00 2001 From: David Rose Date: Wed, 30 Aug 2006 15:21:10 +0000 Subject: [PATCH] thread protection in miles --- panda/src/audiotraits/milesAudioManager.cxx | 254 ++++++++++++++------ panda/src/audiotraits/milesAudioManager.h | 30 ++- panda/src/audiotraits/milesAudioSound.h | 2 +- panda/src/event/asyncTaskManager.cxx | 3 +- panda/src/event/config_event.cxx | 2 +- panda/src/event/eventParameter.I | 82 +++++++ panda/src/event/eventParameter.cxx | 69 ++---- panda/src/event/eventParameter.h | 42 +++- 8 files changed, 347 insertions(+), 137 deletions(-) diff --git a/panda/src/audiotraits/milesAudioManager.cxx b/panda/src/audiotraits/milesAudioManager.cxx index 2748c101c6..9fd4f00b4b 100644 --- a/panda/src/audiotraits/milesAudioManager.cxx +++ b/panda/src/audiotraits/milesAudioManager.cxx @@ -27,12 +27,14 @@ #include "config_express.h" #include "virtualFileSystem.h" #include "nullAudioSound.h" +#include "mutexHolder.h" #include TypeHandle MilesAudioManager::_type_handle; +Mutex *MilesAudioManager::_static_lock; int MilesAudioManager::_active_managers = 0; bool MilesAudioManager::_miles_active = false; HDLSFILEID MilesAudioManager::_dls_field = NULL; @@ -59,6 +61,18 @@ MilesAudioManager:: MilesAudioManager() { audio_debug("MilesAudioManager::MilesAudioManager(), this = " << (void *)this); + if (_static_lock == NULL) { + Mutex *new_lock = new Mutex; + void *result = AtomicAdjust::compare_and_exchange_ptr((void * TVOLATILE &)_static_lock, (void *)NULL, (void *)new_lock); + if (result != NULL) { + // Someone else must have assigned the mutex first. OK. + nassertv(_static_lock != new_lock); + delete new_lock; + } + nassertv((Mutex * TVOLATILE &)_static_lock != NULL); + } + + MutexHolder st_holder(*(Mutex * TVOLATILE &)_static_lock); if (_managers == (Managers *)NULL) { _managers = new Managers; } @@ -165,14 +179,22 @@ MilesAudioManager() { //////////////////////////////////////////////////////////////////// MilesAudioManager:: ~MilesAudioManager() { - audio_debug("MilesAudioManager::~MilesAudioManager(), this = " - << (void *)this); - nassertv(_managers != (Managers *)NULL); - Managers::iterator mi = _managers->find(this); - nassertv(mi != _managers->end()); - _managers->erase(mi); + { + MutexHolder st_holder(*_static_lock); + audio_debug("MilesAudioManager::~MilesAudioManager(), this = " + << (void *)this); + nassertv(_managers != (Managers *)NULL); + Managers::iterator mi = _managers->find(this); + if (mi != _managers->end()) { + // The manager might already have been removed from the list, if + // the user has already called shutdown(). + nassertv(!_cleanup_required); + _managers->erase(mi); + } + } cleanup(); + audio_debug("MilesAudioManager::~MilesAudioManager() finished"); } @@ -187,27 +209,60 @@ MilesAudioManager:: //////////////////////////////////////////////////////////////////// void MilesAudioManager:: shutdown() { - audio_debug("shutdown(), _miles_active = " << _miles_active); - if (_managers != (Managers *)NULL) { - Managers::iterator mi; - for (mi = _managers->begin(); mi != _managers->end(); ++mi) { - (*mi)->cleanup(); + Managers old_managers; + { + MutexHolder st_holder(*_static_lock); + + audio_debug("shutdown(), _miles_active = " << _miles_active); + if (_managers != (Managers *)NULL) { + old_managers.swap(*_managers); } } + // We insist on cleaning up our manager pointers, rather than + // relying on their destructors, since there might be outstanding + // pointers to these somewhere that would otherwise prevent them + // from being cleaned up and therefore prevent Miles from being shut + // down properly. In order for this to work, we must write + // cleanup() so that it is safe to call it twice. + Managers::iterator mi; + for (mi = old_managers.begin(); mi != old_managers.end(); ++mi) { + (*mi)->cleanup(); + } + + // Another thread might conceivably have started up a new manager + // after we called shutdown(), which is technically legal and would + // incorrectly trigger the following assertion. However, presumably + // shutdown() will only be called as the application is shutting + // down anyway, and there won't be any other threads still out there + // creating audio managers (and if there were, it would defeat the + // purpose of shutdown() anyway). nassertv(_active_managers == 0); + audio_debug("shutdown() finished"); } //////////////////////////////////////////////////////////////////// // Function: MilesAudioManager::is_valid -// Access: +// Access: Public // Description: This is mostly for debugging, but it it could be // used to detect errors in a release build if you // don't mind the cpu cost. //////////////////////////////////////////////////////////////////// bool MilesAudioManager:: is_valid() { + MutexHolder holder(_lock); + + return do_is_valid(); +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::do_is_valid +// Access: Private +// Description: Assumes the lock is already held. +//////////////////////////////////////////////////////////////////// +bool MilesAudioManager:: +do_is_valid() { bool check=true; if (_sounds.size() != _lru.size()) { audio_debug("-- Error _sounds.size() != _lru.size() --"); @@ -320,59 +375,82 @@ get_sound(const string& file_name, bool) { return get_null_sound(); } - assert(is_valid()); Filename path = file_name; VirtualFileSystem *vfs = VirtualFileSystem::get_global_ptr(); vfs->resolve_filename(path, get_sound_path()); audio_debug(" resolved file_name is '"<= (unsigned int)_cache_limit) { - uncache_a_sound(); + SoundMap::const_iterator si = _sounds.find(path); + if (si != _sounds.end()) { + // Oops, someone else must have just put it in the cache. + // Throw away the sound object we just loaded. + cache_path = &((*si).first); + sd = (*si).second; + audio_debug(" sound found in pool 0x" << (void*)sd); + + } else { + // OK, put the sound object we just loaded into the cache. + while (_sounds.size() >= (unsigned int)_cache_limit) { + uncache_a_sound(); + } + + // The following is roughly like: _sounds[path] = sd; + // But, it gives us an iterator into the map. + pair ib = + _sounds.insert(SoundMap::value_type(path, sd)); + if (!ib.second) { + // The insert failed. + audio_debug(" failed map insert of "< ib - =_sounds.insert(SoundMap::value_type(path, sd)); - if (!ib.second) { - // The insert failed. - audio_debug(" failed map insert of "<set_active(_active); bool inserted = _sounds_on_loan.insert(milesAudioSound).second; nassertr(inserted, milesAudioSound.p()); - audioSound=milesAudioSound; + audioSound = milesAudioSound; } _hasMidiSounds |= (file_name.find(".mid")!=string::npos); audio_debug(" returning 0x" << (void*)audioSound); - assert(is_valid()); + assert(do_is_valid()); return audioSound; } @@ -382,17 +460,19 @@ get_sound(const string& file_name, bool) { // Description: //////////////////////////////////////////////////////////////////// void MilesAudioManager:: -uncache_sound(const string& file_name) { +uncache_sound(const string &file_name) { + MutexHolder holder(_lock); + audio_debug("MilesAudioManager::uncache_sound(file_name=\"" <resolve_filename(path, get_sound_path()); audio_debug(" path=\""<0); LRU::iterator lru_i=find(_lru.begin(), _lru.end(), &(i->first)); @@ -400,18 +480,18 @@ uncache_sound(const string& file_name) { _lru.erase(lru_i); _sounds.erase(i); } - assert(is_valid()); + assert(do_is_valid()); } //////////////////////////////////////////////////////////////////// // Function: MilesAudioManager::uncache_a_sound -// Access: Public -// Description: +// Access: Private +// Description: Assumes the lock is already held. //////////////////////////////////////////////////////////////////// void MilesAudioManager:: uncache_a_sound() { audio_debug("MilesAudioManager::uncache_a_sound()"); - assert(is_valid()); + assert(do_is_valid()); // uncache least recently used: assert(_lru.size()>0); LRU::reference path=_lru.front(); @@ -423,26 +503,26 @@ uncache_a_sound() { audio_debug(" uncaching \""<first<<"\""); _sounds.erase(i); } - assert(is_valid()); + assert(do_is_valid()); } //////////////////////////////////////////////////////////////////// // Function: MilesAudioManager::most_recently_used -// Access: Public -// Description: +// Access: Private +// Description: Assumes the lock is already held. //////////////////////////////////////////////////////////////////// void MilesAudioManager:: -most_recently_used(const string& path) { +most_recently_used(const string *path) { audio_debug("MilesAudioManager::most_recently_used(path=\"" < count) { uncache_a_sound(); } _cache_limit=count; - assert(is_valid()); + assert(do_is_valid()); } //////////////////////////////////////////////////////////////////// @@ -482,6 +575,8 @@ set_cache_limit(unsigned int count) { //////////////////////////////////////////////////////////////////// unsigned int MilesAudioManager:: get_cache_limit() const { + MutexHolder holder(_lock); + audio_debug("MilesAudioManager::get_cache_limit() returning " <<_cache_limit); return _cache_limit; @@ -494,6 +589,8 @@ get_cache_limit() const { //////////////////////////////////////////////////////////////////// void MilesAudioManager:: release_sound(MilesAudioSound* audioSound) { + MutexHolder holder(_lock); + audio_debug("MilesAudioManager::release_sound(audioSound=\"" <get_name()<<"\"), this = " << (void *)this); AudioSet::iterator ai = _sounds_on_loan.find(audioSound); @@ -766,19 +863,40 @@ force_midi_reset() { //////////////////////////////////////////////////////////////////// void MilesAudioManager:: cleanup() { - audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this - << ", _cleanup_required = " << _cleanup_required); - if (!_cleanup_required) { - return; + { + MutexHolder holder(_lock); + + audio_debug("MilesAudioManager::cleanup(), this = " << (void *)this + << ", _cleanup_required = " << _cleanup_required); + if (!_cleanup_required) { + return; + } + + // Be sure to cleanup associated sounds before cleaning up the manager: + AudioSet::iterator ai; + for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) { + (*ai)->cleanup(); + } + + do_clear_cache(); + + _cleanup_required = false; } - // Be sure to cleanup associated sounds before cleaning up the manager: - AudioSet::iterator ai; - for (ai = _sounds_on_loan.begin(); ai != _sounds_on_loan.end(); ++ai) { - (*ai)->cleanup(); - } + deactivate(); + + audio_debug("MilesAudioManager::cleanup() finished"); +} + +//////////////////////////////////////////////////////////////////// +// Function: MilesAudioManager::deactivate +// Access: Private, Static +// Description: Deactivates one manager. Grabs the static lock. +//////////////////////////////////////////////////////////////////// +void MilesAudioManager:: +deactivate() { + MutexHolder st_holder(*_static_lock); - clear_cache(); nassertv(_active_managers > 0); --_active_managers; audio_debug(" _active_managers="<<_active_managers); @@ -798,8 +916,6 @@ cleanup() { _miles_active = false; } } - _cleanup_required = false; - audio_debug("MilesAudioManager::cleanup() finished"); } //////////////////////////////////////////////////////////////////// diff --git a/panda/src/audiotraits/milesAudioManager.h b/panda/src/audiotraits/milesAudioManager.h index fe5331ba71..767869d82c 100644 --- a/panda/src/audiotraits/milesAudioManager.h +++ b/panda/src/audiotraits/milesAudioManager.h @@ -28,6 +28,7 @@ #include "pset.h" #include "pmap.h" #include "pdeque.h" +#include "pmutex.h" class MilesAudioSound; @@ -65,6 +66,8 @@ public: void stop_all_sounds(); private: + Mutex _lock; // Protects all the members of this class. + // The sound cache: class SoundData : public ReferenceCount { public: @@ -82,16 +85,16 @@ private: typedef pmap SoundMap; SoundMap _sounds; - typedef pset AudioSet; + typedef pset AudioSet; // The offspring of this manager: AudioSet _sounds_on_loan; - typedef pset SoundsPlaying; + typedef pset SoundsPlaying; // The sounds from this manager that are currently playing: SoundsPlaying _sounds_playing; // The Least Recently Used mechanism: - typedef pdeque LRU; + typedef pdeque LRU; LRU _lru; // State: float _volume; @@ -99,25 +102,35 @@ private: bool _active; int _cache_limit; bool _cleanup_required; - // keep a count for startup and shutdown: - static int _active_managers; - static bool _miles_active; + unsigned int _concurrent_sound_limit; bool _is_valid; bool _hasMidiSounds; +private: + // Static members. Protected by the following lock. + static Mutex *_static_lock; + + // keep a count for startup and shutdown: + static int _active_managers; + static bool _miles_active; + // Optional Downloadable Sound field for software midi static HDLSFILEID _dls_field; typedef pset Managers; static Managers *_managers; - + +private: + bool do_is_valid(); + void do_clear_cache(); + PT(SoundData) load(Filename file_name); // Tell the manager that the sound dtor was called. void release_sound(MilesAudioSound* audioSound); - void most_recently_used(const string& path); + void most_recently_used(const string *path); void uncache_a_sound(); void starting_sound(MilesAudioSound* audio); @@ -133,6 +146,7 @@ private: void force_midi_reset(); void cleanup(); + static void deactivate(); friend class MilesAudioSound; diff --git a/panda/src/audiotraits/milesAudioSound.h b/panda/src/audiotraits/milesAudioSound.h index 58298ee591..5304315062 100644 --- a/panda/src/audiotraits/milesAudioSound.h +++ b/panda/src/audiotraits/milesAudioSound.h @@ -149,7 +149,7 @@ private: // itwas set inactive. bool _paused; - MilesAudioSound(MilesAudioManager* manager, MilesAudioManager::SoundData *sd, + MilesAudioSound(MilesAudioManager *manager, MilesAudioManager::SoundData *sd, string file_name, float length=0.0f); void cleanup(); diff --git a/panda/src/event/asyncTaskManager.cxx b/panda/src/event/asyncTaskManager.cxx index d79a8c0f5c..3ef2086833 100644 --- a/panda/src/event/asyncTaskManager.cxx +++ b/panda/src/event/asyncTaskManager.cxx @@ -330,7 +330,7 @@ find_task(AsyncTask *task) const { void AsyncTaskManager:: service_one_task(AsyncTaskManager::AsyncTaskManagerThread *thread) { if (!_active.empty()) { - AsyncTask *task = _active.front(); + PT(AsyncTask) task = _active.front(); _active.pop_front(); thread->_servicing = task; @@ -375,6 +375,7 @@ void AsyncTaskManager:: task_done(AsyncTask *task) { task->_state = AsyncTask::S_inactive; task->_manager = NULL; + --_num_tasks; if (!task->_done_event.empty()) { PT_Event event = new Event(task->_done_event); diff --git a/panda/src/event/config_event.cxx b/panda/src/event/config_event.cxx index da9db6a3f7..19e5c58e3b 100644 --- a/panda/src/event/config_event.cxx +++ b/panda/src/event/config_event.cxx @@ -40,7 +40,7 @@ ConfigureFn(config_event) { EventStoreDouble::init_type("EventStoreDouble"); EventStoreString::init_type("EventStoreString"); EventStoreWstring::init_type("EventStoreWstring"); - EventStoreTypedRefCount::init_type("EventStoreTypedRefCount"); + EventStoreTypedRefCount::init_type(); ButtonEventList::register_with_read_factory(); EventStoreInt::register_with_read_factory(); diff --git a/panda/src/event/eventParameter.I b/panda/src/event/eventParameter.I index fbe9aedba9..66d5b562c0 100644 --- a/panda/src/event/eventParameter.I +++ b/panda/src/event/eventParameter.I @@ -47,6 +47,24 @@ EventParameter() { INLINE EventParameter:: EventParameter(const TypedWritableReferenceCount *ptr) : _ptr((TypedWritableReferenceCount *)ptr) { } +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::Pointer constructor +// Access: Public +// Description: Defines an EventParameter that stores a pointer to +// a TypedReferenceCount object. Note that a +// TypedReferenceCount is not the same kind of pointer +// as a TypedWritableReferenceCount, hence we require +// both constructors. +// +// This accepts a const pointer, even though it stores +// (and eventually returns) a non-const pointer. This +// is just the simplest way to allow both const and +// non-const pointers to be stored, but it does lose the +// constness. Be careful. +//////////////////////////////////////////////////////////////////// +INLINE EventParameter:: +EventParameter(const TypedReferenceCount *ptr) : _ptr(new EventStoreTypedRefCount((TypedReferenceCount *)ptr)) { } + //////////////////////////////////////////////////////////////////// // Function: EventParameter::Integer constructor @@ -236,6 +254,36 @@ get_wstring_value() const { return ((const EventStoreWstring *)_ptr.p())->get_value(); } +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::is_typed_ref_count +// Access: Public +// Description: Returns true if the EventParameter stores a +// TypedReferenceCount pointer, false otherwise. Note +// that a TypedReferenceCount is not exactly the same +// kind of pointer as a TypedWritableReferenceCount, +// hence the need for this separate call. +//////////////////////////////////////////////////////////////////// +INLINE bool EventParameter:: +is_typed_ref_count() const { + if (is_empty()) { + return false; + } + return _ptr->is_of_type(EventStoreTypedRefCount::get_class_type()); +} + +//////////////////////////////////////////////////////////////////// +// Function: EventParameter::get_typed_ref_count_value +// Access: Public +// Description: Retrieves the value stored in the EventParameter. It +// is only valid to call this if is_typed_ref_count() +// has already returned true. +//////////////////////////////////////////////////////////////////// +INLINE TypedReferenceCount *EventParameter:: +get_typed_ref_count_value() const { + nassertr(is_typed_ref_count(), NULL); + return ((const EventStoreTypedRefCount *)_ptr.p())->get_value(); +} + //////////////////////////////////////////////////////////////////// // Function: EventParameter::get_ptr // Access: Public @@ -275,6 +323,40 @@ INLINE EventStoreValueBase:: ~EventStoreValueBase() { } +//////////////////////////////////////////////////////////////////// +// Function: EventStoreTypedRefCount::Constructor +// Access: Public +// Description: +//////////////////////////////////////////////////////////////////// +INLINE EventStoreTypedRefCount:: +EventStoreTypedRefCount(const TypedReferenceCount *value) : + _value((TypedReferenceCount *)value) +{ +} + +//////////////////////////////////////////////////////////////////// +// Function: EventStoreTypedRefCount::set_value +// Access: Public +// Description: Changes the value stored in the parameter. It is +// dangerous to do this for a parameter already added to +// an event, since the parameters may be shared. +//////////////////////////////////////////////////////////////////// +INLINE void EventStoreTypedRefCount:: +set_value(const TypedReferenceCount *value) { + _value = (TypedReferenceCount *)value; +} + + +//////////////////////////////////////////////////////////////////// +// Function: EventStoreTypedRefCount::get_value +// Access: Public +// Description: Retrieves the value stored in the parameter. +//////////////////////////////////////////////////////////////////// +INLINE TypedReferenceCount *EventStoreTypedRefCount:: +get_value() const { + return _value; +} + //////////////////////////////////////////////////////////////////// // Function: EventStoreValue::Default Constructor // Access: Private diff --git a/panda/src/event/eventParameter.cxx b/panda/src/event/eventParameter.cxx index c3cd807395..6b5786b575 100644 --- a/panda/src/event/eventParameter.cxx +++ b/panda/src/event/eventParameter.cxx @@ -25,59 +25,7 @@ #endif TypeHandle EventStoreValueBase::_type_handle; - -//////////////////////////////////////////////////////////////////// -// Function: EventParameter::Pointer constructor -// Access: Public -// Description: Defines an EventParameter that stores a pointer to -// a TypedReferenceCount object. Note that a -// TypedReferenceCount is not the same kind of pointer -// as a TypedWritableReferenceCount, hence we require -// both constructors. -// -// This accepts a const pointer, even though it stores -// (and eventually returns) a non-const pointer. This -// is just the simplest way to allow both const and -// non-const pointers to be stored, but it does lose the -// constness. Be careful. -// -// This constructor, and the accessors for this type of -// event parameter, are declared non-inline so we don't -// have to worry about exporting this template class -// from this DLL. -//////////////////////////////////////////////////////////////////// -EventParameter:: -EventParameter(const TypedReferenceCount *ptr) : _ptr(new EventStoreTypedRefCount((TypedReferenceCount *)ptr)) { } - -//////////////////////////////////////////////////////////////////// -// Function: EventParameter::is_typed_ref_count -// Access: Public -// Description: Returns true if the EventParameter stores a -// TypedReferenceCount pointer, false otherwise. Note -// that a TypedReferenceCount is not exactly the same -// kind of pointer as a TypedWritableReferenceCount, -// hence the need for this separate call. -//////////////////////////////////////////////////////////////////// -bool EventParameter:: -is_typed_ref_count() const { - if (is_empty()) { - return false; - } - return _ptr->is_of_type(EventStoreTypedRefCount::get_class_type()); -} - -//////////////////////////////////////////////////////////////////// -// Function: EventParameter::get_typed_ref_count_value -// Access: Public -// Description: Retrieves the value stored in the EventParameter. It -// is only valid to call this if is_typed_ref_count() -// has already returned true. -//////////////////////////////////////////////////////////////////// -TypedReferenceCount *EventParameter:: -get_typed_ref_count_value() const { - nassertr(is_typed_ref_count(), NULL); - return ((const EventStoreTypedRefCount *)_ptr.p())->get_value(); -} +TypeHandle EventStoreTypedRefCount::_type_handle; //////////////////////////////////////////////////////////////////// // Function: EventParameter::output @@ -98,3 +46,18 @@ output(ostream &out) const { out << _ptr->get_type(); } } + +//////////////////////////////////////////////////////////////////// +// Function: EventStoreTypedRefCount::output +// Access: Public, Virtual +// Description: +//////////////////////////////////////////////////////////////////// +void EventStoreTypedRefCount:: +output(ostream &out) const { + if (_value == (TypedReferenceCount *)NULL) { + out << "(empty)"; + + } else { + out << _value->get_type(); + } +} diff --git a/panda/src/event/eventParameter.h b/panda/src/event/eventParameter.h index c17a81746a..8d421e80a5 100644 --- a/panda/src/event/eventParameter.h +++ b/panda/src/event/eventParameter.h @@ -43,7 +43,7 @@ class EXPCL_PANDA EventParameter { PUBLISHED: INLINE EventParameter(); INLINE EventParameter(const TypedWritableReferenceCount *ptr); - EventParameter(const TypedReferenceCount *ptr); + INLINE EventParameter(const TypedReferenceCount *ptr); INLINE EventParameter(int value); INLINE EventParameter(double value); INLINE EventParameter(const string &value); @@ -68,8 +68,8 @@ PUBLISHED: INLINE bool is_wstring() const; INLINE wstring get_wstring_value() const; - bool is_typed_ref_count() const; - TypedReferenceCount *get_typed_ref_count_value() const; + INLINE bool is_typed_ref_count() const; + INLINE TypedReferenceCount *get_typed_ref_count_value() const; INLINE TypedWritableReferenceCount *get_ptr() const; @@ -111,6 +111,41 @@ private: static TypeHandle _type_handle; }; +//////////////////////////////////////////////////////////////////// +// Class : EventStoreTypedRefCount +// Description : A class object for storing specifically objects of +// type TypedReferenceCount, which is different than +// TypedWritableReferenceCount. +//////////////////////////////////////////////////////////////////// +class EXPCL_PANDA EventStoreTypedRefCount : public EventStoreValueBase { +public: + INLINE EventStoreTypedRefCount(const TypedReferenceCount *value); + + INLINE void set_value(const TypedReferenceCount *value); + INLINE TypedReferenceCount *get_value() const; + + virtual void output(ostream &out) const; + + PT(TypedReferenceCount) _value; + +public: + virtual TypeHandle get_type() const { + return get_class_type(); + } + virtual TypeHandle force_init_type() {init_type(); return get_class_type();} + static TypeHandle get_class_type() { + return _type_handle; + } + static void init_type() { + EventStoreValueBase::init_type(); + register_type(_type_handle, "EventStoreTypedRefCount", + EventStoreValueBase::get_class_type()); + } + +private: + static TypeHandle _type_handle; +}; + //////////////////////////////////////////////////////////////////// // Class : EventStoreValue // Description : A handy class object for storing simple values (like @@ -174,7 +209,6 @@ typedef EventStoreValue EventStoreInt; typedef EventStoreValue EventStoreDouble; typedef EventStoreValue EventStoreString; typedef EventStoreValue EventStoreWstring; -typedef EventStoreValue< PT(TypedReferenceCount) > EventStoreTypedRefCount; #include "eventParameter.I"