diff options
| author | Rider Linden <rider@lindenlab.com> | 2016-09-16 14:43:35 -0700 | 
|---|---|---|
| committer | Rider Linden <rider@lindenlab.com> | 2016-09-16 14:43:35 -0700 | 
| commit | 884b03e8770a64aee22281518a5f3e2a7c0420ab (patch) | |
| tree | 70c723e32dcb5d857ea7f3c4684d586047a15a30 | |
| parent | 921cfa355cfc5130a55d77690e2ff80e1f370dcb (diff) | |
| parent | 68b8d2658a741617ae9844824090efe922da1edd (diff) | |
Merge
| -rw-r--r-- | indra/llcommon/llhandle.h | 81 | ||||
| -rw-r--r-- | indra/newview/llappviewer.cpp | 1 | ||||
| -rw-r--r-- | indra/newview/llcompilequeue.cpp | 323 | 
3 files changed, 222 insertions, 183 deletions
| diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 401e4d759a..feb5f41848 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,8 +28,11 @@  #define LLHANDLE_H  #include "llpointer.h" +#include "llexception.h" +#include <stdexcept>  #include <boost/type_traits/is_convertible.hpp>  #include <boost/utility/enable_if.hpp> +#include <boost/throw_exception.hpp>  /**   * Helper object for LLHandle. Don't instantiate these directly, used @@ -213,4 +216,82 @@ private:  	mutable LLRootHandle<T> mHandle;  }; + + +class LLCheckedHandleBase +{ +public: +    class Stale : public LLException +    { +    public: +        Stale() : +            LLException("Attempt to access stale handle.") +        {} +    }; + +protected: +    LLCheckedHandleBase() { } + +}; + +/** + * This is a simple wrapper for Handles, allowing direct calls to the underlying  + * pointer. The checked handle will throw a Stale if an attempt  + * is made to access the object referenced by the handle and that object has  + * been destroyed. + **/ +template <typename T>  +class LLCheckedHandle: public LLCheckedHandleBase +{ +public: + +    LLCheckedHandle(LLHandle<T> handle): +        mHandle(handle) +    { } + +    /** +     * Test the underlying handle.  If it is no longer valid, throw a Stale exception. +     */ +    void check() const +    { +        T* ptr = mHandle.get(); +        if (!ptr) +            BOOST_THROW_EXCEPTION(Stale()); +    } + +    /** +     * Cast back to an appropriate handle +     */ +    operator LLHandle<T>() const +    { +        return mHandle; +    } + +    /** +     * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {}  +     * Does not throw. +     */ +    /*explicit*/ operator bool() const // explicit conversion operator not available with Linux compiler +    { +        return (mHandle.get() != NULL); +    } + +    /** +     * Attempt to call a method or access a member in the structure referenced  +     * by the handle.  If the handle no longer points to a valid structure  +     * throw a Stale. +     */ +    T* operator ->() const +    { +        T* ptr = mHandle.get(); +        if (!ptr) +            BOOST_THROW_EXCEPTION(Stale()); +        return ptr; +    } + +private: + +    LLHandle<T> mHandle; +}; +  #endif diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 3023944f6e..d31008f1c4 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -135,6 +135,7 @@  #include <boost/foreach.hpp>  #include <boost/algorithm/string.hpp>  #include <boost/regex.hpp> +#include <boost/throw_exception.hpp>  #if LL_WINDOWS  #	include <share.h> // For _SH_DENYWR in processMarkerFiles diff --git a/indra/newview/llcompilequeue.cpp b/indra/newview/llcompilequeue.cpp index 7721e67290..76e16f5a1f 100644 --- a/indra/newview/llcompilequeue.cpp +++ b/indra/newview/llcompilequeue.cpp @@ -67,7 +67,9 @@ namespace      const std::string QUEUE_EVENTPUMP_NAME("ScriptActionQueue"); - +    // ObjectIventoryFetcher is an adapter between the LLVOInventoryListener::inventoryChanged  +    // callback mechanism and the LLEventPump coroutine architecture allowing the  +    // coroutine to wait for the inventory event.      class ObjectInventoryFetcher: public LLVOInventoryListener      {      public: @@ -144,7 +146,7 @@ public:              queue->getChild<LLScrollListCtrl>("queue output")->addSimpleElement(message, ADD_BOTTOM);          } -        return LLSD().with("success", LLSD::Boolean(true)); +        return LLSDMap("success", LLSD::Boolean(true));      } @@ -254,7 +256,6 @@ LLFloaterCompileQueue::LLFloaterCompileQueue(const LLSD& key)  	setTitle(LLTrans::getString("CompileQueueTitle"));  	setStartString(LLTrans::getString("CompileQueueStart")); -//	mUploadQueue = new LLAssetUploadQueue(new LLCompileFloaterUploadQueueSupplier(key.asUUID()));  }  LLFloaterCompileQueue::~LLFloaterCompileQueue() @@ -267,7 +268,6 @@ void LLFloaterCompileQueue::experienceIdsReceived( const LLSD& content )  	{  		mExperienceIds.insert(it->asUUID());  	} -//	nextObject();  }  BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const @@ -277,11 +277,6 @@ BOOL LLFloaterCompileQueue::hasExperience( const LLUUID& id ) const  // //Attempt to record this asset ID.  If it can not be inserted into the set   // //then it has already been processed so return false. -// bool LLFloaterCompileQueue::checkAssetId(const LLUUID &assetId) -// { -//     std::pair<uuid_list_t::iterator, bool> result = mAssetIds.insert(assetId); -//     return result.second; -// }  void LLFloaterCompileQueue::handleHTTPResponse(std::string pumpName, const LLSD &expresult)  { @@ -331,8 +326,10 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren      queue->experienceIdsReceived(result["experience_ids"]); +    // getDerived handle gets a handle that can be resolved to a parent class of the derived object.      LLHandle<LLFloaterScriptQueue> hFloater(queue->getDerivedHandle<LLFloaterScriptQueue>()); +    // note subtle difference here: getDerivedHandle in this case is for an LLFloaterCompileQueue      fnQueueAction_t fn = boost::bind(LLFloaterCompileQueue::processScript,          queue->getDerivedHandle<LLFloaterCompileQueue>(), _1, _2, _3); @@ -345,37 +342,35 @@ void LLFloaterCompileQueue::processExperienceIdResults(LLSD result, LLUUID paren  } +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception.  bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloater,      const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)  {      LLSD result; -    LLFloaterCompileQueue *that = hfloater.get(); -    bool monocompile = that->mMono; +    LLCheckedHandle<LLFloaterCompileQueue> floater(hfloater); +    // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. +    // which is caught in objectScriptProcessingQueueCoro +    bool monocompile = floater->mMono;      F32 fetch_timeout = gSavedSettings.getF32("QueueInventoryFetchTimeout"); -    if (!that) -        return false;      // Initial test to see if we can (or should) attempt to compile the script.      LLInventoryItem *item = dynamic_cast<LLInventoryItem *>(inventory); -    { -        if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || -            !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) -        { -            std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; -            that->addStringMessage(buffer); -            return true; -        } +    if (!item) +    { +        LL_WARNS("SCRIPTQ") << "item retrieved is not an LLInventoryItem." << LL_ENDL; +        return true; +    } -//         if (!that->checkAssetId(item->getAssetUUID())) -//         { -//             std::string buffer = "Skipping: " + item->getName() + "(Repeat)"; -//             that->addStringMessage(buffer); -//             return true; -//         } +    if (!item->getPermissions().allowModifyBy(gAgent.getID(), gAgent.getGroupID()) || +        !item->getPermissions().allowCopyBy(gAgent.getID(), gAgent.getGroupID())) +    { +        std::string buffer = "Skipping: " + item->getName() + "(Permissions)"; +        floater->addStringMessage(buffer); +        return true;      } -    that = NULL;      // Attempt to retrieve the experience      LLUUID experienceId; @@ -384,37 +379,30 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat              boost::bind(&LLFloaterCompileQueue::handleHTTPResponse, pump.getName(), _1));          result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, -            LLSD().with("timeout", LLSD::Boolean(true))); +            LLSDMap("timeout", LLSD::Boolean(true))); -        that = hfloater.get(); -        if (!that) -        { -            return false; -        } - -        if (result.has("timeout") && result["timeout"].asBoolean()) -        { +        if (result.has("timeout")) +        {   // A timeout filed in the result will always be true if present.              LLStringUtil::format_map_t args;              args["[OBJECT_NAME]"] = inventory->getName(); -            std::string buffer = that->getString("Timeout", args); -            that->addStringMessage(buffer); +            std::string buffer = floater->getString("Timeout", args); +            floater->addStringMessage(buffer);              return true;          }          if (result.has(LLExperienceCache::EXPERIENCE_ID))          {              experienceId = result[LLExperienceCache::EXPERIENCE_ID].asUUID(); -            if (!that->hasExperience(experienceId)) +            if (!floater->hasExperience(experienceId))              { -                that->addProcessingMessage("CompileNoExperiencePerm", LLSD() -                    .with("SCRIPT", inventory->getName()) -                    .with("EXPERIENCE", result[LLExperienceCache::NAME].asString())); +                floater->addProcessingMessage("CompileNoExperiencePerm",  +                    LLSDMap("SCRIPT", inventory->getName()) +                        ("EXPERIENCE", result[LLExperienceCache::NAME].asString()));                  return true;              }          }      } -    that = NULL;      {          HandleScriptUserData    userData(pump.getName()); @@ -433,32 +421,23 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat              &userData);          result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, -            LLSD().with("timeout", LLSD::Boolean(true))); -    } - -    that = hfloater.get(); -    if (!that) -    { -        return false; +            LLSDMap("timeout", LLSD::Boolean(true)));      }      if (result.has("timeout")) -    { -        if (result.has("timeout") && result["timeout"].asBoolean()) -        { -            LLStringUtil::format_map_t args; -            args["[OBJECT_NAME]"] = inventory->getName(); -            std::string buffer = that->getString("Timeout", args); -            that->addStringMessage(buffer); -            return true; -        } +    {   // A timeout filed in the result will always be true if present. +        LLStringUtil::format_map_t args; +        args["[OBJECT_NAME]"] = inventory->getName(); +        std::string buffer = floater->getString("Timeout", args); +        floater->addStringMessage(buffer); +        return true;      }      if (result.has("error"))      {          LL_WARNS("SCRIPTQ") << "Inventory fetch returned with error. Code: " << result["error"].asString() << LL_ENDL;          std::string buffer = result["message"].asString() + " " + inventory->getName(); -        that->addStringMessage(buffer); +        floater->addStringMessage(buffer);          if (result.has("alert"))          { @@ -470,12 +449,9 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat      }      LLUUID assetId = result["asset_id"]; -    that = NULL; -      std::string url = object->getRegion()->getCapability("UpdateScriptTask"); -      {          LLResourceUploadInfo::ptr_t uploadInfo(new LLQueuedScriptAssetUpload(object->getID(),               inventory->getUUID(),  @@ -490,24 +466,15 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat          LLViewerAssetUpload::EnqueueInventoryUpload(url, uploadInfo);      } -    result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSD().with("timeout", LLSD::Boolean(true))); - -    that = hfloater.get(); -    if (!that) -    { -        return false; -    } +    result = llcoro::suspendUntilEventOnWithTimeout(pump, fetch_timeout, LLSDMap("timeout", LLSD::Boolean(true)));      if (result.has("timeout")) -    { -        if (result.has("timeout") && result["timeout"].asBoolean()) -        { -            LLStringUtil::format_map_t args; -            args["[OBJECT_NAME]"] = inventory->getName(); -            std::string buffer = that->getString("Timeout", args); -            that->addStringMessage(buffer); -            return true; -        } +    { // A timeout filed in the result will always be true if present. +        LLStringUtil::format_map_t args; +        args["[OBJECT_NAME]"] = inventory->getName(); +        std::string buffer = floater->getString("Timeout", args); +        floater->addStringMessage(buffer); +        return true;      }      // Bytecode save completed @@ -515,21 +482,21 @@ bool LLFloaterCompileQueue::processScript(LLHandle<LLFloaterCompileQueue> hfloat      {          std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" succeeded"); -        that->addStringMessage(buffer); +        floater->addStringMessage(buffer);          LL_INFOS() << buffer << LL_ENDL;      }      else      {          LLSD compile_errors = result["errors"];          std::string buffer = std::string("Compilation of \"") + inventory->getName() + std::string("\" failed:"); -        that->addStringMessage(buffer); +        floater->addStringMessage(buffer);          for (LLSD::array_const_iterator line = compile_errors.beginArray();              line < compile_errors.endArray(); line++)          {              std::string str = line->asString();              str.erase(std::remove(str.begin(), str.end(), '\n'), str.end()); -            that->addStringMessage(str); +            floater->addStringMessage(str);          }          LL_INFOS() << result["errors"] << LL_ENDL;      } @@ -576,16 +543,18 @@ LLFloaterResetQueue::~LLFloaterResetQueue()  {   } -bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,  +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,      const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)  { -    LLFloaterScriptQueue *that = hfloater.get(); -    if (that) -    { -        std::string buffer; -        buffer = that->getString("Resetting") + (": ") + inventory->getName(); -        that->addStringMessage(buffer); -    } +    LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); +    // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. +    // which is caught in objectScriptProcessingQueueCoro + +    std::string buffer; +    buffer = floater->getString("Resetting") + (": ") + inventory->getName(); +    floater->addStringMessage(buffer);      LLMessageSystem* msg = gMessageSystem;      msg->newMessageFast(_PREHASH_ScriptReset); @@ -602,6 +571,8 @@ bool LLFloaterResetQueue::resetObjectScripts(LLHandle<LLFloaterScriptQueue> hflo  bool LLFloaterResetQueue::startQueue()  { +    // Bind the resetObjectScripts method into a QueueAction function and pass it +    // into the object queue processing coroutine.      fnQueueAction_t fn = boost::bind(LLFloaterResetQueue::resetObjectScripts,          getDerivedHandle<LLFloaterScriptQueue>(), _1, _2, _3); @@ -629,16 +600,18 @@ LLFloaterRunQueue::~LLFloaterRunQueue()  {   } -bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,  +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception. +bool LLFloaterRunQueue::runObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,      const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)  { -    LLFloaterScriptQueue *that = hfloater.get(); -    if (that) -    { -        std::string buffer; -        buffer = that->getString("Running") + (": ") + inventory->getName(); -        that->addStringMessage(buffer); -    } +    LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); +    // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. +    // which is caught in objectScriptProcessingQueueCoro + +    std::string buffer; +    buffer = floater->getString("Running") + (": ") + inventory->getName(); +    floater->addStringMessage(buffer);      LLMessageSystem* msg = gMessageSystem;      msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -684,16 +657,18 @@ LLFloaterNotRunQueue::~LLFloaterNotRunQueue()  {   } +/// This is a utility function to be bound and called from objectScriptProcessingQueueCoro. +/// Do not call directly. It may throw a LLCheckedHandle<>::Stale exception.  bool LLFloaterNotRunQueue::stopObjectScripts(LLHandle<LLFloaterScriptQueue> hfloater,       const LLPointer<LLViewerObject> &object, LLInventoryObject* inventory, LLEventPump &pump)  { -    LLFloaterScriptQueue *that = hfloater.get(); -    if (that) -    { -        std::string buffer; -        buffer = that->getString("NotRunning") + (": ") + inventory->getName(); -        that->addStringMessage(buffer); -    } +    LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater); +    // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle. +    // which is caught in objectScriptProcessingQueueCoro + +    std::string buffer; +    buffer = floater->getString("NotRunning") + (": ") + inventory->getName(); +    floater->addStringMessage(buffer);      LLMessageSystem* msg = gMessageSystem;      msg->newMessageFast(_PREHASH_SetScriptRunning); @@ -732,7 +707,7 @@ void ObjectInventoryFetcher::inventoryChanged(LLViewerObject* object,      mInventoryList.clear();      mInventoryList.assign(inventory->begin(), inventory->end()); -    mPump.post(LLSD().with("changed", LLSD::Boolean(true))); +    mPump.post(LLSDMap("changed", LLSD::Boolean(true)));  } @@ -740,115 +715,97 @@ void LLFloaterScriptQueue::objectScriptProcessingQueueCoro(std::string action, L      object_data_list_t objectList, fnQueueAction_t func)  {      LLCoros::set_consuming(true); -    LLFloaterScriptQueue * floater(NULL); +    LLCheckedHandle<LLFloaterScriptQueue> floater(hfloater);     +    // Dereferencing floater may fail. If they do they throw LLExeceptionStaleHandle.  +    // This is expected if the dialog closes.      LLEventMailDrop        maildrop(QUEUE_EVENTPUMP_NAME, true);      F32 fetch_timeout = gSavedSettings.getF32("QueueInventoryFetchTimeout"); -//     floater = hfloater.get(); -//     floater->addProcessingMessage("Starting", -//         LLSD() -//         .with("[START]", action) -//         .with("[COUNT]", LLSD::Integer(objectList.size()))); -//     floater = NULL; -    for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj) +    try      { -        bool firstForObject = true; -        LLUUID object_id = (*itObj).mObjectId; -        LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; - -        LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); -        LLInventoryObject::object_list_t inventory; -        if (obj) +        for (object_data_list_t::iterator itObj(objectList.begin()); (itObj != objectList.end()); ++itObj)          { -            ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); +            bool firstForObject = true; +            LLUUID object_id = (*itObj).mObjectId; +            LL_INFOS("SCRIPTQ") << "Next object in queue with ID=" << object_id.asString() << LL_ENDL; -            fetcher->fetchInventory(); - -            floater = hfloater.get(); -            if (floater) +            LLPointer<LLViewerObject> obj = gObjectList.findObject(object_id); +            LLInventoryObject::object_list_t inventory; +            if (obj)              { +                ObjectInventoryFetcher::ptr_t fetcher(new ObjectInventoryFetcher(maildrop, obj, NULL)); + +                fetcher->fetchInventory(); +                  LLStringUtil::format_map_t args;                  args["[OBJECT_NAME]"] = (*itObj).mObjectName;                  floater->addStringMessage(floater->getString("LoadingObjInv", args)); -            } -            LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, fetch_timeout, -                LLSD().with("timeout", LLSD::Boolean(true))); +                LLSD result = llcoro::suspendUntilEventOnWithTimeout(maildrop, fetch_timeout, +                    LLSDMap("timeout", LLSD::Boolean(true))); -            if (result.has("timeout") && result["timeout"].asBoolean()) -            { -                LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << -                    ". Skipping to next object." << LL_ENDL; +                if (result.has("timeout")) +                {    // A timeout filed in the result will always be true if present. +                    LL_WARNS("SCRIPTQ") << "Unable to retrieve inventory for object " << object_id.asString() << +                        ". Skipping to next object." << LL_ENDL; -                // floater could have been closed -                floater = hfloater.get(); -                if (floater) -                {                      LLStringUtil::format_map_t args;                      args["[OBJECT_NAME]"] = (*itObj).mObjectName;                      floater->addStringMessage(floater->getString("Timeout", args)); + +                    continue;                  } +                inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); +            } +            else +            { +                LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << +                    ". Skipping to next." << LL_ENDL;                  continue;              } -            inventory.assign(fetcher->getInventoryList().begin(), fetcher->getInventoryList().end()); -        } -        else -        { -            LL_WARNS("SCRIPTQ") << "Unable to retrieve object with ID of " << object_id << -                ". Skipping to next." << LL_ENDL; -            continue; -        } +            // TODO: Get the name of the object we are looking at here so that we can display it below. +            //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); +            LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; -        // TODO: Get the name of the object we are looking at here so that we can display it below. -        //std::string objName = (dynamic_cast<LLInventoryObject *>(obj.get()))->getName(); -        LL_DEBUGS("SCRIPTQ") << "Object has " << inventory.size() << " items." << LL_ENDL; - -        for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); -            itInv != inventory.end(); ++itInv) -        { -            floater = hfloater.get(); -            if (!floater) +            for (LLInventoryObject::object_list_t::iterator itInv = inventory.begin(); +                itInv != inventory.end(); ++itInv)              { -                LL_WARNS("SCRIPTQ") << "Script Queue floater closed! Canceling remaining ops" << LL_ENDL; -                break; -            } +                floater.check(); -            // note, we have a smart pointer to the obj above... but if we didn't we'd check that  -            // it still exists here. +                // note, we have a smart pointer to the obj above... but if we didn't we'd check that  +                // it still exists here. -            if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT)) -            { -                LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; -                if (firstForObject) +                if (((*itInv)->getType() == LLAssetType::AT_LSL_TEXT))                  { -                    //floater->addStringMessage(objName + ":"); -                    firstForObject = false; +                    LL_DEBUGS("SCRIPTQ") << "Inventory item " << (*itInv)->getUUID().asString() << "\"" << (*itInv)->getName() << "\"" << LL_ENDL; +                    if (firstForObject) +                    { +                        //floater->addStringMessage(objName + ":"); +                        firstForObject = false; +                    } + +                    if (!func(obj, (*itInv), maildrop)) +                    { +                        continue; +                    }                  } -                if (!func(obj, (*itInv), maildrop)) -                { -                    continue; -                } +                // no other explicit suspension point in this loop.  func(...) MIGHT suspend +                // but offers no guarantee of doing so. +                llcoro::suspend();              } - -            llcoro::suspend(); -        } -        // Just test to be sure the floater is still present before calling the func -        if (!hfloater.get()) -        { -            LL_WARNS("SCRIPTQ") << "Script Queue floater dismissed." << LL_ENDL; -            break;          } -    } - -    floater = hfloater.get(); -    if (floater) -    {          floater->addStringMessage("Done");          floater->getChildView("close")->setEnabled(TRUE);      } +    catch (LLCheckedHandleBase::Stale &) +    { +        // This is expected.  It means that floater has been closed before  +        // processing was completed. +        LL_DEBUGS("SCRIPTQ") << "LLExeceptionStaleHandle caught! Floater has most likely been closed." << LL_ENDL; +    }  } | 
