diff options
author | Steven Bennetts <steve@lindenlab.com> | 2007-03-02 21:25:50 +0000 |
---|---|---|
committer | Steven Bennetts <steve@lindenlab.com> | 2007-03-02 21:25:50 +0000 |
commit | 4dabd9c0472deb49573fdafef2fa413e59703f19 (patch) | |
tree | 06c680d6a2047e03838d6548bccd26c7baf9d652 /indra/llcommon | |
parent | d4462963c6ba5db2088723bbedc7b60f1184c594 (diff) |
merge release@58699 beta-1-14-0@58707 -> release
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/imageids.h | 2 | ||||
-rw-r--r-- | indra/llcommon/llapr.cpp | 174 | ||||
-rw-r--r-- | indra/llcommon/llapr.h | 18 | ||||
-rw-r--r-- | indra/llcommon/llcommon.cpp | 1 | ||||
-rw-r--r-- | indra/llcommon/llcommon.h | 1 | ||||
-rw-r--r-- | indra/llcommon/llerror.cpp | 1 | ||||
-rw-r--r-- | indra/llcommon/llevent.h | 1 | ||||
-rw-r--r-- | indra/llcommon/llfasttimer.h | 25 | ||||
-rw-r--r-- | indra/llcommon/llfile.cpp | 13 | ||||
-rw-r--r-- | indra/llcommon/llfile.h | 1 | ||||
-rw-r--r-- | indra/llcommon/llmemory.cpp | 38 | ||||
-rw-r--r-- | indra/llcommon/llmemory.h | 51 | ||||
-rw-r--r-- | indra/llcommon/llmemtype.h | 1 | ||||
-rw-r--r-- | indra/llcommon/llqueuedthread.cpp | 195 | ||||
-rw-r--r-- | indra/llcommon/llqueuedthread.h | 56 | ||||
-rw-r--r-- | indra/llcommon/llsecondlifeurls.h | 9 | ||||
-rw-r--r-- | indra/llcommon/llstrider.h | 1 | ||||
-rw-r--r-- | indra/llcommon/llthread.cpp | 63 | ||||
-rw-r--r-- | indra/llcommon/llthread.h | 75 | ||||
-rw-r--r-- | indra/llcommon/llworkerthread.cpp | 325 | ||||
-rw-r--r-- | indra/llcommon/llworkerthread.h | 85 |
21 files changed, 724 insertions, 412 deletions
diff --git a/indra/llcommon/imageids.h b/indra/llcommon/imageids.h index 8147183bec..9f57ec84c2 100644 --- a/indra/llcommon/imageids.h +++ b/indra/llcommon/imageids.h @@ -31,7 +31,7 @@ const LLUUID IMG_CLEAR ("11ee27f5-43c0-414e-afd5-d7f5688c351f"); // VIEWER const LLUUID IMG_SMOKE ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER -const LLUUID IMG_DEFAULT ("b4ba225c-373f-446d-9f7e-6cb7b5cf9b3d"); // VIEWER +const LLUUID IMG_DEFAULT ("d2114404-dd59-4a4d-8e6c-49359e91bbf0"); // VIEWER //const LLUUID IMG_SAND ("0ff70ead-4562-45f9-9e8a-52b1a3286868"); // VIEWER 1.5k //const LLUUID IMG_GRASS ("5ab48dd5-05d0-4f1a-ace6-efd4e2fb3508"); // VIEWER 1.2k diff --git a/indra/llcommon/llapr.cpp b/indra/llcommon/llapr.cpp index a7fc6a40a7..bf6a4d1b21 100644 --- a/indra/llcommon/llapr.cpp +++ b/indra/llcommon/llapr.cpp @@ -103,11 +103,13 @@ void ll_apr_assert_status(apr_status_t status) llassert(ll_apr_warn_status(status) == false); } -apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep) +// File I/O +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep, apr_pool_t* pool) { apr_file_t* apr_file; apr_status_t s; - s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, gAPRPoolp); + if (pool == NULL) pool = gAPRPoolp; + s = apr_file_open(&apr_file, filename.c_str(), flags, APR_OS_DEFAULT, pool); if (s != APR_SUCCESS) { if (sizep) @@ -123,6 +125,7 @@ apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* s apr_off_t offset = 0; if (apr_file_seek(apr_file, APR_END, &offset) == APR_SUCCESS) { + llassert_always(offset <= 0x7fffffff); file_size = (S32)offset; offset = 0; apr_file_seek(apr_file, APR_SET, &offset); @@ -132,6 +135,18 @@ apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* s return apr_file; } +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep) +{ + return ll_apr_file_open(filename, flags, sizep, NULL); +} +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, apr_pool_t* pool) +{ + return ll_apr_file_open(filename, flags, NULL, pool); +} +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags) +{ + return ll_apr_file_open(filename, flags, NULL, NULL); +} S32 ll_apr_file_read(apr_file_t* apr_file, void *buf, S32 nbytes) { @@ -143,10 +158,37 @@ S32 ll_apr_file_read(apr_file_t* apr_file, void *buf, S32 nbytes) } else { + llassert_always(sz <= 0x7fffffff); return (S32)sz; } } +S32 ll_apr_file_read_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes) +{ + if (pool == NULL) pool = gAPRPoolp; + apr_file_t* filep = ll_apr_file_open(filename, APR_READ|APR_BINARY, pool); + if (!filep) + { + return 0; + } + S32 off; + if (offset < 0) + off = ll_apr_file_seek(filep, APR_END, 0); + else + off = ll_apr_file_seek(filep, APR_SET, offset); + S32 bytes_read; + if (off < 0) + { + bytes_read = 0; + } + else + { + bytes_read = ll_apr_file_read(filep, buf, nbytes ); + } + apr_file_close(filep); + + return bytes_read; +} S32 ll_apr_file_write(apr_file_t* apr_file, const void *buf, S32 nbytes) { @@ -158,28 +200,73 @@ S32 ll_apr_file_write(apr_file_t* apr_file, const void *buf, S32 nbytes) } else { + llassert_always(sz <= 0x7fffffff); return (S32)sz; } } +S32 ll_apr_file_write_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes) +{ + if (pool == NULL) pool = gAPRPoolp; + apr_int32_t flags = APR_CREATE|APR_WRITE|APR_BINARY; + if (offset < 0) + { + flags |= APR_APPEND; + offset = 0; + } + apr_file_t* filep = ll_apr_file_open(filename, flags, pool); + if (!filep) + { + return 0; + } + if (offset > 0) + { + offset = ll_apr_file_seek(filep, APR_SET, offset); + } + S32 bytes_written; + if (offset < 0) + { + bytes_written = 0; + } + else + { + bytes_written = ll_apr_file_write(filep, buf, nbytes ); + } + apr_file_close(filep); + + return bytes_written; +} + S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset) { - apr_off_t apr_offset = offset; - apr_status_t s = apr_file_seek(apr_file, where, &apr_offset); + apr_status_t s; + apr_off_t apr_offset; + if (offset >= 0) + { + apr_offset = (apr_off_t)offset; + s = apr_file_seek(apr_file, where, &apr_offset); + } + else + { + apr_offset = 0; + s = apr_file_seek(apr_file, APR_END, &apr_offset); + } if (s != APR_SUCCESS) { return -1; } else { + llassert_always(apr_offset <= 0x7fffffff); return (S32)apr_offset; } } -bool ll_apr_file_remove(const LLString& filename) +bool ll_apr_file_remove(const LLString& filename, apr_pool_t* pool) { apr_status_t s; - s = apr_file_remove(filename.c_str(), gAPRPoolp); + if (pool == NULL) pool = gAPRPoolp; + s = apr_file_remove(filename.c_str(), pool); if (s != APR_SUCCESS) { llwarns << "ll_apr_file_remove failed on file: " << filename << llendl; @@ -188,10 +275,11 @@ bool ll_apr_file_remove(const LLString& filename) return true; } -bool ll_apr_file_rename(const LLString& filename, const LLString& newname) +bool ll_apr_file_rename(const LLString& filename, const LLString& newname, apr_pool_t* pool) { apr_status_t s; - s = apr_file_rename(filename.c_str(), newname.c_str(), gAPRPoolp); + if (pool == NULL) pool = gAPRPoolp; + s = apr_file_rename(filename.c_str(), newname.c_str(), pool); if (s != APR_SUCCESS) { llwarns << "ll_apr_file_rename failed on file: " << filename << llendl; @@ -199,3 +287,73 @@ bool ll_apr_file_rename(const LLString& filename, const LLString& newname) } return true; } + +bool ll_apr_file_exists(const LLString& filename, apr_pool_t* pool) +{ + apr_file_t* apr_file; + apr_status_t s; + if (pool == NULL) pool = gAPRPoolp; + s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool); + if (s != APR_SUCCESS || !apr_file) + { + return false; + } + else + { + apr_file_close(apr_file); + return true; + } +} + +S32 ll_apr_file_size(const LLString& filename, apr_pool_t* pool) +{ + apr_file_t* apr_file; + apr_finfo_t info; + apr_status_t s; + if (pool == NULL) pool = gAPRPoolp; + s = apr_file_open(&apr_file, filename.c_str(), APR_READ, APR_OS_DEFAULT, pool); + if (s != APR_SUCCESS || !apr_file) + { + return 0; + } + else + { + apr_status_t s = apr_file_info_get(&info, APR_FINFO_SIZE, apr_file); + apr_file_close(apr_file); + if (s == APR_SUCCESS) + { + return (S32)info.size; + } + else + { + return 0; + } + } +} + +bool ll_apr_dir_make(const LLString& dirname, apr_pool_t* pool) +{ + apr_status_t s; + if (pool == NULL) pool = gAPRPoolp; + s = apr_dir_make(dirname.c_str(), APR_FPROT_OS_DEFAULT, pool); + if (s != APR_SUCCESS) + { + llwarns << "ll_apr_file_remove failed on file: " << dirname << llendl; + return false; + } + return true; +} + +bool ll_apr_dir_remove(const LLString& dirname, apr_pool_t* pool) +{ + apr_status_t s; + if (pool == NULL) pool = gAPRPoolp; + s = apr_file_remove(dirname.c_str(), pool); + if (s != APR_SUCCESS) + { + llwarns << "ll_apr_file_remove failed on file: " << dirname << llendl; + return false; + } + return true; +} + diff --git a/indra/llcommon/llapr.h b/indra/llcommon/llapr.h index 1e9e944eef..e89912279e 100644 --- a/indra/llcommon/llapr.h +++ b/indra/llcommon/llapr.h @@ -106,14 +106,24 @@ typedef LLAtomic32<S32> LLAtomicS32; #define LL_APR_WB (APR_CREATE|APR_TRUNCATE|APR_WRITE|APR_BINARY) // "wb" #define LL_APR_RPB (APR_READ|APR_WRITE|APR_BINARY) // "r+b" #define LL_APR_WPB (APR_CREATE|APR_TRUNCATE|APR_READ|APR_WRITE|APR_BINARY) // "w+b" -apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep = NULL); +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep, apr_pool_t* pool); +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, S32* sizep); +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags, apr_pool_t* pool); +apr_file_t* ll_apr_file_open(const LLString& filename, apr_int32_t flags); // Returns actual offset, -1 if seek fails S32 ll_apr_file_seek(apr_file_t* apr_file, apr_seek_where_t where, S32 offset); -// Returns bytes read/written, 0 if read/write fails +// Returns bytes read/written, 0 if read/write fails: S32 ll_apr_file_read(apr_file_t* apr_file, void* buf, S32 nbytes); +S32 ll_apr_file_read_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes); S32 ll_apr_file_write(apr_file_t* apr_file, const void* buf, S32 nbytes); -bool ll_apr_file_remove(const LLString& filename); -bool ll_apr_file_rename(const LLString& filename, const LLString& newname); +S32 ll_apr_file_write_ex(const LLString& filename, apr_pool_t* pool, void *buf, S32 offset, S32 nbytes); +// returns false if failure: +bool ll_apr_file_remove(const LLString& filename, apr_pool_t* pool = NULL); +bool ll_apr_file_rename(const LLString& filename, const LLString& newname, apr_pool_t* pool = NULL); +bool ll_apr_file_exists(const LLString& filename, apr_pool_t* pool = NULL); +S32 ll_apr_file_size(const LLString& filename, apr_pool_t* pool = NULL); +bool ll_apr_dir_make(const LLString& dirname, apr_pool_t* pool = NULL); +bool ll_apr_dir_remove(const LLString& dirname, apr_pool_t* pool = NULL); /** * @brief Function which approprately logs error or remains quiet on diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index ff810abfa9..52fcaa3c19 100644 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -8,6 +8,7 @@ #include "linden_common.h" #include "llcommon.h" +#include "llthread.h" //static BOOL LLCommon::sAprInitialized = FALSE; diff --git a/indra/llcommon/llcommon.h b/indra/llcommon/llcommon.h index c37d479ba9..e7fb5b84e4 100644 --- a/indra/llcommon/llcommon.h +++ b/indra/llcommon/llcommon.h @@ -12,7 +12,6 @@ #include "llapr.h" // #include "llframecallbackmanager.h" #include "lltimer.h" -#include "llworkerthread.h" #include "llfile.h" class LLCommon diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index 1ed25a0d17..bbff363f54 100644 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -12,6 +12,7 @@ #include "llerror.h" #include "llerrorcontrol.h" +#include "llapp.h" #include "llapr.h" extern apr_thread_mutex_t *gLogMutexp; #include "llfile.h" diff --git a/indra/llcommon/llevent.h b/indra/llcommon/llevent.h index 283f40f923..a3f6b0e942 100644 --- a/indra/llcommon/llevent.h +++ b/indra/llcommon/llevent.h @@ -12,6 +12,7 @@ #include "llsd.h" #include "llmemory.h" +#include "llthread.h" class LLEventListener; class LLEvent; diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 2fde728eaf..1a7e60656c 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -37,13 +37,22 @@ public: FTM_UPDATE_TERRAIN, FTM_UPDATE_PRIMITIVES, FTM_UPDATE_PARTICLES, + FTM_SIMULATE_PARTICLES, FTM_UPDATE_SKY, FTM_UPDATE_TEXTURES, + FTM_UPDATE_WATER, + FTM_UPDATE_CLOUDS, + FTM_UPDATE_GRASS, + FTM_UPDATE_TREE, + FTM_UPDATE_AVATAR, // common render components FTM_RENDER_GEOMETRY, FTM_RENDER_TERRAIN, FTM_RENDER_SIMPLE, + FTM_RENDER_FULLBRIGHT, + FTM_RENDER_GRASS, + FTM_RENDER_INVISIBLE, FTM_RENDER_SHINY, FTM_RENDER_BUMP, FTM_RENDER_TREES, @@ -62,6 +71,20 @@ public: FTM_MESSAGES, FTM_REBUILD, FTM_STATESORT, + FTM_STATESORT_DRAWABLE, + FTM_STATESORT_POSTSORT, + FTM_REBUILD_VBO, + FTM_REBUILD_VOLUME_VB, + FTM_REBUILD_BRIDGE_VB, + FTM_REBUILD_HUD_VB, + FTM_REBUILD_TERRAIN_VB, + FTM_REBUILD_WATER_VB, + FTM_REBUILD_TREE_VB, + FTM_REBUILD_PARTICLE_VB, + FTM_REBUILD_CLOUD_VB, + FTM_REBUILD_GRASS_VB, + FTM_REBUILD_NONE_VB, + FTM_REBUILD_OCCLUSION_VB, FTM_POOLS, FTM_POOLRENDER, FTM_IDLE_CB, @@ -71,6 +94,7 @@ public: FTM_UPDATE_LIGHTS, FTM_CULL, FTM_CULL_REBOUND, + FTM_FRUSTUM_CULL, FTM_GEO_UPDATE, FTM_GEO_RESERVE, FTM_GEO_LIGHT, @@ -97,6 +121,7 @@ public: FTM_IMAGE_UPDATE, FTM_IMAGE_CREATE, FTM_IMAGE_DECODE, + FTM_IMAGE_MARK_DIRTY, FTM_PIPELINE, FTM_VFILE_WAIT, FTM_FLEXIBLE_UPDATE, diff --git a/indra/llcommon/llfile.cpp b/indra/llcommon/llfile.cpp index 4acd94f943..f43e57f467 100644 --- a/indra/llcommon/llfile.cpp +++ b/indra/llcommon/llfile.cpp @@ -29,6 +29,19 @@ int LLFile::mkdir(const char* dirname, int perms) } // static +int LLFile::rmdir(const char* dirname) +{ +#if LL_WINDOWS + // permissions are ignored on Windows + std::string utf8dirname = dirname; + llutf16string utf16dirname = utf8str_to_utf16str(utf8dirname); + return _wrmdir(utf16dirname.c_str()); +#else + return ::rmdir(dirname); +#endif +} + +// static LLFILE* LLFile::fopen(const char* filename, const char* mode) /* Flawfinder: ignore */ { #if LL_WINDOWS diff --git a/indra/llcommon/llfile.h b/indra/llcommon/llfile.h index 2899f51a60..605918cd29 100644 --- a/indra/llcommon/llfile.h +++ b/indra/llcommon/llfile.h @@ -50,6 +50,7 @@ public: // be overridden by the user's umask. It is ignored on Windows. static int mkdir(const char* filename, int perms = 0700); + static int rmdir(const char* filename); static int remove(const char* filename); static int rename(const char* filename,const char* newname); static int stat(const char* filename,llstat* file_status); diff --git a/indra/llcommon/llmemory.cpp b/indra/llcommon/llmemory.cpp index 0a21c3a2a7..1063de5a8c 100644 --- a/indra/llcommon/llmemory.cpp +++ b/indra/llcommon/llmemory.cpp @@ -9,6 +9,7 @@ #include "linden_common.h" #include "llmemory.h" +#include "llmemtype.h" // not defining nullfunc will currently crash when trying to use a LLHandle template< typename _Ty > @@ -244,43 +245,6 @@ void operator delete[] (void *p) //---------------------------------------------------------------------------- -//static -LLMutex* LLThreadSafeRefCount::sMutex = 0; - -//static -void LLThreadSafeRefCount::initClass() -{ - if (!sMutex) - { - sMutex = new LLMutex(0); - } -} - -//static -void LLThreadSafeRefCount::cleanupClass() -{ - delete sMutex; - sMutex = NULL; -} - - -//---------------------------------------------------------------------------- - -LLThreadSafeRefCount::LLThreadSafeRefCount() : - mRef(0) -{ -} - -LLThreadSafeRefCount::~LLThreadSafeRefCount() -{ - if (mRef != 0) - { - llerrs << "deleting non-zero reference" << llendl; - } -} - -//---------------------------------------------------------------------------- - LLRefCount::LLRefCount() : mRef(0) { diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 962a4aa5d5..d543d023ff 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -12,8 +12,6 @@ #include <cstdlib> #include "llerror.h" -#include "llthread.h" -#include "llmemtype.h" extern S32 gTotalDAlloc; extern S32 gTotalDAUse; @@ -42,53 +40,7 @@ private: // LLPointer<LLFoo> x = new LLFoo; // constructor does not do anything interesting // x->instantiate(); // does stuff like place x into an update queue -class LLThreadSafeRefCount -{ -public: - static void initClass(); // creates sMutex - static void cleanupClass(); // destroys sMutex - -private: - static LLMutex* sMutex; - -private: - LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented - LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented - -protected: - virtual ~LLThreadSafeRefCount(); // use unref() - -public: - LLThreadSafeRefCount(); - - void ref() - { - if (sMutex) sMutex->lock(); - mRef++; - if (sMutex) sMutex->unlock(); - } - - S32 unref() - { - llassert(mRef >= 1); - if (sMutex) sMutex->lock(); - S32 res = --mRef; - if (sMutex) sMutex->unlock(); - if (0 == res) - { - delete this; - res = 0; - } - return res; - } - S32 getNumRefs() const - { - return mRef; - } - -private: - S32 mRef; -}; +// see llthread.h for LLThreadSafeRefCount //---------------------------------------------------------------------------- @@ -132,6 +84,7 @@ private: //---------------------------------------------------------------------------- +// Note: relies on Type having ref() and unref() methods template <class Type> class LLPointer { public: diff --git a/indra/llcommon/llmemtype.h b/indra/llcommon/llmemtype.h index 17afaa6a8a..53f7f66285 100644 --- a/indra/llcommon/llmemtype.h +++ b/indra/llcommon/llmemtype.h @@ -52,6 +52,7 @@ public: MTYPE_DRAWABLE, MTYPE_OBJECT, + MTYPE_VERTEX_DATA, MTYPE_SPACE_PARTITION, MTYPE_PIPELINE, MTYPE_AVATAR, diff --git a/indra/llcommon/llqueuedthread.cpp b/indra/llcommon/llqueuedthread.cpp index bc41927d38..e6ac9ac11d 100644 --- a/indra/llcommon/llqueuedthread.cpp +++ b/indra/llcommon/llqueuedthread.cpp @@ -12,10 +12,9 @@ //============================================================================ // MAIN THREAD -LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runalways) : +LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded) : LLThread(name), mThreaded(threaded), - mRunAlways(runalways), mIdleThread(TRUE), mNextHandle(0) { @@ -28,6 +27,12 @@ LLQueuedThread::LLQueuedThread(const std::string& name, bool threaded, bool runa // MAIN THREAD LLQueuedThread::~LLQueuedThread() { + shutdown(); + // ~LLThread() will be called here +} + +void LLQueuedThread::shutdown() +{ setQuitting(); unpause(); // MAIN THREAD @@ -54,61 +59,69 @@ LLQueuedThread::~LLQueuedThread() } QueuedRequest* req; + S32 active_count = 0; while ( (req = (QueuedRequest*)mRequestHash.pop_element()) ) { + if (req->getStatus() == STATUS_QUEUED || req->getStatus() == STATUS_INPROGRESS) + { + ++active_count; + } req->deleteRequest(); } - - // ~LLThread() will be called here + if (active_count) + { + llwarns << "~LLQueuedThread() called with active requests: " << active_count << llendl; + } } //---------------------------------------------------------------------------- // MAIN THREAD -void LLQueuedThread::update(U32 ms_elapsed) +// virtual +S32 LLQueuedThread::update(U32 max_time_ms) { - updateQueue(0); + return updateQueue(max_time_ms); } -void LLQueuedThread::updateQueue(S32 inc) +S32 LLQueuedThread::updateQueue(U32 max_time_ms) { - // If mRunAlways == TRUE, unpause the thread whenever we put something into the queue. - // If mRunAlways == FALSE, we only unpause the thread when updateQueue() is called from the main loop (i.e. between rendered frames) - - if (inc == 0) // Frame Update + F64 max_time = (F64)max_time_ms * .001; + LLTimer timer; + S32 pending = 1; + + // Frame Update + if (mThreaded) { - if (mThreaded) - { - unpause(); - wake(); // Wake the thread up if necessary. - } - else + pending = getPending(); + unpause(); + } + else + { + while (pending > 0) { - while (processNextRequest() > 0) - ; + pending = processNextRequest(); + if (max_time && timer.getElapsedTimeF64() > max_time) + break; } } - else + return pending; +} + +void LLQueuedThread::incQueue() +{ + // Something has been added to the queue + if (!isPaused()) { - // Something has been added to the queue - if (mRunAlways) + if (mThreaded) { - if (mThreaded) - { - wake(); // Wake the thread up if necessary. - } - else - { - while(processNextRequest() > 0) - ; - } + wake(); // Wake the thread up if necessary. } } } //virtual // May be called from any thread -S32 LLQueuedThread::getPending(bool child_thread) +S32 LLQueuedThread::getPending() { S32 res; lockData(); @@ -122,7 +135,7 @@ void LLQueuedThread::waitOnPending() { while(1) { - updateQueue(0); + update(0); if (mIdleThread) { @@ -181,7 +194,7 @@ bool LLQueuedThread::addRequest(QueuedRequest* req) #endif unlockData(); - updateQueue(1); + incQueue(); return true; } @@ -195,7 +208,7 @@ bool LLQueuedThread::waitForResult(LLQueuedThread::handle_t handle, bool auto_co bool done = false; while(!done) { - updateQueue(0); // unpauses + update(0); // unpauses lockData(); QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); if (!req) @@ -253,51 +266,47 @@ LLQueuedThread::status_t LLQueuedThread::getRequestStatus(handle_t handle) return res; } -LLQueuedThread::status_t LLQueuedThread::abortRequest(handle_t handle, U32 flags) +void LLQueuedThread::abortRequest(handle_t handle, bool autocomplete) { - status_t res = STATUS_EXPIRED; lockData(); QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); if (req) { - res = req->abortRequest(flags); - if ((flags & AUTO_COMPLETE) && (res == STATUS_COMPLETE)) - { - mRequestHash.erase(handle); - req->deleteRequest(); -// check(); - } -#if _DEBUG -// llinfos << llformat("LLQueuedThread::Aborted req [%08d]",handle) << llendl; -#endif + req->setFlags(FLAG_ABORT | (autocomplete ? FLAG_AUTO_COMPLETE : 0)); } unlockData(); - return res; } // MAIN thread -LLQueuedThread::status_t LLQueuedThread::setFlags(handle_t handle, U32 flags) +void LLQueuedThread::setFlags(handle_t handle, U32 flags) { - status_t res = STATUS_EXPIRED; lockData(); QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); if (req) { - res = req->setFlags(flags); + req->setFlags(flags); } unlockData(); - return res; } void LLQueuedThread::setPriority(handle_t handle, U32 priority) { lockData(); QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); - if (req && (req->getStatus() == STATUS_QUEUED)) + if (req) { - llverify(mRequestQueue.erase(req) == 1); - req->setPriority(priority); - mRequestQueue.insert(req); + if(req->getStatus() == STATUS_INPROGRESS) + { + // not in list + req->setPriority(priority); + } + else if(req->getStatus() == STATUS_QUEUED) + { + // remove from list then re-insert + llverify(mRequestQueue.erase(req) == 1); + req->setPriority(priority); + mRequestQueue.insert(req); + } } unlockData(); } @@ -309,9 +318,10 @@ bool LLQueuedThread::completeRequest(handle_t handle) QueuedRequest* req = (QueuedRequest*)mRequestHash.find(handle); if (req) { - llassert(req->getStatus() != STATUS_QUEUED && req->getStatus() != STATUS_ABORT); + llassert_always(req->getStatus() != STATUS_QUEUED); + llassert_always(req->getStatus() != STATUS_INPROGRESS); #if _DEBUG -// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl; +// llinfos << llformat("LLQueuedThread::Completed req [%08d]",handle) << llendl; #endif mRequestHash.erase(handle); req->deleteRequest(); @@ -345,28 +355,34 @@ bool LLQueuedThread::check() //============================================================================ // Runs on its OWN thread -int LLQueuedThread::processNextRequest() +S32 LLQueuedThread::processNextRequest() { - QueuedRequest *req = 0; + QueuedRequest *req; // Get next request from pool lockData(); while(1) { - if (!mRequestQueue.empty()) + req = NULL; + if (mRequestQueue.empty()) { - req = *mRequestQueue.begin(); - mRequestQueue.erase(mRequestQueue.begin()); + break; } - if (req && req->getStatus() == STATUS_ABORT) + req = *mRequestQueue.begin(); + mRequestQueue.erase(mRequestQueue.begin()); + if ((req->getFlags() & FLAG_ABORT) || (mStatus == QUITTING)) { req->setStatus(STATUS_ABORTED); - req = 0; - } - else - { - llassert (!req || req->getStatus() == STATUS_QUEUED) - break; + req->finishRequest(false); + if (req->getFlags() & FLAG_AUTO_COMPLETE) + { + mRequestHash.erase(req); + req->deleteRequest(); +// check(); + } + continue; } + llassert_always(req->getStatus() == STATUS_QUEUED); + break; } if (req) { @@ -374,22 +390,22 @@ int LLQueuedThread::processNextRequest() } unlockData(); - // This is the only place we will cal req->setStatus() after + // This is the only place we will call req->setStatus() after // it has initially been seet to STATUS_QUEUED, so it is // safe to access req. if (req) { // process request - bool complete = processRequest(req); + bool complete = req->processRequest(); if (complete) { lockData(); req->setStatus(STATUS_COMPLETE); - req->finishRequest(); - if (req->getFlags() & AUTO_COMPLETE) + req->finishRequest(true); + if (req->getFlags() & FLAG_AUTO_COMPLETE) { - llverify(mRequestHash.erase(req)) + mRequestHash.erase(req); req->deleteRequest(); // check(); } @@ -400,12 +416,18 @@ int LLQueuedThread::processNextRequest() lockData(); req->setStatus(STATUS_QUEUED); mRequestQueue.insert(req); + U32 priority = req->getPriority(); unlockData(); + if (priority < PRIORITY_NORMAL) + { + ms_sleep(1); // sleep the thread a little + } } } - int res; - if (getPending(true) == 0) + S32 res; + S32 pending = getPending(); + if (pending == 0) { if (isQuitting()) { @@ -418,7 +440,7 @@ int LLQueuedThread::processNextRequest() } else { - res = 1; + res = pending; } return res; } @@ -426,13 +448,14 @@ int LLQueuedThread::processNextRequest() bool LLQueuedThread::runCondition() { // mRunCondition must be locked here - return (mRequestQueue.empty() && mIdleThread) ? FALSE : TRUE; + if (mRequestQueue.empty() && mIdleThread) + return false; + else + return true; } void LLQueuedThread::run() { - llinfos << "QUEUED THREAD STARTING" << llendl; - while (1) { // this will block on the condition until runCondition() returns true, the thread is unpaused, or the thread leaves the RUNNING state. @@ -455,6 +478,8 @@ void LLQueuedThread::run() { break; } + + //LLThread::yield(); // thread should yield after each request } llinfos << "QUEUED THREAD " << mName << " EXITING." << llendl; @@ -472,20 +497,18 @@ LLQueuedThread::QueuedRequest::QueuedRequest(LLQueuedThread::handle_t handle, U3 LLQueuedThread::QueuedRequest::~QueuedRequest() { - if (mStatus != STATUS_DELETE) - { - llerrs << "Attemt to directly delete a LLQueuedThread::QueuedRequest; use deleteRequest()" << llendl; - } + llassert_always(mStatus == STATUS_DELETE); } //virtual -void LLQueuedThread::QueuedRequest::finishRequest() +void LLQueuedThread::QueuedRequest::finishRequest(bool completed) { } //virtual void LLQueuedThread::QueuedRequest::deleteRequest() { + llassert_always(mStatus != STATUS_INPROGRESS); setStatus(STATUS_DELETE); delete this; } diff --git a/indra/llcommon/llqueuedthread.h b/indra/llcommon/llqueuedthread.h index beff473f52..8b6a658333 100644 --- a/indra/llcommon/llqueuedthread.h +++ b/indra/llcommon/llqueuedthread.h @@ -33,7 +33,8 @@ public: PRIORITY_HIGH = 0x30000000, PRIORITY_NORMAL = 0x20000000, PRIORITY_LOW = 0x10000000, - PRIORITY_LOWBITS = 0x0FFFFFFF + PRIORITY_LOWBITS = 0x0FFFFFFF, + PRIORITY_HIGHBITS = 0x70000000 }; enum status_t { STATUS_EXPIRED = -1, @@ -41,13 +42,13 @@ public: STATUS_QUEUED = 1, STATUS_INPROGRESS = 2, STATUS_COMPLETE = 3, - STATUS_ABORT = 4, - STATUS_ABORTED = 5, - STATUS_DELETE = 6 + STATUS_ABORTED = 4, + STATUS_DELETE = 5 }; enum flags_t { - AUTO_COMPLETE = 1, - AUTO_DELETE = 2 // child-class dependent + FLAG_AUTO_COMPLETE = 1, + FLAG_AUTO_DELETE = 2, // child-class dependent + FLAG_ABORT = 4 }; typedef U32 handle_t; @@ -60,7 +61,7 @@ public: friend class LLQueuedThread; protected: - ~QueuedRequest(); // use deleteRequest() + virtual ~QueuedRequest(); // use deleteRequest() public: QueuedRequest(handle_t handle, U32 priority, U32 flags = 0); @@ -92,26 +93,14 @@ public: mStatus = newstatus; return oldstatus; } - status_t abortRequest(U32 flags) + void setFlags(U32 flags) { // NOTE: flags are |'d - if (mStatus == STATUS_QUEUED) - { - setStatus(STATUS_ABORT); - } mFlags |= flags; - status_t status = mStatus; - return status; - } - status_t setFlags(U32 flags) - { - // NOTE: flags are |'d - mFlags |= flags; - status_t status = mStatus; - return status; } - virtual void finishRequest(); // Always called when after has been processed + virtual bool processRequest() = 0; // Return true when request has completed + virtual void finishRequest(bool completed); // Always called from thread after request has completed or aborted virtual void deleteRequest(); // Only method to delete a request void setPriority(U32 pri) @@ -141,9 +130,10 @@ public: static handle_t nullHandle() { return handle_t(0); } public: - LLQueuedThread(const std::string& name, bool threaded = TRUE, bool runalways = TRUE); + LLQueuedThread(const std::string& name, bool threaded = true); virtual ~LLQueuedThread(); - + virtual void shutdown(); + private: // No copy constructor or copy assignment LLQueuedThread(const LLQueuedThread&); @@ -155,26 +145,25 @@ private: protected: handle_t generateHandle(); bool addRequest(QueuedRequest* req); - int processNextRequest(void); + S32 processNextRequest(void); + void incQueue(); - virtual bool processRequest(QueuedRequest* req) = 0; - public: bool waitForResult(handle_t handle, bool auto_complete = true); - void update(U32 ms_elapsed); - void updateQueue(S32 inc); + virtual S32 update(U32 max_time_ms); + S32 updateQueue(U32 max_time_ms); + void waitOnPending(); void printQueueStats(); - S32 getPending(bool child_thread = false); + S32 getPending(); bool getThreaded() { return mThreaded ? true : false; } - bool getRunAlways() { return mRunAlways ? true : false; } // Request accessors status_t getRequestStatus(handle_t handle); - status_t abortRequest(handle_t handle, U32 flags = 0); - status_t setFlags(handle_t handle, U32 flags); + void abortRequest(handle_t handle, bool autocomplete); + void setFlags(handle_t handle, U32 flags); void setPriority(handle_t handle, U32 priority); bool completeRequest(handle_t handle); // This is public for support classes like LLWorkerThread, @@ -186,7 +175,6 @@ public: protected: BOOL mThreaded; // if false, run on main thread and do updates during update() - BOOL mRunAlways; // if false, only wake the threads when updateClass() is called LLAtomic32<BOOL> mIdleThread; // request queue is empty (or we are quitting) and the thread is idle typedef std::set<QueuedRequest*, queued_request_less> request_queue_t; diff --git a/indra/llcommon/llsecondlifeurls.h b/indra/llcommon/llsecondlifeurls.h index b07c18eb72..cbea60034f 100644 --- a/indra/llcommon/llsecondlifeurls.h +++ b/indra/llcommon/llsecondlifeurls.h @@ -31,18 +31,9 @@ extern const char UPGRADE_TO_PREMIUM_URL[]; // How to get DirectX 9 extern const char DIRECTX_9_URL[]; -// On AMD with bad AGP controller -extern const char AMD_AGP_URL[]; - // Out of date VIA chipset extern const char VIA_URL[]; -// Out of date intel chipset driver -extern const char INTEL_CHIPSET_URL[]; - -// Out of date SiS chipset driver -extern const char SIS_CHIPSET_URL[]; - // Linden Blogs page extern const char BLOGS_URL[]; diff --git a/indra/llcommon/llstrider.h b/indra/llcommon/llstrider.h index 0688e43940..c5fbf1282a 100644 --- a/indra/llcommon/llstrider.h +++ b/indra/llcommon/llstrider.h @@ -32,6 +32,7 @@ public: Object* operator->() { return mObjectp; } Object& operator *() { return *mObjectp; } Object* operator ++(int) { Object* old = mObjectp; mBytep += mSkip; return old; } + Object* operator +=(int i) { mBytep += mSkip*i; return mObjectp; } Object& operator[](U32 index) { return *(Object*)(mBytep + (mSkip * index)); } }; diff --git a/indra/llcommon/llthread.cpp b/indra/llcommon/llthread.cpp index bd2dd7c8f5..a92d553148 100644 --- a/indra/llcommon/llthread.cpp +++ b/indra/llcommon/llthread.cpp @@ -81,6 +81,11 @@ LLThread::LLThread(const std::string& name, apr_pool_t *poolp) : LLThread::~LLThread() { + shutdown(); +} + +void LLThread::shutdown() +{ // Warning! If you somehow call the thread destructor from itself, // the thread will die in an unclean fashion! if (mAPRThreadp) @@ -186,18 +191,6 @@ void LLThread::checkPause() //============================================================================ -bool LLThread::isQuitting() const -{ - return (QUITTING == mStatus); -} - - -bool LLThread::isStopped() const -{ - return (STOPPED == mStatus); -} - - void LLThread::setQuitting() { mRunCondition->lock(); @@ -328,3 +321,49 @@ void LLCondition::broadcast() apr_thread_cond_broadcast(mAPRCondp); } +//============================================================================ + +//---------------------------------------------------------------------------- + +//static +LLMutex* LLThreadSafeRefCount::sMutex = 0; + +//static +void LLThreadSafeRefCount::initClass() +{ + if (!sMutex) + { + sMutex = new LLMutex(0); + } +} + +//static +void LLThreadSafeRefCount::cleanupClass() +{ + delete sMutex; + sMutex = NULL; +} + + +//---------------------------------------------------------------------------- + +LLThreadSafeRefCount::LLThreadSafeRefCount() : + mRef(0) +{ +} + +LLThreadSafeRefCount::~LLThreadSafeRefCount() +{ + if (mRef != 0) + { + llerrs << "deleting non-zero reference" << llendl; + } +} + +//============================================================================ + +LLResponder::~LLResponder() +{ +} + +//============================================================================ diff --git a/indra/llcommon/llthread.h b/indra/llcommon/llthread.h index f6f6bd210a..ce5daa938c 100644 --- a/indra/llcommon/llthread.h +++ b/indra/llcommon/llthread.h @@ -11,6 +11,7 @@ #include "llapr.h" #include "llapp.h" +#include "llmemory.h" #include "apr-1/apr_thread_cond.h" @@ -30,19 +31,20 @@ public: LLThread(const std::string& name, apr_pool_t *poolp = NULL); virtual ~LLThread(); // Warning! You almost NEVER want to destroy a thread unless it's in the STOPPED state. - + virtual void shutdown(); // stops the thread + static void yield(); // Static because it can be called by the main thread, which doesn't have an LLThread data structure. - bool isQuitting() const; - bool isStopped() const; + bool isQuitting() const { return (QUITTING == mStatus); } + bool isStopped() const { return (STOPPED == mStatus); } // PAUSE / RESUME functionality. See source code for important usage notes. public: // Called from MAIN THREAD. void pause(); void unpause(); - bool isPaused() { return mPaused ? true : false; } + bool isPaused() { return isStopped() || mPaused == TRUE; } // Cause the thread to wake up and check its condition void wake(); @@ -60,7 +62,7 @@ public: private: BOOL mPaused; - + // static function passed to APR thread creation routine static void *APR_THREAD_FUNC staticRun(apr_thread_t *apr_threadp, void *datap); @@ -161,4 +163,67 @@ void LLThread::unlockData() //============================================================================ +// see llmemory.h for LLPointer<> definition + +class LLThreadSafeRefCount +{ +public: + static void initClass(); // creates sMutex + static void cleanupClass(); // destroys sMutex + +private: + static LLMutex* sMutex; + +private: + LLThreadSafeRefCount(const LLThreadSafeRefCount&); // not implemented + LLThreadSafeRefCount&operator=(const LLThreadSafeRefCount&); // not implemented + +protected: + virtual ~LLThreadSafeRefCount(); // use unref() + +public: + LLThreadSafeRefCount(); + + void ref() + { + if (sMutex) sMutex->lock(); + mRef++; + if (sMutex) sMutex->unlock(); + } + + S32 unref() + { + llassert(mRef >= 1); + if (sMutex) sMutex->lock(); + S32 res = --mRef; + if (sMutex) sMutex->unlock(); + if (0 == res) + { + delete this; + res = 0; + } + return res; + } + S32 getNumRefs() const + { + return mRef; + } + +private: + S32 mRef; +}; + +//============================================================================ + +// Simple responder for self destructing callbacks +// Pure virtual class +class LLResponder : public LLThreadSafeRefCount +{ +public: + virtual ~LLResponder(); + virtual void completed(bool success) = 0; +}; + +//============================================================================ + #endif // LL_LLTHREAD_H diff --git a/indra/llcommon/llworkerthread.cpp b/indra/llcommon/llworkerthread.cpp index a9370c8f6d..31f5c1cfcd 100644 --- a/indra/llcommon/llworkerthread.cpp +++ b/indra/llcommon/llworkerthread.cpp @@ -14,98 +14,86 @@ #endif //============================================================================ - -/*static*/ LLWorkerThread* LLWorkerThread::sLocal = NULL; -/*static*/ std::set<LLWorkerThread*> LLWorkerThread::sThreadList; - -//============================================================================ // Run on MAIN thread -//static -void LLWorkerThread::initClass(bool local_is_threaded, bool local_run_always) +LLWorkerThread::LLWorkerThread(const std::string& name, bool threaded) : + LLQueuedThread(name, threaded), + mWorkerAPRPoolp(NULL) { - if (!sLocal) - { - sLocal = new LLWorkerThread(local_is_threaded, local_run_always); - } + apr_pool_create(&mWorkerAPRPoolp, NULL); + mDeleteMutex = new LLMutex(getAPRPool()); } -//static -void LLWorkerThread::cleanupClass() +LLWorkerThread::~LLWorkerThread() { - if (sLocal) + // Delete any workers in the delete queue (should be safe - had better be!) + if (!mDeleteList.empty()) { - while (sLocal->getPending()) - { - sLocal->update(0); - } - delete sLocal; - sLocal = NULL; - llassert(sThreadList.size() == 0); + llwarns << "Worker Thread: " << mName << " destroyed with " << mDeleteList.size() + << " entries in delete list." << llendl; } -} -//static -S32 LLWorkerThread::updateClass(U32 ms_elapsed) -{ - for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) - { - (*iter)->update(ms_elapsed); - } - return getAllPending(); + delete mDeleteMutex; + + // ~LLQueuedThread() will be called here } -//static -S32 LLWorkerThread::getAllPending() +// virtual +S32 LLWorkerThread::update(U32 max_time_ms) { - S32 res = 0; - for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) + S32 res = LLQueuedThread::update(max_time_ms); + // Delete scheduled workers + std::vector<LLWorkerClass*> delete_list; + std::vector<LLWorkerClass*> abort_list; + mDeleteMutex->lock(); + for (delete_list_t::iterator iter = mDeleteList.begin(); + iter != mDeleteList.end(); ) { - res += (*iter)->getPending(); + delete_list_t::iterator curiter = iter++; + LLWorkerClass* worker = *curiter; + if (worker->deleteOK()) + { + if (worker->getFlags(LLWorkerClass::WCF_WORK_FINISHED)) + { + delete_list.push_back(worker); + mDeleteList.erase(curiter); + } + else if (!worker->getFlags(LLWorkerClass::WCF_ABORT_REQUESTED)) + { + abort_list.push_back(worker); + } + } } - return res; -} - -//static -void LLWorkerThread::pauseAll() -{ - for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) + mDeleteMutex->unlock(); + // abort and delete after releasing mutex + for (std::vector<LLWorkerClass*>::iterator iter = abort_list.begin(); + iter != abort_list.end(); ++iter) { - (*iter)->pause(); + (*iter)->abortWork(false); } -} - -//static -void LLWorkerThread::waitOnAllPending() -{ - for (std::set<LLWorkerThread*>::iterator iter = sThreadList.begin(); iter != sThreadList.end(); iter++) + for (std::vector<LLWorkerClass*>::iterator iter = delete_list.begin(); + iter != delete_list.end(); ++iter) { - (*iter)->waitOnPending(); + LLWorkerClass* worker = *iter; + if (worker->mRequestHandle) + { + // Finished but not completed + completeRequest(worker->mRequestHandle); + worker->mRequestHandle = LLWorkerThread::nullHandle(); + worker->clearFlags(LLWorkerClass::WCF_HAVE_WORK); + } + delete *iter; } + return res; } //---------------------------------------------------------------------------- -LLWorkerThread::LLWorkerThread(bool threaded, bool runalways) : - LLQueuedThread("Worker", threaded, runalways) -{ - sThreadList.insert(this); -} - -LLWorkerThread::~LLWorkerThread() -{ - llverify(sThreadList.erase(this) == 1); - // ~LLQueuedThread() will be called here -} - -//---------------------------------------------------------------------------- - - -LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 param, U32 priority) +LLWorkerThread::handle_t LLWorkerThread::addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority) { handle_t handle = generateHandle(); - Request* req = new Request(handle, priority, workerclass, param); + WorkRequest* req = new WorkRequest(handle, priority, workerclass, param); bool res = addRequest(req); if (!res) @@ -118,63 +106,80 @@ LLWorkerThread::handle_t LLWorkerThread::add(LLWorkerClass* workerclass, S32 par return handle; } -//============================================================================ -// Runs on its OWN thread - -bool LLWorkerThread::processRequest(QueuedRequest* qreq) +void LLWorkerThread::deleteWorker(LLWorkerClass* workerclass) { - Request *req = (Request*)qreq; - - req->getWorkerClass()->setWorking(true); - - bool complete = req->getWorkerClass()->doWork(req->getParam()); - - req->getWorkerClass()->setWorking(false); - - LLThread::yield(); // worker thread should yield after each request - - return complete; + mDeleteMutex->lock(); + mDeleteList.push_back(workerclass); + mDeleteMutex->unlock(); } //============================================================================ +// Runs on its OWN thread -LLWorkerThread::Request::Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) : +LLWorkerThread::WorkRequest::WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param) : LLQueuedThread::QueuedRequest(handle, priority), mWorkerClass(workerclass), mParam(param) { } -void LLWorkerThread::Request::deleteRequest() +LLWorkerThread::WorkRequest::~WorkRequest() +{ +} + +// virtual (required for access by LLWorkerThread) +void LLWorkerThread::WorkRequest::deleteRequest() { LLQueuedThread::QueuedRequest::deleteRequest(); } +// virtual +bool LLWorkerThread::WorkRequest::processRequest() +{ + LLWorkerClass* workerclass = getWorkerClass(); + workerclass->setWorking(true); + bool complete = workerclass->doWork(getParam()); + workerclass->setWorking(false); + return complete; +} + +// virtual +void LLWorkerThread::WorkRequest::finishRequest(bool completed) +{ + LLWorkerClass* workerclass = getWorkerClass(); + workerclass->finishWork(getParam(), completed); + U32 flags = LLWorkerClass::WCF_WORK_FINISHED | (completed ? 0 : LLWorkerClass::WCF_WORK_ABORTED); + workerclass->setFlags(flags); +} + //============================================================================ // LLWorkerClass:: operates in main thread LLWorkerClass::LLWorkerClass(LLWorkerThread* workerthread, const std::string& name) : mWorkerThread(workerthread), mWorkerClassName(name), - mWorkHandle(LLWorkerThread::nullHandle()), + mRequestHandle(LLWorkerThread::nullHandle()), + mMutex(workerthread->getWorkerAPRPool()), mWorkFlags(0) { if (!mWorkerThread) { - mWorkerThread = LLWorkerThread::sLocal; + llerrs << "LLWorkerClass() called with NULL workerthread: " << name << llendl; } } + LLWorkerClass::~LLWorkerClass() { - if (mWorkHandle != LLWorkerThread::nullHandle()) + llassert_always(!(mWorkFlags & WCF_WORKING)); + llassert_always(mWorkFlags & WCF_DELETE_REQUESTED); + if (mRequestHandle != LLWorkerThread::nullHandle()) { - LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle); + LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle); if (!workreq) { llerrs << "LLWorkerClass destroyed with stale work handle" << llendl; } - if (workreq->getStatus() != LLWorkerThread::STATUS_ABORT && - workreq->getStatus() != LLWorkerThread::STATUS_ABORTED && + if (workreq->getStatus() != LLWorkerThread::STATUS_ABORTED && workreq->getStatus() != LLWorkerThread::STATUS_COMPLETE) { llerrs << "LLWorkerClass destroyed with active worker! Worker Status: " << workreq->getStatus() << llendl; @@ -184,21 +189,58 @@ LLWorkerClass::~LLWorkerClass() void LLWorkerClass::setWorkerThread(LLWorkerThread* workerthread) { - if (mWorkHandle != LLWorkerThread::nullHandle()) + mMutex.lock(); + if (mRequestHandle != LLWorkerThread::nullHandle()) { llerrs << "LLWorkerClass attempt to change WorkerThread with active worker!" << llendl; } mWorkerThread = workerthread; + mMutex.unlock(); +} + +//---------------------------------------------------------------------------- + +//virtual +void LLWorkerClass::finishWork(S32 param, bool success) +{ +} + +//virtual +bool LLWorkerClass::deleteOK() +{ + return true; // default always OK +} + +//---------------------------------------------------------------------------- + +// Called from worker thread +void LLWorkerClass::setWorking(bool working) +{ + mMutex.lock(); + if (working) + { + llassert_always(!(mWorkFlags & WCF_WORKING)); + setFlags(WCF_WORKING); + } + else + { + llassert_always((mWorkFlags & WCF_WORKING)); + clearFlags(WCF_WORKING); + } + mMutex.unlock(); } //---------------------------------------------------------------------------- bool LLWorkerClass::yield() { - llassert(mWorkFlags & WCF_WORKING); LLThread::yield(); mWorkerThread->checkPause(); - return (getFlags() & WCF_ABORT_REQUESTED) ? true : false; + bool res; + mMutex.lock(); + res = (getFlags() & WCF_ABORT_REQUESTED) ? true : false; + mMutex.unlock(); + return res; } //---------------------------------------------------------------------------- @@ -206,7 +248,9 @@ bool LLWorkerClass::yield() // calls startWork, adds doWork() to queue void LLWorkerClass::addWork(S32 param, U32 priority) { - if (mWorkHandle != LLWorkerThread::nullHandle()) + mMutex.lock(); + llassert_always(!(mWorkFlags & (WCF_WORKING|WCF_HAVE_WORK))); + if (mRequestHandle != LLWorkerThread::nullHandle()) { llerrs << "LLWorkerClass attempt to add work with active worker!" << llendl; } @@ -214,70 +258,93 @@ void LLWorkerClass::addWork(S32 param, U32 priority) // llinfos << "addWork: " << mWorkerClassName << " Param: " << param << llendl; #endif startWork(param); - mWorkHandle = mWorkerThread->add(this, param, priority); + clearFlags(WCF_WORK_FINISHED|WCF_WORK_ABORTED); + setFlags(WCF_HAVE_WORK); + mRequestHandle = mWorkerThread->addWorkRequest(this, param, priority); + mMutex.unlock(); } -void LLWorkerClass::abortWork() +void LLWorkerClass::abortWork(bool autocomplete) { + mMutex.lock(); #if _DEBUG -// LLWorkerThread::Request* workreq = mWorkerThread->getRequest(mWorkHandle); +// LLWorkerThread::WorkRequest* workreq = mWorkerThread->getRequest(mRequestHandle); // if (workreq) // llinfos << "abortWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl; #endif - mWorkerThread->abortRequest(mWorkHandle); - setFlags(WCF_ABORT_REQUESTED); + if (mRequestHandle != LLWorkerThread::nullHandle()) + { + mWorkerThread->abortRequest(mRequestHandle, autocomplete); + mWorkerThread->setPriority(mRequestHandle, LLQueuedThread::PRIORITY_IMMEDIATE); + setFlags(WCF_ABORT_REQUESTED); + } + mMutex.unlock(); } // if doWork is complete or aborted, call endWork() and return true -bool LLWorkerClass::checkWork() +bool LLWorkerClass::checkWork(bool aborting) { + LLMutexLock lock(&mMutex); bool complete = false, abort = false; - LLWorkerThread::Request* workreq = (LLWorkerThread::Request*)mWorkerThread->getRequest(mWorkHandle); - llassert(workreq); - if (getFlags(WCF_ABORT_REQUESTED) || workreq->getStatus() == LLWorkerThread::STATUS_ABORTED) + if (mRequestHandle != LLWorkerThread::nullHandle()) { - complete = true; - abort = true; + LLWorkerThread::WorkRequest* workreq = (LLWorkerThread::WorkRequest*)mWorkerThread->getRequest(mRequestHandle); + llassert_always(workreq); + LLQueuedThread::status_t status = workreq->getStatus(); + if (status == LLWorkerThread::STATUS_ABORTED) + { + complete = true; + abort = true; + } + else if (status == LLWorkerThread::STATUS_COMPLETE) + { + complete = true; + } + else + { + llassert_always(!aborting || (workreq->getFlags() & LLQueuedThread::FLAG_ABORT)); + } + if (complete) + { + llassert_always(!(getFlags(WCF_WORKING))); + endWork(workreq->getParam(), abort); + mWorkerThread->completeRequest(mRequestHandle); + mRequestHandle = LLWorkerThread::nullHandle(); + clearFlags(WCF_HAVE_WORK); + } } - else if (workreq->getStatus() == LLWorkerThread::STATUS_COMPLETE) + else { complete = true; } - if (complete) - { -#if _DEBUG -// llinfos << "endWork: " << mWorkerClassName << " Param: " << workreq->getParam() << llendl; -#endif - endWork(workreq->getParam(), abort); - mWorkerThread->completeRequest(mWorkHandle); - mWorkHandle = LLWorkerThread::nullHandle(); - } return complete; } -void LLWorkerClass::killWork() +void LLWorkerClass::scheduleDelete() { - if (haveWork()) + bool do_delete = false; + mMutex.lock(); + if (!(getFlags(WCF_DELETE_REQUESTED))) { - abortWork(); - bool paused = mWorkerThread->isPaused(); - while (!checkWork()) - { - mWorkerThread->updateQueue(0); - } - if (paused) - { - mWorkerThread->pause(); - } + setFlags(WCF_DELETE_REQUESTED); + do_delete = true; + } + mMutex.unlock(); + if (do_delete) + { + mWorkerThread->deleteWorker(this); } } void LLWorkerClass::setPriority(U32 priority) { - if (haveWork()) + mMutex.lock(); + if (mRequestHandle != LLWorkerThread::nullHandle()) { - mWorkerThread->setPriority(mWorkHandle, priority); + mRequestPriority = priority; + mWorkerThread->setPriority(mRequestHandle, priority); } + mMutex.unlock(); } //============================================================================ diff --git a/indra/llcommon/llworkerthread.h b/indra/llcommon/llworkerthread.h index bf5887e797..d466c35786 100644 --- a/indra/llcommon/llworkerthread.h +++ b/indra/llcommon/llworkerthread.h @@ -28,13 +28,13 @@ class LLWorkerClass; class LLWorkerThread : public LLQueuedThread { public: - class Request : public LLQueuedThread::QueuedRequest + class WorkRequest : public LLQueuedThread::QueuedRequest { protected: - ~Request() {}; // use deleteRequest() + virtual ~WorkRequest(); // use deleteRequest() public: - Request(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param); + WorkRequest(handle_t handle, U32 priority, LLWorkerClass* workerclass, S32 param); S32 getParam() { @@ -45,6 +45,8 @@ public: return mWorkerClass; } + /*virtual*/ bool processRequest(); + /*virtual*/ void finishRequest(bool completed); /*virtual*/ void deleteRequest(); private: @@ -52,26 +54,24 @@ public: S32 mParam; }; -public: - LLWorkerThread(bool threaded = true, bool runalways = true); - ~LLWorkerThread(); - -protected: - /*virtual*/ bool processRequest(QueuedRequest* req); +private: + typedef std::list<LLWorkerClass*> delete_list_t; + delete_list_t mDeleteList; + LLMutex* mDeleteMutex; + apr_pool_t* mWorkerAPRPoolp; public: - handle_t add(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL); - - static void initClass(bool local_is_threaded = true, bool local_run_always = true); // Setup sLocal - static S32 updateClass(U32 ms_elapsed); - static S32 getAllPending(); - static void pauseAll(); - static void waitOnAllPending(); - static void cleanupClass(); // Delete sLocal + LLWorkerThread(const std::string& name, bool threaded = true); + ~LLWorkerThread(); -public: - static LLWorkerThread* sLocal; // Default worker thread - static std::set<LLWorkerThread*> sThreadList; // array of threads (includes sLocal) + apr_pool_t* getWorkerAPRPool() { return mWorkerAPRPoolp; } + + /*virtual*/ S32 update(U32 max_time_ms); + + handle_t addWorkRequest(LLWorkerClass* workerclass, S32 param, U32 priority = PRIORITY_NORMAL); + + void deleteWorker(LLWorkerClass* workerclass); // schedule for deletion + S32 getNumDeletes() { return mDeleteList.size(); } // debug }; //============================================================================ @@ -93,11 +93,17 @@ public: class LLWorkerClass { + friend class LLWorkerThread; + friend class LLWorkerThread::WorkRequest; public: typedef LLWorkerThread::handle_t handle_t; enum FLAGS { - WCF_WORKING = 0x01, + WCF_HAVE_WORK = 0x01, + WCF_WORKING = 0x02, + WCF_WORK_FINISHED = 0x10, + WCF_WORK_ABORTED = 0x20, + WCF_DELETE_REQUESTED = 0x40, WCF_ABORT_REQUESTED = 0x80 }; @@ -106,17 +112,29 @@ public: virtual ~LLWorkerClass(); // pure virtual, called from WORKER THREAD, returns TRUE if done - virtual bool doWork(S32 param)=0; // Called from LLWorkerThread::processRequest() - - // called from WORKER THREAD - void setWorking(bool working) { working ? setFlags(WCF_WORKING) : clearFlags(WCF_WORKING); } + virtual bool doWork(S32 param)=0; // Called from WorkRequest::processRequest() + // virtual, called from finishRequest() after completed or aborted + virtual void finishWork(S32 param, bool completed); // called from finishRequest() (WORK THREAD) + // virtual, returns true if safe to delete the worker + virtual bool deleteOK(); // called from update() (WORK THREAD) + // schedlueDelete(): schedules deletion once aborted or completed + void scheduleDelete(); + + bool haveWork() { return getFlags(WCF_HAVE_WORK); } // may still be true if aborted bool isWorking() { return getFlags(WCF_WORKING); } bool wasAborted() { return getFlags(WCF_ABORT_REQUESTED); } + + // setPriority(): changes the priority of a request + void setPriority(U32 priority); + U32 getPriority() { return mRequestPriority; } const std::string& getName() const { return mWorkerClassName; } protected: + // called from WORKER THREAD + void setWorking(bool working); + // Call from doWork only to avoid eating up cpu time. // Returns true if work has been aborted // yields the current thread and calls mWorkerThread->checkPause() @@ -128,20 +146,11 @@ protected: void addWork(S32 param, U32 priority = LLWorkerThread::PRIORITY_NORMAL); // abortWork(): requests that work be aborted - void abortWork(); + void abortWork(bool autocomplete); // checkWork(): if doWork is complete or aborted, call endWork() and return true - bool checkWork(); + bool checkWork(bool aborting = false); - // haveWork(): return true if mWorkHandle != null - bool haveWork() { return mWorkHandle != LLWorkerThread::nullHandle(); } - - // killWork(): aborts work and waits for the abort to process - void killWork(); - - // setPriority(): changes the priority of a request - void setPriority(U32 priority); - private: void setFlags(U32 flags) { mWorkFlags = mWorkFlags | flags; } void clearFlags(U32 flags) { mWorkFlags = mWorkFlags & ~flags; } @@ -156,9 +165,11 @@ private: protected: LLWorkerThread* mWorkerThread; std::string mWorkerClassName; - handle_t mWorkHandle; + handle_t mRequestHandle; + U32 mRequestPriority; // last priority set private: + LLMutex mMutex; LLAtomicU32 mWorkFlags; }; |