Modified: activemq/activemq-cpp/trunk/src/main/decaf/net/TcpSocket.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/net/TcpSocket.h?view=diff&rev=542904&r1=542903&r2=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/net/TcpSocket.h (original) +++ activemq/activemq-cpp/trunk/src/main/decaf/net/TcpSocket.h Wed May 30 11:43:33 2007 @@ -201,7 +201,7 @@ * Closes this object and deallocates the appropriate resources. * @throws CMSException */ - virtual void close() throw( cms::CMSException ); + virtual void close() throw( lang::Exception ); public: @@ -210,14 +210,14 @@ * @returns true if TCP_NODELAY is enabled * @throws CMSException */ - virtual bool getTcpNoDelay() const throw ( cms::CMSException ); + virtual bool getTcpNoDelay() const throw ( lang::Exception ); /** * Sets the Status of the TCP_NODELAY param for this socket as a Bool * @param value - true if TCP_NODELAY is to be enabled * @throws CMSException */ - virtual void setTcpNoDelay( bool value ) throw ( cms::CMSException ); + virtual void setTcpNoDelay( bool value ) throw ( lang::Exception ); protected:
Added: activemq/activemq-cpp/trunk/src/main/decaf/util/Config.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/util/Config.h?view=auto&rev=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/util/Config.h (added) +++ activemq/activemq-cpp/trunk/src/main/decaf/util/Config.h Wed May 30 11:43:33 2007 @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef ACTIVEMQ_UTIL_CONFIG_H_ +#define ACTIVEMQ_UTIL_CONFIG_H_ + +#ifdef AMQCPP_DLL +#ifdef AMQCPP_EXPORTS +#define AMQCPP_API __declspec(dllexport) +#else +#define AMQCPP_API __declspec(dllimport) +#endif +#else +#define AMQCPP_API +#endif + +// +// The purpose of this header is to try to detect the supported headers +// of the platform when the ./configure script is not being used to generate +// the config.h file. +// +#if defined(HAVE_CONFIG_H) + + // config.h is generated by the ./configure script and it only + // used by unix like systems (including cygwin) + #include <config.h> + +#else /* !defined(HAVE_CONFIG_H) */ + + // Not using ./configure script and make system.. chances are your using the native build tools + // of Windows or OS X to do this build + + #if defined(_WIN32) + #ifndef HAVE_OBJBASE_H + #define HAVE_OBJBASE_H + #endif + #ifndef HAVE_RPCDCE_H + #define HAVE_RPCDCE_H + #endif + #ifndef HAVE_WINSOCK2_H + #define HAVE_WINSOCK2_H + #endif + #ifndef HAVE_STRUCT_ADDRINFO + #define HAVE_STRUCT_ADDRINFO + #endif + #ifndef HAVE_SYS_TIMEB_H + #define HAVE_SYS_TIMEB_H + #endif + #ifndef HAVE_FTIME + #define HAVE_FTIME + #endif + #ifndef HAVE_WINDOWS_H + #define HAVE_WINDOWS_H + #endif + #else + #ifndef HAVE_UUID_UUID_H + #define HAVE_UUID_UUID_H + #endif + #ifndef HAVE_UUID_T + #define HAVE_UUID_T + #endif + #ifndef HAVE_PTHREAD_H + #define HAVE_PTHREAD_H + #endif + #endif + +#endif /* !defined(HAVE_CONFIG_H) */ + +// Macro to mark attributes as unused +#ifdef __GNUC__ + #define AMQCPP_UNUSED __attribute__ ((__unused__)) +#else + #define AMQCPP_UNUSED +#endif + + +#endif /*ACTIVEMQ_UTIL_CONFIG_H_*/ Added: activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Concurrent.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Concurrent.h?view=auto&rev=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Concurrent.h (added) +++ activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Concurrent.h Wed May 30 11:43:33 2007 @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DECAF_UTIL_CONCURRENT_CONCURRENT_H_ +#define _DECAF_UTIL_CONCURRENT_CONCURRENT_H_ + +#include <decaf/util/concurrent/Lock.h> + +namespace decaf{ +namespace util{ +namespace concurrent{ + +/** + * The synchronized macro defines a mechanism for snycronizing + * a scetion of code. The macro must be passed an object that + * implements the Syncronizable interface. + * + * The macro works by creating a for loop that will loop exactly + * once, creating a Lock object that is scoped to the loop. Once + * the loop conpletes and exits the Lock object goes out of scope + * releasing the lock on object W. For added safety the if else + * is used because not all compiles restrict the lifetime of + * loop variables to the loop, they will however restrict them + * to the scope of the else. + * + * The macro would be used as follows. + * + * <Syncronizable> X; + * + * somefunction() + * { + * syncronized(X) + * { + * // Do something that needs syncronizing. + * } + * } + */ + +#define WAIT_INFINITE 0xFFFFFFFF + +#define synchronized(W) \ + if(false){} \ + else \ + for( decaf::util::concurrent::Lock lock_W(W); \ + lock_W.isLocked(); lock_W.unlock() ) + +}}} + +#endif /*_DECAF_UTIL_CONCURRENT_CONCURRENT_H_*/ Added: activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Lock.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Lock.h?view=auto&rev=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Lock.h (added) +++ activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Lock.h Wed May 30 11:43:33 2007 @@ -0,0 +1,125 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DECAF_UTIL_CONCURRENT_LOCK_H_ +#define _DECAF_UTIL_CONCURRENT_LOCK_H_ + +#include <decaf/lang/Exception.h> +#include <decaf/util/concurrent/Synchronizable.h> + +namespace decaf{ +namespace util{ +namespace concurrent{ + + /** + * A wrapper class around a given synchronization mechanism that + * provides automatic release upon destruction. + * @author Nathan Mittler + */ + class Lock + { + private: + + /** + * Flag to indicate whether or not this object has locked the + * sync object. + */ + bool locked; + + /** + * The synchronizable object to lock/unlock. + */ + Synchronizable* syncObject; + + public: + + /** + * Constructor - initializes the object member and locks + * the object if desired. + * @param object The sync object to control + * @param intiallyLocked If true, the object will automatically + * be locked. + */ + Lock( Synchronizable* object, const bool intiallyLocked = true ) + { + try{ + syncObject = object; + locked = false; + + if( intiallyLocked ) + { + lock(); + } + } + DECAF_CATCH_RETHROW( lang::Exception ) + DECAF_CATCHALL_THROW( lang::Exception ) + } + + /** + * Destructor - Unlocks the object if it is locked. + */ + virtual ~Lock() + { + try{ + if( locked ) + { + syncObject->unlock(); + } + } + DECAF_CATCH_RETHROW( lang::Exception ) + DECAF_CATCHALL_THROW( lang::Exception ) + } + + /** + * Locks the object. + */ + void lock() + { + try{ + syncObject->lock(); + locked = true; + } + DECAF_CATCH_RETHROW( lang::Exception ) + DECAF_CATCHALL_THROW( lang::Exception ) + } + + /** + * Unlocks the object. + */ + void unlock() + { + try{ + if(locked) + { + syncObject->unlock(); + locked = false; + } + } + DECAF_CATCH_RETHROW( lang::Exception ) + DECAF_CATCHALL_THROW( lang::Exception ) + } + + /** + * Indicates whether or not the object is locked. + * @return true if the object is locked, otherwise false. + */ + bool isLocked() const{ return locked; } + }; + +}}} + +#endif // _DECAF_UTIL_CONCURRENT_LOCK_H_ Added: activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.cpp URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.cpp?view=auto&rev=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.cpp (added) +++ activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.cpp Wed May 30 11:43:33 2007 @@ -0,0 +1,263 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <decaf/util/concurrent/Mutex.h> + +using namespace decaf; +using namespace decaf::util; +using namespace decaf::util::concurrent; + +//////////////////////////////////////////////////////////////////////////////// +Mutex::Mutex() +{ +#ifdef HAVE_PTHREAD_H + pthread_mutexattr_t attr; + pthread_mutexattr_init(&attr); + pthread_mutex_init(&mutex, &attr); + pthread_mutexattr_destroy(&attr); +#else + InitializeCriticalSection(&mutex); +#endif + + lock_owner = 0; + lock_count = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +Mutex::~Mutex() +{ + // Unlock the mutex. + unlock(); + +#ifdef HAVE_PTHREAD_H + pthread_mutex_destroy(&mutex); +#else + DeleteCriticalSection(&mutex); +#endif +} + +//////////////////////////////////////////////////////////////////////////////// +void Mutex::lock() throw( lang::Exception ) +{ + if(isLockOwner()) + { + lock_count++; + } + else + { +#ifdef HAVE_PTHREAD_H + pthread_mutex_lock(&mutex); +#else + EnterCriticalSection(&mutex); +#endif + + lock_count = 1; + lock_owner = Thread::getId(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +void Mutex::unlock() throw( lang::Exception ) +{ + if(lock_owner == 0) + { + return; + } + + if(!isLockOwner()) + { + throw lang::ActiveMQException( + __FILE__, __LINE__, + "Mutex::unlock - Failed, not Lock Owner!" ); + } + + lock_count--; + + if(lock_count == 0) + { + lock_owner = 0; + +#ifdef HAVE_PTHREAD_H + pthread_mutex_unlock(&mutex); +#else + LeaveCriticalSection(&mutex); +#endif + } +} + +//////////////////////////////////////////////////////////////////////////////// +void Mutex::wait() throw( lang::Exception ) +{ + // Delegate to the timed version + wait( WAIT_INFINITE ); +} + +//////////////////////////////////////////////////////////////////////////////// +void Mutex::wait( unsigned long millisecs ) + throw( lang::Exception ) +{ + if(!isLockOwner()) + { + throw lang::Exception( + __FILE__, __LINE__, + "Mutex::wait - Failed, not Lock Owner!"); + } + + // Save the current owner and Lock count as we are going to + // unlock and release for someone else to lock on potentially. + // When we come back and re-lock we want to restore to the + // state we were in before. + unsigned long lock_owner = this->lock_owner; + int lock_count = this->lock_count; + + this->lock_count = 0; + this->lock_owner = 0; + +#ifdef HAVE_PTHREAD_H + + // Create this threads wait event + pthread_cond_t waitEvent; + pthread_cond_init(&waitEvent, NULL); + + // Store the event in the queue so that a notify can + // call it and wake up the thread. + eventQ.push_back(&waitEvent); + + int returnValue = 0; + if(millisecs != WAIT_INFINITE) + { + timeval now = {0,0}; + gettimeofday(&now, NULL); + + timespec wait = {0,0}; + wait.tv_sec = now.tv_sec + (millisecs / 1000); + wait.tv_nsec = (now.tv_usec * 1000) + ((millisecs % 1000) * 1000000); + + if(wait.tv_nsec > 1000000000) + { + wait.tv_sec++; + wait.tv_nsec -= 1000000000; + } + + returnValue = pthread_cond_timedwait(&waitEvent, &mutex, &wait); + } + else + { + returnValue = pthread_cond_wait(&waitEvent, &mutex); + } + + // If the wait did not succeed for any reason, remove it + // from the queue. + if( returnValue != 0 ){ + std::list<pthread_cond_t*>::iterator iter = eventQ.begin(); + for( ; iter != eventQ.end(); ++iter ){ + if( *iter == &waitEvent ){ + eventQ.erase(iter); + break; + } + } + } + + // Destroy our wait event now, the notify method will have removed it + // from the event queue. + pthread_cond_destroy(&waitEvent); + +#else // !defined(HAVE_PTHREAD_H) + + // Create the event to wait on + HANDLE waitEvent = CreateEvent( NULL, false, false, NULL ); + + if(waitEvent == NULL) + { + throw lang::Exception( + __FILE__, __LINE__, + "Mutex::Mutex - Failed Creating Event." ); + } + + eventQ.push_back( waitEvent ); + + // Release the Lock + LeaveCriticalSection( &mutex ); + + // Wait for a signal + WaitForSingleObject( waitEvent, millisecs ); + + // Reaquire the Lock + EnterCriticalSection( &mutex ); + + // Clean up the event, the notif methods will have + // already poped it from the queue. + CloseHandle( waitEvent ); + +#endif // !defined(HAVE_PTHREAD_H) + + // restore the owner + this->lock_owner = lock_owner; + this->lock_count = lock_count; +} + +//////////////////////////////////////////////////////////////////////////////// +void Mutex::notify() throw( lang::Exception ) +{ + if( !isLockOwner() ) + { + throw lang::Exception( + __FILE__, __LINE__, + "Mutex::Notify - Failed, not Lock Owner!" ); + } + + if( !eventQ.empty() ) + { +#ifdef HAVE_PTHREAD_H + pthread_cond_signal( eventQ.front() ); + eventQ.pop_front(); +#else + SetEvent( eventQ.front() ); + eventQ.pop_front(); +#endif + } +} + +//////////////////////////////////////////////////////////////////////////////// +void Mutex::notifyAll() throw( lang::Exception ) +{ + if(!isLockOwner()) + { + throw lang::Exception( + __FILE__, __LINE__, + "Mutex::NotifyAll - Failed, not Lock Owner!" ); + } + +#ifdef HAVE_PTHREAD_H + + while(!eventQ.empty()) + { + pthread_cond_signal( eventQ.front() ); + eventQ.pop_front(); + } + +#else + + while(!eventQ.empty()) + { + SetEvent( eventQ.front() ); + eventQ.pop_front(); + } + +#endif +} + Added: activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.h?view=auto&rev=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.h (added) +++ activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Mutex.h Wed May 30 11:43:33 2007 @@ -0,0 +1,149 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DECAF_UTIL_CONCURRENT_MUTEX_H_ +#define _DECAF_UTIL_CONCURRENT_MUTEX_H_ + +// Includes. +#include <decaf/util/concurrent/Synchronizable.h> +#include <decaf/util/concurrent/Concurrent.h> +#include <decaf/lang/Thread.h> +#include <decaf/util/Config.h> +#include <list> + +#ifdef HAVE_SYS_TIME_H + #include <sys/time.h> +#endif + +#ifdef HAVE_PTHREAD_H + #include <pthread.h> +#else + #include <windows.h> + + #if ( !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0400) + #if ( !defined(WINVER) || WINVER < 0x0400) + #pragma message ("Unsupported platform, Windows NT 4.0 or later required") + #endif + #endif + +#endif + +#include <assert.h> + +namespace decaf{ +namespace util{ +namespace concurrent{ + + /** + * Creates a pthread_mutex_t object. The object is created + * such that successive locks from the same thread is allowed + * and will be successful. + * @see pthread_mutex_t + */ + class AMQCPP_API Mutex : public Synchronizable + { + private: // Data + + /** + * The mutex object. + */ + #ifdef HAVE_PTHREAD_H + pthread_mutex_t mutex; + + std::list<pthread_cond_t*> eventQ; + #else + CRITICAL_SECTION mutex; + + std::list<HANDLE> eventQ; + #endif + + // Lock Status Members + int lock_count; + unsigned long lock_owner; + + public: + + /** + * Constructor - creates and initializes the mutex. + */ + Mutex(); + + /** + * Destructor - destroys the mutex object. + */ + virtual ~Mutex(); + + /** + * Locks the object. + * @throws ActiveMQException + */ + virtual void lock() throw( lang::Exception ); + + /** + * Unlocks the object. + * @throws ActiveMQException + */ + virtual void unlock() throw( lang::Exception ); + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. + * @throws ActiveMQException + */ + virtual void wait() throw( lang::Exception ); + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param millisecs the time in milliseconds to wait. + * @throws ActiveMQException + */ + virtual void wait( unsigned long millisecs ) + throw( lang::Exception ); + + /** + * Signals a waiter on this object that it can now wake + * up and continue. + * @throws ActiveMQException + */ + virtual void notify() throw( lang::Exception ); + + /** + * Signals the waiters on this object that it can now wake + * up and continue. + * @throws ActiveMQException + */ + virtual void notifyAll() throw( lang::Exception ); + + private: + + /** + * Check if the calling thread is the Lock Owner + * @retuns true if the caller is the lock owner + */ + bool isLockOwner() + { + return lock_owner == lang::Thread::getId(); + } + + }; + +}}} + +#endif // _DECAF_UTIL_CONCURRENT_MUTEX_H_ Added: activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Synchronizable.h URL: http://svn.apache.org/viewvc/activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Synchronizable.h?view=auto&rev=542904 ============================================================================== --- activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Synchronizable.h (added) +++ activemq/activemq-cpp/trunk/src/main/decaf/util/concurrent/Synchronizable.h Wed May 30 11:43:33 2007 @@ -0,0 +1,90 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DECAF_UTIL_CONCURRENT_SYNCHRONIZABLE_H_ +#define _DECAF_UTIL_CONCURRENT_SYNCHRONIZABLE_H_ + +#include <decaf/lang/Exception.h> +#include <decaf/util/Config.h> + +namespace decaf{ +namespace util{ +namespace concurrent{ + + /** + * The interface for all synchronizable objects (that is, objects + * that can be locked and unlocked). + */ + class Synchronizable + { + public: + + virtual ~Synchronizable(){} + + /** + * Locks the object. + * @throws Exception + */ + virtual void lock() throw( lang::Exception ) = 0; + + /** + * Unlocks the object. + * @throws Exception + */ + virtual void unlock() throw( lang::Exception ) = 0; + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. + * @throws Exception + */ + virtual void wait() throw( lang::Exception ) = 0; + + /** + * Waits on a signal from this object, which is generated + * by a call to Notify. Must have this object locked before + * calling. This wait will timeout after the specified time + * interval. + * @param millisecs the time in millisecsonds to wait, or + * WAIT_INIFINITE + * @throws Exception + */ + virtual void wait(unsigned long millisecs) + throw( lang::Exception ) = 0; + + /** + * Signals a waiter on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws Exception + */ + virtual void notify() throw( lang::Exception ) = 0; + + /** + * Signals the waiters on this object that it can now wake + * up and continue. Must have this object locked before + * calling. + * @throws Exception + */ + virtual void notifyAll() throw( lang::Exception ) = 0; + + }; + +}}} + +#endif /*_DECAF_UTIL_CONCURRENT_SYNCHRONIZABLE_H_*/
