version 1.57.2.4, 2006/07/29 01:08:49
|
version 1.69, 2009/08/13 13:13:07
|
|
|
//%2006//////////////////////////////////////////////////////////////////////// |
//%LICENSE//////////////////////////////////////////////////////////////// |
// | // |
// Copyright (c) 2000, 2001, 2002 BMC Software; Hewlett-Packard Development |
// Licensed to The Open Group (TOG) under one or more contributor license |
// Company, L.P.; IBM Corp.; The Open Group; Tivoli Systems. |
// agreements. Refer to the OpenPegasusNOTICE.txt file distributed with |
// Copyright (c) 2003 BMC Software; Hewlett-Packard Development Company, L.P.; |
// this work for additional information regarding copyright ownership. |
// IBM Corp.; EMC Corporation, The Open Group. |
// Each contributor licenses this file to you under the OpenPegasus Open |
// Copyright (c) 2004 BMC Software; Hewlett-Packard Development Company, L.P.; |
// Source License; you may not use this file except in compliance with the |
// IBM Corp.; EMC Corporation; VERITAS Software Corporation; The Open Group. |
// License. |
// Copyright (c) 2005 Hewlett-Packard Development Company, L.P.; IBM Corp.; |
// |
// EMC Corporation; VERITAS Software Corporation; The Open Group. |
// Permission is hereby granted, free of charge, to any person obtaining a |
// Copyright (c) 2006 Hewlett-Packard Development Company, L.P.; IBM Corp.; |
// copy of this software and associated documentation files (the "Software"), |
// EMC Corporation; Symantec Corporation; The Open Group. |
// to deal in the Software without restriction, including without limitation |
// |
// the rights to use, copy, modify, merge, publish, distribute, sublicense, |
// Permission is hereby granted, free of charge, to any person obtaining a copy |
// and/or sell copies of the Software, and to permit persons to whom the |
// of this software and associated documentation files (the "Software"), to |
// Software is furnished to do so, subject to the following conditions: |
// deal in the Software without restriction, including without limitation the |
// |
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or |
// The above copyright notice and this permission notice shall be included |
// sell copies of the Software, and to permit persons to whom the Software is |
// in all copies or substantial portions of the Software. |
// furnished to do so, subject to the following conditions: |
// |
// |
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS |
// THE ABOVE COPYRIGHT NOTICE AND THIS PERMISSION NOTICE SHALL BE INCLUDED IN |
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
// ALL COPIES OR SUBSTANTIAL PORTIONS OF THE SOFTWARE. THE SOFTWARE IS PROVIDED |
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
// "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT |
// IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY |
// LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR |
// CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, |
// PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT |
// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE |
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
// SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
// ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION |
// |
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
////////////////////////////////////////////////////////////////////////// |
// |
|
//============================================================================== |
|
// |
|
// Author: Mike Day (mdday@us.ibm.com) |
|
// |
|
// Modified By: Markus Mueller |
|
// Roger Kumpf, Hewlett-Packard Company (roger_kumpf@hp.com) |
|
// Amit K Arora, IBM (amita@in.ibm.com) for PEP#101 |
|
// David Dillard, VERITAS Software Corp. |
|
// (david.dillard@veritas.com) |
|
// Sean Keenan, Hewlett-Packard Company (sean.keenan@hp.com) |
|
// Josephine Eskaline Joyce, IBM (jojustin@in.ibm.com) for Bug#2393 |
|
// | // |
//%///////////////////////////////////////////////////////////////////////////// | //%///////////////////////////////////////////////////////////////////////////// |
| |
|
|
| |
/////////////////////////////////////////////////////////////////////////////// | /////////////////////////////////////////////////////////////////////////////// |
| |
class PEGASUS_COMMON_LINKAGE thread_data : public Linkable |
enum TSD_Key |
|
{ |
|
TSD_ACCEPT_LANGUAGES, |
|
TSD_SLEEP_SEM, |
|
TSD_LAST_ACTIVITY_TIME, |
|
TSD_WORK_FUNC, |
|
TSD_WORK_PARM, |
|
TSD_BLOCKING_SEM, |
|
TSD_CIMOM_HANDLE_CONTENT_LANGUAGES, |
|
TSD_RESERVED_1, |
|
TSD_RESERVED_2, |
|
TSD_RESERVED_3, |
|
TSD_RESERVED_4, |
|
TSD_RESERVED_5, |
|
TSD_RESERVED_6, |
|
TSD_RESERVED_7, |
|
TSD_RESERVED_8, |
|
// Add new TSD keys before this line. |
|
TSD_COUNT |
|
}; |
|
|
|
class thread_data |
{ | { |
|
/** |
|
* This class is NOT build thread-safe. |
|
* The Caller(user) of this class has to ensure there is no collision |
|
* taking place. |
|
* |
|
* There is no mechanism in place to protect threads from manipulating |
|
* the same thread-specific storage at one time. |
|
* Make sure the possibility for a parallel access to the same |
|
* threads-specific data from multiple threads cannot arise. |
|
* |
|
* In OpenPegasus this class is used in the ThreadPool |
|
* - on initialisation and creation of threads owned by ThreadPool |
|
* - on threads that are idle inside the ThreadPool |
|
* - on the ThreadPools main thread (the thread which the ThreadPool |
|
* runs in) |
|
* In OpenPegasus this class is used in the |
|
* ClientCIMOMHandleRep and InternalCIMOMHandleRep |
|
* - on the current active Thread which belongs to that CIMOMHandle |
|
* |
|
*/ |
public: | public: |
| |
static void default_delete(void *data); |
static void default_delete(void *data) |
|
{ |
|
if (data) |
|
::operator delete(data); |
|
} |
| |
thread_data(const char *key) : _delete_func(NULL), _data(NULL), _size(0) |
thread_data(TSD_Key key) : _delete_func(0), _data(0), _size(0), _key(key) |
{ | { |
PEGASUS_ASSERT(key != NULL); |
|
size_t keysize = strlen(key); |
|
_key.reset(new char[keysize + 1]); |
|
memcpy(_key.get(), key, keysize); |
|
_key.get()[keysize] = 0x00; |
|
} | } |
| |
thread_data(const char *key, size_t size) : |
thread_data(TSD_Key key, size_t size) : |
_delete_func(default_delete), _size(size) |
_delete_func(default_delete), _size(size), _key(key) |
{ | { |
PEGASUS_ASSERT(key != NULL); |
|
size_t keysize = strlen(key); |
|
_key.reset(new char[keysize + 1]); |
|
memcpy(_key.get(), key, keysize); |
|
_key.get()[keysize] = 0x00; |
|
_data =::operator new(_size); | _data =::operator new(_size); |
} | } |
| |
thread_data(const char *key, size_t size, void *data) : |
thread_data(TSD_Key key, size_t size, void* data) |
_delete_func(default_delete), _size(size) |
: _delete_func(default_delete), _size(size), _key(key) |
{ | { |
PEGASUS_ASSERT(key != NULL); |
|
PEGASUS_ASSERT(data != NULL); |
|
size_t keysize = strlen(key); |
|
|
|
_key.reset(new char[keysize + 1]); |
|
memcpy(_key.get(), key, keysize); |
|
_key.get()[keysize] = 0x00; |
|
_data =::operator new(_size); | _data =::operator new(_size); |
memcpy(_data, data, size); | memcpy(_data, data, size); |
} | } |
| |
~thread_data() | ~thread_data() |
{ | { |
if (_data != NULL) |
if (_data && _delete_func) |
if (_delete_func != NULL) |
(*_delete_func)(_data); |
{ |
|
_delete_func(_data); |
|
} |
|
} | } |
| |
/** | /** |
|
|
*/ | */ |
void put_data(void (*del) (void *), size_t size, void *data) | void put_data(void (*del) (void *), size_t size, void *data) |
{ | { |
if (_data != NULL) |
if (_data && _delete_func) |
if (_delete_func != NULL) |
(*_delete_func)(_data); |
_delete_func(_data); |
|
| |
_delete_func = del; | _delete_func = del; |
_data = data; | _data = data; |
_size = size; | _size = size; |
return; |
|
} | } |
| |
size_t get_size() | size_t get_size() |
|
|
return _size; | return _size; |
} | } |
| |
|
void* get_data() |
|
{ |
|
return _data; |
|
} |
|
|
/** | /** |
* This function is used to retrieve data from the |
This function is used to retrieve data from the |
* TSD, the thread specific data. |
TSD, the thread specific data. |
* |
|
* Be aware that there is NOTHING in place to stop |
Be aware that there is NOTHING in place to stop |
* other users of the thread to change the data you |
other users of the thread to change the data you |
* get from this function. |
get from this function. |
* |
|
* You, the developer has to make sure that there are |
You, the developer has to make sure that there are |
* no situations in which this can arise (ie, have a |
no situations in which this can arise (ie, have a |
* lock for the function which manipulates the TSD. |
lock for the function which manipulates the TSD. |
*/ | */ |
void get_data(void **data, size_t * size) | void get_data(void **data, size_t * size) |
{ | { |
if (data == NULL || size == NULL) |
if (data == 0 || size == 0) |
throw NullPointer(); | throw NullPointer(); |
| |
*data = _data; | *data = _data; |
*size = _size; | *size = _size; |
return; |
|
|
|
} | } |
| |
// @exception NullPointer | // @exception NullPointer |
void copy_data(void **buf, size_t * size) | void copy_data(void **buf, size_t * size) |
{ | { |
if ((buf == NULL) || (size == NULL)) |
if ((buf == 0) || (size == 0)) |
throw NullPointer(); | throw NullPointer(); |
|
|
*buf =::operator new(_size); | *buf =::operator new(_size); |
*size = _size; | *size = _size; |
memcpy(*buf, _data, _size); | memcpy(*buf, _data, _size); |
return; |
|
} |
|
|
|
inline Boolean operator==(const void *key) const |
|
{ |
|
if (!strcmp(_key.get(), reinterpret_cast < const char *>(key))) |
|
return (true); |
|
return (false); |
|
} | } |
| |
inline Boolean operator==(const thread_data & b) const |
|
{ |
|
return (operator==(b._key.get())); |
|
} |
|
|
|
static bool equal(const thread_data * node, const void *key) |
|
{ |
|
return ((thread_data *) node)->operator==(key); |
|
} |
|
| |
private: | private: |
void (*_delete_func) (void *data); |
|
thread_data(); | thread_data(); |
|
thread_data(const thread_data& x); |
|
thread_data& operator=(const thread_data& x); |
|
|
|
void (*_delete_func)(void*); |
void *_data; | void *_data; |
size_t _size; | size_t _size; |
AutoArrayPtr < char >_key; |
TSD_Key _key; |
|
|
friend class Thread; |
|
}; | }; |
| |
| |
|
|
{ | { |
public: | public: |
| |
Thread(ThreadReturnType(PEGASUS_THREAD_CDECL * start) (void *), |
Thread( |
void *parameter, Boolean detached); |
ThreadReturnType(PEGASUS_THREAD_CDECL* start) (void*), |
|
void* parameter, |
|
Boolean detached); |
| |
~Thread(); | ~Thread(); |
| |
|
|
ThreadStatus run(); | ThreadStatus run(); |
| |
// get the user parameter | // get the user parameter |
inline void *get_parm() |
void *get_parm() |
{ | { |
return _thread_parm; | return _thread_parm; |
} | } |
|
|
// when it should die. | // when it should die. |
void cancel(); | void cancel(); |
| |
// cancel if there is a pending cancellation request |
|
void test_cancel(); |
|
|
|
Boolean is_cancelled(); |
|
|
|
// for user-level threads - put the calling thread | // for user-level threads - put the calling thread |
// to sleep and jump to the thread scheduler. | // to sleep and jump to the thread scheduler. |
// platforms with preemptive scheduling and native threads | // platforms with preemptive scheduling and native threads |
|
|
| |
void thread_switch(); | void thread_switch(); |
| |
#if defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU) |
|
// suspend this thread |
|
void suspend(); |
|
|
|
// resume this thread |
|
void resume(); |
|
#endif |
|
|
|
static void sleep(Uint32 msec); | static void sleep(Uint32 msec); |
| |
// block the calling thread until this thread terminates | // block the calling thread until this thread terminates |
void join(); | void join(); |
void thread_init(); |
|
| |
// thread routine needs to call this function when | // thread routine needs to call this function when |
// it is ready to exit | // it is ready to exit |
|
|
| |
// stack of functions to be called when thread terminates | // stack of functions to be called when thread terminates |
// will be called last in first out (LIFO) | // will be called last in first out (LIFO) |
// @exception IPCException |
|
void cleanup_push(void (*routine) (void *), void *parm); | void cleanup_push(void (*routine) (void *), void *parm); |
| |
// @exception IPCException |
|
void cleanup_pop(Boolean execute = true); | void cleanup_pop(Boolean execute = true); |
| |
// create and initialize a tsd |
|
// @exception IPCException |
|
inline void create_tsd(const char *key, int size, void *buffer) |
|
{ |
|
AutoPtr < thread_data > tsd(new thread_data(key, size, buffer)); |
|
_tsd.insert_front(tsd.get()); |
|
tsd.release(); |
|
} |
|
|
|
// get the buffer associated with the key | // get the buffer associated with the key |
// NOTE: this call leaves the tsd LOCKED !!!! | // NOTE: this call leaves the tsd LOCKED !!!! |
// @exception IPCException |
void* reference_tsd(TSD_Key key) |
inline void *reference_tsd(const char *key) |
|
{ | { |
_tsd.lock(); |
if (_tsd[key]) |
thread_data *tsd = _tsd.find(thread_data::equal, key); |
return _tsd[key]->get_data(); |
if (tsd != NULL) |
|
return ((void *) (tsd->_data)); |
|
else | else |
return (NULL); |
return 0; |
} | } |
| |
// @exception IPCException |
|
inline void *try_reference_tsd(const char *key) |
|
{ |
|
_tsd.try_lock(); |
|
thread_data *tsd = _tsd.find(thread_data::equal, key); |
|
if (tsd != NULL) |
|
return ((void *) (tsd->_data)); |
|
else |
|
return (NULL); |
|
} |
|
|
|
|
|
// release the lock held on the tsd | // release the lock held on the tsd |
// NOTE: assumes a corresponding and prior call to reference_tsd() !!! | // NOTE: assumes a corresponding and prior call to reference_tsd() !!! |
// @exception IPCException |
void dereference_tsd() |
inline void dereference_tsd() |
|
{ | { |
_tsd.unlock(); |
|
} | } |
| |
// delete the tsd associated with the key | // delete the tsd associated with the key |
// @exception IPCException |
void delete_tsd(TSD_Key key) |
inline void delete_tsd(const char *key) |
|
{ | { |
AutoPtr < thread_data > tsd(_tsd.remove(thread_data::equal, key)); |
thread_data* tsd; |
|
|
|
tsd = _tsd[key]; |
|
_tsd[key] = 0; |
|
|
|
if (tsd) |
|
delete tsd; |
} | } |
| |
// @exception IPCException |
void empty_tsd() |
inline void empty_tsd() |
|
{ | { |
_tsd.clear(); |
thread_data* data[TSD_COUNT]; |
|
|
|
memcpy(data, _tsd, sizeof(_tsd)); |
|
memset(_tsd, 0, sizeof(_tsd)); |
|
|
|
for (size_t i = 0; i < TSD_COUNT; i++) |
|
{ |
|
if (data[i]) |
|
delete data[i]; |
|
} |
} | } |
| |
// create or re-initialize tsd associated with the key | // create or re-initialize tsd associated with the key |
// if the tsd already exists, delete the existing buffer | // if the tsd already exists, delete the existing buffer |
// @exception IPCException |
void put_tsd( |
void put_tsd(const char *key, void (*delete_func) (void *), Uint32 size, |
TSD_Key key, |
|
void (*delete_func)(void*), |
|
Uint32 size, |
void *value) | void *value) |
{ | { |
PEGASUS_ASSERT(key != NULL); |
thread_data* tsd = new thread_data(key); |
AutoPtr < thread_data > tsd; |
tsd->put_data(delete_func, size, value); |
tsd.reset(_tsd.remove(thread_data::equal, key)); // may throw |
|
// an IPC |
thread_data* old; |
// exception |
|
tsd.reset(); |
old = _tsd[key]; |
AutoPtr < thread_data > ntsd(new thread_data(key)); |
_tsd[key] = tsd; |
ntsd->put_data(delete_func, size, value); |
|
try |
if (old) |
{ |
delete old; |
_tsd.insert_front(ntsd.get()); |
|
} |
|
catch(IPCException & e) |
|
{ |
|
e = e; |
|
throw; |
|
} |
|
ntsd.release(); |
|
} | } |
inline ThreadReturnType get_exit() |
|
|
ThreadReturnType get_exit() |
{ | { |
return _exit_code; | return _exit_code; |
} | } |
inline ThreadType self() |
|
|
ThreadType self() |
{ | { |
return Threads::self(); | return Threads::self(); |
} | } |
|
|
return _handle; | return _handle; |
} | } |
| |
|
Boolean isDetached() |
|
{ |
|
return _is_detached; |
|
} |
|
|
void detach(); | void detach(); |
| |
// | // |
|
|
// Note: this may return NULL if no Thread object is associated | // Note: this may return NULL if no Thread object is associated |
// with the caller's thread. | // with the caller's thread. |
// | // |
static Thread *getCurrent(); // l10n |
static Thread *getCurrent(); |
| |
// | // |
// Sets the Thread object associated with the caller's thread. | // Sets the Thread object associated with the caller's thread. |
// Note: the Thread object must be placed on the heap. | // Note: the Thread object must be placed on the heap. |
// | // |
static void setCurrent(Thread * thrd); // l10n |
static void setCurrent(Thread* thrd); |
| |
// | // |
// Gets the AcceptLanguageList object associated with the caller's | // Gets the AcceptLanguageList object associated with the caller's |
|
|
// Note: this may return NULL if no Thread object, or no | // Note: this may return NULL if no Thread object, or no |
// AcceptLanguageList object, is associated with the caller's thread. | // AcceptLanguageList object, is associated with the caller's thread. |
// | // |
static AcceptLanguageList *getLanguages(); // l10n |
static AcceptLanguageList* getLanguages(); |
| |
// | // |
// Sets the AcceptLanguageList object associated with the caller's | // Sets the AcceptLanguageList object associated with the caller's |
// Thread. | // Thread. |
// Note: a Thread object must have been previously associated with | // Note: a Thread object must have been previously associated with |
// the caller's thread. | // the caller's thread. |
// Note: the AcceptLanguageList object must be placed on the heap. |
|
// | // |
static void setLanguages(AcceptLanguageList * langs); // l10n |
static void setLanguages(const AcceptLanguageList& langs); |
| |
// | // |
// Removes the AcceptLanguageList object associated with the caller's | // Removes the AcceptLanguageList object associated with the caller's |
// Thread. | // Thread. |
// | // |
static void clearLanguages(); // l10n |
static void clearLanguages(); |
| |
private: | private: |
Thread(); | Thread(); |
| |
static Sint8 initializeKey(); // l10n |
static Sint8 initializeKey(); |
| |
// @exception IPCException |
|
inline void create_tsd(const char *key) |
|
{ |
|
AutoPtr < thread_data > tsd(new thread_data(key)); |
|
_tsd.insert_front(tsd.get()); |
|
tsd.release(); |
|
} |
|
ThreadHandle _handle; | ThreadHandle _handle; |
Boolean _is_detached; | Boolean _is_detached; |
Boolean _cancel_enabled; |
|
Boolean _cancelled; | Boolean _cancelled; |
| |
// always pass this * as the void * parameter to the thread | // always pass this * as the void * parameter to the thread |
|
|
| |
ThreadReturnType(PEGASUS_THREAD_CDECL * _start) (void *); | ThreadReturnType(PEGASUS_THREAD_CDECL * _start) (void *); |
List < cleanup_handler, Mutex > _cleanup; | List < cleanup_handler, Mutex > _cleanup; |
List < thread_data, Mutex > _tsd; |
thread_data* _tsd[TSD_COUNT]; |
| |
void *_thread_parm; | void *_thread_parm; |
ThreadReturnType _exit_code; | ThreadReturnType _exit_code; |
static Boolean _signals_blocked; | static Boolean _signals_blocked; |
static TSDKeyType _platform_thread_key; // l10n |
static TSDKeyType _platform_thread_key; |
static Boolean _key_initialized; // l10n |
static Boolean _key_initialized; |
static Boolean _key_error; // l10n |
static Boolean _key_error; |
}; |
|
|
|
|
|
class PEGASUS_COMMON_LINKAGE ThreadPool |
|
{ |
|
public: |
|
|
|
/** |
|
Constructs a new ThreadPool object. |
|
@param initialSize The number of threads that are initially added to |
|
the thread pool. |
|
@param key A name for this thread pool that can be used to determine |
|
equality of two thread pool objects. Only the first 16 characters |
|
of this value are used. |
|
@param minThreads The minimum number of threads that should be |
|
contained in this thread pool at any given time. |
|
@param maxThreads The maximum number of threads that should be |
|
contained in this thread pool at any given time. |
|
@param deallocateWait The minimum time that a thread should be idle |
|
before it is removed from the pool and cleaned up. |
|
*/ |
|
ThreadPool(Sint16 initialSize, |
|
const char *key, |
|
Sint16 minThreads, |
|
Sint16 maxThreads, struct timeval &deallocateWait); |
|
|
|
/** |
|
Destructs the ThreadPool object. |
|
*/ |
|
~ThreadPool(); |
|
|
|
/** |
|
Allocate and start a thread to do a unit of work. |
|
@param parm A generic parameter to pass to the thread |
|
@param work A pointer to the function that is to be executed by |
|
the thread |
|
@param blocking A pointer to an optional semaphore which, if |
|
specified, is signaled after the thread finishes |
|
executing the work function |
|
@return PEGASUS_THREAD_OK if the thread is started successfully, |
|
PEGASUS_THREAD_INSUFFICIENT_RESOURCES if the |
|
resources necessary to start the thread are not currently |
|
available. PEGASUS_THREAD_SETUP_FAILURE if the thread |
|
could not be setup properly. PEGASUS_THREAD_UNAVAILABLE |
|
if this service is shutting down and no more threads can |
|
be allocated. |
|
@exception IPCException |
|
*/ |
|
ThreadStatus allocate_and_awaken(void *parm, |
|
ThreadReturnType(PEGASUS_THREAD_CDECL * |
|
work) (void *), |
|
Semaphore * blocking = 0); |
|
|
|
/** |
|
Cleans up idle threads if they have been running longer than the |
|
deallocate_wait configuration and more than the configured |
|
minimum number of threads is running. |
|
@return The number of threads that were cleaned up. |
|
@exception IPCException |
|
*/ |
|
Uint32 cleanupIdleThreads(); |
|
|
|
void get_key(Sint8 * buf, int bufsize); |
|
|
|
inline void setMinThreads(Sint16 min) |
|
{ |
|
_minThreads = min; |
|
} |
|
|
|
inline Sint16 getMinThreads() const |
|
{ |
|
return _minThreads; |
|
} |
|
|
|
inline void setMaxThreads(Sint16 max) |
|
{ |
|
_maxThreads = max; |
|
} |
|
|
|
inline Sint16 getMaxThreads() const |
|
{ |
|
return _maxThreads; |
|
} |
|
|
|
inline Uint32 runningCount() |
|
{ |
|
return _runningThreads.size(); |
|
} |
|
|
|
inline Uint32 idleCount() |
|
{ |
|
return _idleThreads.size(); |
|
} |
|
|
|
private: |
|
|
|
ThreadPool(); // Unimplemented |
|
ThreadPool(const ThreadPool &); // Unimplemented |
|
ThreadPool & operator=(const ThreadPool &); // Unimplemented |
|
|
|
static ThreadReturnType PEGASUS_THREAD_CDECL _loop(void *); |
|
|
|
static Boolean _timeIntervalExpired(struct timeval *start, |
|
struct timeval *interval); |
|
|
|
static void _deleteSemaphore(void *p); |
|
|
|
void _cleanupThread(Thread * thread); |
|
Thread *_initializeThread(); |
|
void _addToIdleThreadsQueue(Thread * th); |
|
|
|
Sint16 _maxThreads; |
|
Sint16 _minThreads; |
|
AtomicInt _currentThreads; |
|
struct timeval _deallocateWait; |
|
char _key[17]; |
|
List < Thread, Mutex > _idleThreads; |
|
List < Thread, Mutex > _runningThreads; |
|
AtomicInt _dying; |
|
}; |
|
|
|
//============================================================================== |
|
// |
|
// POSIX Threads Implementation: |
|
// |
|
//============================================================================== |
|
|
|
#if defined(PEGASUS_HAVE_PTHREADS) |
|
|
|
struct StartWrapperArg |
|
{ |
|
void *(PEGASUS_THREAD_CDECL * start) (void *); |
|
void *arg; |
|
}; | }; |
| |
extern "C" void *_start_wrapper(void *arg); |
|
|
|
inline ThreadStatus Thread::run() |
|
{ |
|
StartWrapperArg *arg = new StartWrapperArg; |
|
arg->start = _start; |
|
arg->arg = this; |
|
|
|
Threads::Type type = _is_detached ? Threads::DETACHED : Threads::JOINABLE; |
|
int rc = Threads::create(_handle.thid, type, _start_wrapper, arg); |
|
|
|
// On Linux distributions released prior 2005, the implementation of |
|
// Native POSIX Thread Library returns ENOMEM instead of EAGAIN when |
|
// there |
|
// are no insufficient memory. Hence we are checking for both. See bug |
|
// 386. |
|
|
|
if ((rc == EAGAIN) || (rc == ENOMEM)) |
|
{ |
|
Threads::clear(_handle.thid); |
|
delete arg; |
|
return PEGASUS_THREAD_INSUFFICIENT_RESOURCES; |
|
} |
|
else if (rc != 0) |
|
{ |
|
Threads::clear(_handle.thid); |
|
delete arg; |
|
return PEGASUS_THREAD_SETUP_FAILURE; |
|
} |
|
return PEGASUS_THREAD_OK; |
|
} |
|
|
|
inline void Thread::cancel() |
|
{ |
|
_cancelled = true; |
|
pthread_cancel(_handle.thid.tt_handle()); |
|
} |
|
|
|
inline void Thread::test_cancel() |
|
{ |
|
#if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM) |
|
pthread_testintr(); |
|
#else |
|
pthread_testcancel(); |
|
#endif |
|
} |
|
|
|
inline Boolean Thread::is_cancelled(void) |
|
{ |
|
return _cancelled; |
|
} |
|
|
|
inline void Thread::thread_switch() |
|
{ |
|
#if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM) |
|
pthread_yield(NULL); |
|
#else |
|
sched_yield(); |
|
#endif |
|
} |
|
|
|
/* |
|
ATTN: why are these missing on other platforms? |
|
*/ |
|
#if defined(PEGASUS_PLATFORM_LINUX_GENERIC_GNU) |
|
inline void Thread::suspend() |
|
{ |
|
pthread_kill(_handle.thid.tt_handle(), SIGSTOP); |
|
} |
|
|
|
inline void Thread::resume() |
|
{ |
|
pthread_kill(_handle.thid.tt_handle(), SIGCONT); |
|
} |
|
#endif |
|
|
|
inline void Thread::sleep(Uint32 msec) |
|
{ |
|
Threads::sleep(msec); |
|
} |
|
|
|
inline void Thread::join(void) |
|
{ |
|
if (!_is_detached && Threads::id(_handle.thid) != 0) |
|
pthread_join(_handle.thid.tt_handle(), &_exit_code); |
|
|
|
Threads::clear(_handle.thid); |
|
} |
|
|
|
inline void Thread::thread_init(void) |
|
{ |
|
#if defined(PEGASUS_PLATFORM_ZOS_ZSERIES_IBM) |
|
pthread_setintr(PTHREAD_INTR_ENABLE); |
|
pthread_setintrtype(PTHREAD_INTR_ASYNCHRONOUS); |
|
#else |
|
pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL); |
|
pthread_setcanceltype(PTHREAD_CANCEL_DEFERRED, NULL); |
|
#endif |
|
_cancel_enabled = true; |
|
} |
|
|
|
inline void Thread::detach(void) |
|
{ |
|
_is_detached = true; |
|
pthread_detach(_handle.thid.tt_handle()); |
|
} |
|
|
|
#endif /* PEGASUS_HAVE_PTHREADS */ |
|
|
|
//============================================================================== |
|
// |
|
// Windows Threads Implementation: |
|
// |
|
//============================================================================== |
|
|
|
#if defined(PEGASUS_HAVE_WINDOWS_THREADS) |
|
|
|
inline ThreadStatus Thread::run(void) |
|
{ |
|
// Note: A Win32 thread ID is not the same thing as a pthread ID. |
|
// Win32 threads have both a thread ID and a handle. The handle |
|
// is used in the wait functions, etc. |
|
// So _handle.thid is actually the thread handle. |
|
|
|
unsigned threadid = 0; |
|
|
|
ThreadType tt; |
|
tt.handle = (HANDLE) _beginthreadex(NULL, 0, _start, this, 0, &threadid); |
|
_handle.thid = tt; |
|
|
|
if (Threads::id(_handle.thid) == 0) |
|
{ |
|
if (errno == EAGAIN) |
|
{ |
|
return PEGASUS_THREAD_INSUFFICIENT_RESOURCES; |
|
} |
|
else |
|
{ |
|
return PEGASUS_THREAD_SETUP_FAILURE; |
|
} |
|
} |
|
return PEGASUS_THREAD_OK; |
|
} |
|
|
|
inline void Thread::cancel(void) |
|
{ |
|
_cancelled = true; |
|
} |
|
|
|
inline void Thread::test_cancel(void) |
|
{ |
|
if (_cancel_enabled && _cancelled) |
|
{ |
|
exit_self(0); |
|
} |
|
} |
|
|
|
inline Boolean Thread::is_cancelled(void) |
|
{ |
|
return _cancelled; |
|
} |
|
|
|
inline void Thread::thread_switch(void) |
|
{ |
|
Sleep(0); |
|
} |
|
|
|
inline void Thread::sleep(Uint32 milliseconds) |
|
{ |
|
Sleep(milliseconds); |
|
} |
|
|
|
inline void Thread::join(void) |
|
{ |
|
if (Threads::id(_handle.thid) != 0) |
|
{ |
|
if (!_is_detached) |
|
{ |
|
if (!_cancelled) |
|
{ |
|
// Emulate the unix join api. Caller sleeps until thread is |
|
// done. |
|
WaitForSingleObject(_handle.thid.handle, INFINITE); |
|
} |
|
else |
|
{ |
|
// Currently this is the only way to ensure this code does |
|
// not |
|
// hang forever. |
|
if (WaitForSingleObject(_handle.thid.handle, 10000) == |
|
WAIT_TIMEOUT) |
|
{ |
|
TerminateThread(_handle.thid.handle, 0); |
|
} |
|
} |
|
|
|
DWORD exit_code = 0; |
|
GetExitCodeThread(_handle.thid.handle, &exit_code); |
|
_exit_code = (ThreadReturnType) exit_code; |
|
} |
|
|
|
CloseHandle(_handle.thid.handle); |
|
Threads::clear(_handle.thid); |
|
} |
|
} |
|
|
|
inline void Thread::thread_init(void) |
|
{ |
|
_cancel_enabled = true; |
|
} |
|
|
|
inline void Thread::detach(void) |
|
{ |
|
_is_detached = true; |
|
} |
|
|
|
#endif /* PEGASUS_HAVE_WINDOWS_THREADS */ |
|
|
|
PEGASUS_NAMESPACE_END | PEGASUS_NAMESPACE_END |
|
|
#endif // Pegasus_Thread_h | #endif // Pegasus_Thread_h |