llevents.cpp
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:23k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file   llevents.cpp
  3.  * @author Nat Goodspeed
  4.  * @date   2008-09-12
  5.  * @brief  Implementation for llevents.
  6.  * 
  7.  * $LicenseInfo:firstyear=2008&license=viewergpl$
  8.  * 
  9.  * Copyright (c) 2008-2010, Linden Research, Inc.
  10.  * 
  11.  * Second Life Viewer Source Code
  12.  * The source code in this file ("Source Code") is provided by Linden Lab
  13.  * to you under the terms of the GNU General Public License, version 2.0
  14.  * ("GPL"), unless you have obtained a separate licensing agreement
  15.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  16.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18.  * 
  19.  * There are special exceptions to the terms and conditions of the GPL as
  20.  * it is applied to this Source Code. View the full text of the exception
  21.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  22.  * online at
  23.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24.  * 
  25.  * By copying, modifying or distributing this software, you acknowledge
  26.  * that you have read and understood your obligations described above,
  27.  * and agree to abide by those obligations.
  28.  * 
  29.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31.  * COMPLETENESS OR PERFORMANCE.
  32.  * $/LicenseInfo$
  33.  */
  34. // Precompiled header
  35. #include "linden_common.h"
  36. #if LL_WINDOWS
  37. #pragma warning (disable : 4675) // "resolved by ADL" -- just as I want!
  38. #endif
  39. // associated header
  40. #include "llevents.h"
  41. // STL headers
  42. #include <set>
  43. #include <sstream>
  44. #include <algorithm>
  45. // std headers
  46. #include <typeinfo>
  47. #include <cassert>
  48. #include <cmath>
  49. #include <cctype>
  50. // external library headers
  51. #include <boost/range/iterator_range.hpp>
  52. #if LL_WINDOWS
  53. #pragma warning (push)
  54. #pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no
  55. #endif
  56. #include <boost/lexical_cast.hpp>
  57. #if LL_WINDOWS
  58. #pragma warning (pop)
  59. #endif
  60. // other Linden headers
  61. #include "stringize.h"
  62. #include "llerror.h"
  63. #include "llsdutil.h"
  64. #if LL_MSVC
  65. #pragma warning (disable : 4702)
  66. #endif
  67. /*****************************************************************************
  68. *   queue_names: specify LLEventPump names that should be instantiated as
  69. *   LLEventQueue
  70. *****************************************************************************/
  71. /**
  72.  * At present, we recognize particular requested LLEventPump names as needing
  73.  * LLEventQueues. Later on we'll migrate this information to an external
  74.  * configuration file.
  75.  */
  76. const char* queue_names[] =
  77. {
  78.     "placeholder - replace with first real name string"
  79. };
  80. /*****************************************************************************
  81. *   If there's a "mainloop" pump, listen on that to flush all LLEventQueues
  82. *****************************************************************************/
  83. struct RegisterFlush : public LLEventTrackable
  84. {
  85.     RegisterFlush():
  86.         pumps(LLEventPumps::instance())
  87.     {
  88.         pumps.obtain("mainloop").listen("flushLLEventQueues", boost::bind(&RegisterFlush::flush, this, _1));
  89.     }
  90.     bool flush(const LLSD&)
  91.     {
  92.         pumps.flush();
  93.         return false;
  94.     }
  95.     ~RegisterFlush()
  96.     {
  97.         // LLEventTrackable handles stopListening for us.
  98.     }
  99.     LLEventPumps& pumps;
  100. };
  101. static RegisterFlush registerFlush;
  102. /*****************************************************************************
  103. *   LLEventPumps
  104. *****************************************************************************/
  105. LLEventPumps::LLEventPumps():
  106.     // Until we migrate this information to an external config file,
  107.     // initialize mQueueNames from the static queue_names array.
  108.     mQueueNames(boost::begin(queue_names), boost::end(queue_names))
  109. {
  110. }
  111. LLEventPump& LLEventPumps::obtain(const std::string& name)
  112. {
  113.     PumpMap::iterator found = mPumpMap.find(name);
  114.     if (found != mPumpMap.end())
  115.     {
  116.         // Here we already have an LLEventPump instance with the requested
  117.         // name.
  118.         return *found->second;
  119.     }
  120.     // Here we must instantiate an LLEventPump subclass. 
  121.     LLEventPump* newInstance;
  122.     // Should this name be an LLEventQueue?
  123.     PumpNames::const_iterator nfound = mQueueNames.find(name);
  124.     if (nfound != mQueueNames.end())
  125.         newInstance = new LLEventQueue(name);
  126.     else
  127.         newInstance = new LLEventStream(name);
  128.     // LLEventPump's constructor implicitly registers each new instance in
  129.     // mPumpMap. But remember that we instantiated it (in mOurPumps) so we'll
  130.     // delete it later.
  131.     mOurPumps.insert(newInstance);
  132.     return *newInstance;
  133. }
  134. void LLEventPumps::flush()
  135. {
  136.     // Flush every known LLEventPump instance. Leave it up to each instance to
  137.     // decide what to do with the flush() call.
  138.     for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
  139.     {
  140.         pmi->second->flush();
  141.     }
  142. }
  143. void LLEventPumps::reset()
  144. {
  145.     // Reset every known LLEventPump instance. Leave it up to each instance to
  146.     // decide what to do with the reset() call.
  147.     for (PumpMap::iterator pmi = mPumpMap.begin(), pmend = mPumpMap.end(); pmi != pmend; ++pmi)
  148.     {
  149.         pmi->second->reset();
  150.     }
  151. }
  152. std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string& name, bool tweak)
  153. {
  154.     std::pair<PumpMap::iterator, bool> inserted =
  155.         mPumpMap.insert(PumpMap::value_type(name, const_cast<LLEventPump*>(&pump)));
  156.     // If the insert worked, then the name is unique; return that.
  157.     if (inserted.second)
  158.         return name;
  159.     // Here the new entry was NOT inserted, and therefore name isn't unique.
  160.     // Unless we're permitted to tweak it, that's Bad.
  161.     if (! tweak)
  162.     {
  163.         throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
  164.     }
  165.     // The passed name isn't unique, but we're permitted to tweak it. Find the
  166.     // first decimal-integer suffix not already taken. The insert() attempt
  167.     // above will have set inserted.first to the iterator of the existing
  168.     // entry by that name. Starting there, walk forward until we reach an
  169.     // entry that doesn't start with 'name'. For each entry consisting of name
  170.     // + integer suffix, capture the integer suffix in a set. Use a set
  171.     // because we're going to encounter string suffixes in the order: name1,
  172.     // name10, name11, name2, ... Walking those possibilities in that order
  173.     // isn't convenient to detect the first available "hole."
  174.     std::set<int> suffixes;
  175.     PumpMap::iterator pmi(inserted.first), pmend(mPumpMap.end());
  176.     // We already know inserted.first references the existing entry with
  177.     // 'name' as the key; skip that one and start with the next.
  178.     while (++pmi != pmend)
  179.     {
  180.         if (pmi->first.substr(0, name.length()) != name)
  181.         {
  182.             // Found the first entry beyond the entries starting with 'name':
  183.             // stop looping.
  184.             break;
  185.         }
  186.         // Here we're looking at an entry that starts with 'name'. Is the rest
  187.         // of it an integer?
  188.         // Dubious (?) assumption: in the local character set, decimal digits
  189.         // are in increasing order such that '9' is the last of them. This
  190.         // test deals with 'name' values such as 'a', where there might be a
  191.         // very large number of entries starting with 'a' whose suffixes
  192.         // aren't integers. A secondary assumption is that digit characters
  193.         // precede most common name characters (true in ASCII, false in
  194.         // EBCDIC). The test below is correct either way, but it's worth more
  195.         // if the assumption holds.
  196.         if (pmi->first[name.length()] > '9')
  197.             break;
  198.         // It should be cheaper to detect that we're not looking at a digit
  199.         // character -- and therefore the suffix can't possibly be an integer
  200.         // -- than to attempt the lexical_cast and catch the exception.
  201.         if (! std::isdigit(pmi->first[name.length()]))
  202.             continue;
  203.         // Okay, the first character of the suffix is a digit, it's worth at
  204.         // least attempting to convert to int.
  205.         try
  206.         {
  207.             suffixes.insert(boost::lexical_cast<int>(pmi->first.substr(name.length())));
  208.         }
  209.         catch (const boost::bad_lexical_cast&)
  210.         {
  211.             // If the rest of pmi->first isn't an int, just ignore it.
  212.         }
  213.     }
  214.     // Here we've accumulated in 'suffixes' all existing int suffixes of the
  215.     // entries starting with 'name'. Find the first unused one.
  216.     int suffix = 1;
  217.     for ( ; suffixes.find(suffix) != suffixes.end(); ++suffix)
  218.         ;
  219.     // Here 'suffix' is not in 'suffixes'. Construct a new name based on that
  220.     // suffix, insert it and return it.
  221.     std::ostringstream out;
  222.     out << name << suffix;
  223.     return registerNew(pump, out.str(), tweak);
  224. }
  225. void LLEventPumps::unregister(const LLEventPump& pump)
  226. {
  227.     // Remove this instance from mPumpMap
  228.     PumpMap::iterator found = mPumpMap.find(pump.getName());
  229.     if (found != mPumpMap.end())
  230.     {
  231.         mPumpMap.erase(found);
  232.     }
  233.     // If this instance is one we created, also remove it from mOurPumps so we
  234.     // won't try again to delete it later!
  235.     PumpSet::iterator psfound = mOurPumps.find(const_cast<LLEventPump*>(&pump));
  236.     if (psfound != mOurPumps.end())
  237.     {
  238.         mOurPumps.erase(psfound);
  239.     }
  240. }
  241. LLEventPumps::~LLEventPumps()
  242. {
  243.     // On destruction, delete every LLEventPump we instantiated (via
  244.     // obtain()). CAREFUL: deleting an LLEventPump calls its destructor, which
  245.     // calls unregister(), which removes that LLEventPump instance from
  246.     // mOurPumps. So an iterator loop over mOurPumps to delete contained
  247.     // LLEventPump instances is dangerous! Instead, delete them one at a time
  248.     // until mOurPumps is empty.
  249.     while (! mOurPumps.empty())
  250.     {
  251.         delete *mOurPumps.begin();
  252.     }
  253. }
  254. /*****************************************************************************
  255. *   LLEventPump
  256. *****************************************************************************/
  257. #if LL_WINDOWS
  258. #pragma warning (push)
  259. #pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
  260. #endif
  261. LLEventPump::LLEventPump(const std::string& name, bool tweak):
  262.     // Register every new instance with LLEventPumps
  263.     mName(LLEventPumps::instance().registerNew(*this, name, tweak)),
  264.     mSignal(new LLStandardSignal()),
  265.     mEnabled(true)
  266. {}
  267. #if LL_WINDOWS
  268. #pragma warning (pop)
  269. #endif
  270. LLEventPump::~LLEventPump()
  271. {
  272.     // Unregister this doomed instance from LLEventPumps
  273.     LLEventPumps::instance().unregister(*this);
  274. }
  275. // static data member
  276. const LLEventPump::NameList LLEventPump::empty;
  277. std::string LLEventPump::inventName(const std::string& pfx)
  278. {
  279.     static long suffix = 0;
  280.     return STRINGIZE(pfx << suffix++);
  281. }
  282. void LLEventPump::reset()
  283. {
  284.     mSignal.reset();
  285.     mConnections.clear();
  286.     //mDeps.clear();
  287. }
  288. LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventListener& listener,
  289.                                          const NameList& after,
  290.                                          const NameList& before)
  291. {
  292.     // Check for duplicate name before connecting listener to mSignal
  293.     ConnectionMap::const_iterator found = mConnections.find(name);
  294.     // In some cases the user might disconnect a connection explicitly -- or
  295.     // might use LLEventTrackable to disconnect implicitly. Either way, we can
  296.     // end up retaining in mConnections a zombie connection object that's
  297.     // already been disconnected. Such a connection object can't be
  298.     // reconnected -- nor, in the case of LLEventTrackable, would we want to
  299.     // try, since disconnection happens with the destruction of the listener
  300.     // object. That means it's safe to overwrite a disconnected connection
  301.     // object with the new one we're attempting. The case we want to prevent
  302.     // is only when the existing connection object is still connected.
  303.     if (found != mConnections.end() && found->second.connected())
  304.     {
  305.         throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
  306.                               "' on " + typeid(*this).name() + " '" + getName() + "'");
  307.     }
  308.     // Okay, name is unique, try to reconcile its dependencies. Specify a new
  309.     // "node" value that we never use for an mSignal placement; we'll fix it
  310.     // later.
  311.     DependencyMap::node_type& newNode = mDeps.add(name, -1.0, after, before);
  312.     // What if this listener has been added, removed and re-added? In that
  313.     // case newNode already has a non-negative value because we never remove a
  314.     // listener from mDeps. But keep processing uniformly anyway in case the
  315.     // listener was added back with different dependencies. Then mDeps.sort()
  316.     // would put it in a different position, and the old newNode placement
  317.     // value would be wrong, so we'd have to reassign it anyway. Trust that
  318.     // re-adding a listener with the same dependencies is the trivial case for
  319.     // mDeps.sort(): it can just replay its cache.
  320.     DependencyMap::sorted_range sorted_range;
  321.     try
  322.     {
  323.         // Can we pick an order that works including this new entry?
  324.         sorted_range = mDeps.sort();
  325.     }
  326.     catch (const DependencyMap::Cycle& e)
  327.     {
  328.         // No: the new node's after/before dependencies have made mDeps
  329.         // unsortable. If we leave the new node in mDeps, it will continue
  330.         // to screw up all future attempts to sort()! Pull it out.
  331.         mDeps.remove(name);
  332.         throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
  333.                     " '" + getName() + "' would cause cycle: " + e.what());
  334.     }
  335.     // Walk the list to verify that we haven't changed the order.
  336.     float previous = 0.0, myprev = 0.0;
  337.     DependencyMap::sorted_iterator mydmi = sorted_range.end(); // need this visible after loop
  338.     for (DependencyMap::sorted_iterator dmi = sorted_range.begin();
  339.          dmi != sorted_range.end(); ++dmi)
  340.     {
  341.         // Since we've added the new entry with an invalid placement,
  342.         // recognize it and skip it.
  343.         if (dmi->first == name)
  344.         {
  345.             // Remember the iterator belonging to our new node, and which
  346.             // placement value was 'previous' at that point.
  347.             mydmi = dmi;
  348.             myprev = previous;
  349.             continue;
  350.         }
  351.         // If the new node has rearranged the existing nodes, we'll find
  352.         // that their placement values are no longer in increasing order.
  353.         if (dmi->second < previous)
  354.         {
  355.             // This is another scenario in which we'd better back out the
  356.             // newly-added node from mDeps -- but don't do it yet, we want to
  357.             // traverse the existing mDeps to report on it!
  358.             // Describe the change to the order of our listeners. Copy
  359.             // everything but the newest listener to a vector we can sort to
  360.             // obtain the old order.
  361.             typedef std::vector< std::pair<float, std::string> > SortNameList;
  362.             SortNameList sortnames;
  363.             for (DependencyMap::sorted_iterator cdmi(sorted_range.begin()), cdmend(sorted_range.end());
  364.                  cdmi != cdmend; ++cdmi)
  365.             {
  366.                 if (cdmi->first != name)
  367.                 {
  368.                     sortnames.push_back(SortNameList::value_type(cdmi->second, cdmi->first));
  369.                 }
  370.             }
  371.             std::sort(sortnames.begin(), sortnames.end());
  372.             std::ostringstream out;
  373.             out << "New listener '" << name << "' on " << typeid(*this).name() << " '" << getName()
  374.                 << "' would move previous listener '" << dmi->first << "'nwas: ";
  375.             SortNameList::const_iterator sni(sortnames.begin()), snend(sortnames.end());
  376.             if (sni != snend)
  377.             {
  378.                 out << sni->second;
  379.                 while (++sni != snend)
  380.                 {
  381.                     out << ", " << sni->second;
  382.                 }
  383.             }
  384.             out << "nnow: ";
  385.             DependencyMap::sorted_iterator ddmi(sorted_range.begin()), ddmend(sorted_range.end());
  386.             if (ddmi != ddmend)
  387.             {
  388.                 out << ddmi->first;
  389.                 while (++ddmi != ddmend)
  390.                 {
  391.                     out << ", " << ddmi->first;
  392.                 }
  393.             }
  394.             // NOW remove the offending listener node.
  395.             mDeps.remove(name);
  396.             // Having constructed a description of the order change, inform caller.
  397.             throw OrderChange(out.str());
  398.         }
  399.         // This node becomes the previous one.
  400.         previous = dmi->second;
  401.     }
  402.     // We just got done with a successful mDeps.add(name, ...) call. We'd
  403.     // better have found 'name' somewhere in that sorted list!
  404.     assert(mydmi != sorted_range.end());
  405.     // Four cases:
  406.     // 0. name is the only entry: placement 1.0
  407.     // 1. name is the first of several entries: placement (next placement)/2
  408.     // 2. name is between two other entries: placement (myprev + (next placement))/2
  409.     // 3. name is the last entry: placement ceil(myprev) + 1.0
  410.     // Since we've cleverly arranged for myprev to be 0.0 if name is the
  411.     // first entry, this folds down to two cases. Case 1 is subsumed by
  412.     // case 2, and case 0 is subsumed by case 3. So we need only handle
  413.     // cases 2 and 3, which means we need only detect whether name is the
  414.     // last entry. Increment mydmi to see if there's anything beyond.
  415.     if (++mydmi != sorted_range.end())
  416.     {
  417.         // The new node isn't last. Place it between the previous node and
  418.         // the successor.
  419.         newNode = (myprev + mydmi->second)/2.0;
  420.     }
  421.     else
  422.     {
  423.         // The new node is last. Bump myprev up to the next integer, add
  424.         // 1.0 and use that.
  425.         newNode = std::ceil(myprev) + 1.0;
  426.     }
  427.     // Now that newNode has a value that places it appropriately in mSignal,
  428.     // connect it.
  429.     LLBoundListener bound = mSignal->connect(newNode, listener);
  430.     mConnections[name] = bound;
  431.     return bound;
  432. }
  433. LLBoundListener LLEventPump::getListener(const std::string& name) const
  434. {
  435.     ConnectionMap::const_iterator found = mConnections.find(name);
  436.     if (found != mConnections.end())
  437.     {
  438.         return found->second;
  439.     }
  440.     // not found, return dummy LLBoundListener
  441.     return LLBoundListener();
  442. }
  443. void LLEventPump::stopListening(const std::string& name)
  444. {
  445.     ConnectionMap::iterator found = mConnections.find(name);
  446.     if (found != mConnections.end())
  447.     {
  448.         found->second.disconnect();
  449.         mConnections.erase(found);
  450.     }
  451.     // We intentionally do NOT remove this name from mDeps. It may happen that
  452.     // the same listener with the same name and dependencies will jump on and
  453.     // off this LLEventPump repeatedly. Keeping a cache of dependencies will
  454.     // avoid a new dependency sort in such cases.
  455. }
  456. /*****************************************************************************
  457. *   LLEventStream
  458. *****************************************************************************/
  459. bool LLEventStream::post(const LLSD& event)
  460. {
  461.     if (! mEnabled)
  462.     {
  463.         return false;
  464.     }
  465.     // NOTE NOTE NOTE: Any new access to member data beyond this point should
  466.     // cause us to move our LLStandardSignal object to a pimpl class along
  467.     // with said member data. Then the local shared_ptr will preserve both.
  468.     // DEV-43463: capture a local copy of mSignal. We've turned up a
  469.     // cross-coroutine scenario (described in the Jira) in which this post()
  470.     // call could end up destroying 'this', the LLEventPump subclass instance
  471.     // containing mSignal, during the call through *mSignal. So -- capture a
  472.     // *stack* instance of the shared_ptr, ensuring that our heap
  473.     // LLStandardSignal object will live at least until post() returns, even
  474.     // if 'this' gets destroyed during the call.
  475.     boost::shared_ptr<LLStandardSignal> signal(mSignal);
  476.     // Let caller know if any one listener handled the event. This is mostly
  477.     // useful when using LLEventStream as a listener for an upstream
  478.     // LLEventPump.
  479.     return (*signal)(event);
  480. }
  481. /*****************************************************************************
  482. *   LLEventQueue
  483. *****************************************************************************/
  484. bool LLEventQueue::post(const LLSD& event)
  485. {
  486.     if (mEnabled)
  487.     {
  488.         // Defer sending this event by queueing it until flush()
  489.         mEventQueue.push_back(event);
  490.     }
  491.     // Unconditionally return false. We won't know until flush() whether a
  492.     // listener claims to have handled the event -- meanwhile, don't block
  493.     // other listeners.
  494.     return false;
  495. }
  496. void LLEventQueue::flush()
  497. {
  498.     // Consider the case when a given listener on this LLEventQueue posts yet
  499.     // another event on the same queue. If we loop over mEventQueue directly,
  500.     // we'll end up processing all those events during the same flush() call
  501.     // -- rather like an EventStream. Instead, copy mEventQueue and clear it,
  502.     // so that any new events posted to this LLEventQueue during flush() will
  503.     // be processed in the *next* flush() call.
  504.     EventQueue queue(mEventQueue);
  505.     mEventQueue.clear();
  506.     // NOTE NOTE NOTE: Any new access to member data beyond this point should
  507.     // cause us to move our LLStandardSignal object to a pimpl class along
  508.     // with said member data. Then the local shared_ptr will preserve both.
  509.     // DEV-43463: capture a local copy of mSignal. See LLEventStream::post()
  510.     // for detailed comments.
  511.     boost::shared_ptr<LLStandardSignal> signal(mSignal);
  512.     for ( ; ! queue.empty(); queue.pop_front())
  513.     {
  514.         (*signal)(queue.front());
  515.     }
  516. }
  517. /*****************************************************************************
  518. *   LLListenerOrPumpName
  519. *****************************************************************************/
  520. LLListenerOrPumpName::LLListenerOrPumpName(const std::string& pumpname):
  521.     // Look up the specified pumpname, and bind its post() method as our listener
  522.     mListener(boost::bind(&LLEventPump::post,
  523.                           boost::ref(LLEventPumps::instance().obtain(pumpname)),
  524.                           _1))
  525. {
  526. }
  527. LLListenerOrPumpName::LLListenerOrPumpName(const char* pumpname):
  528.     // Look up the specified pumpname, and bind its post() method as our listener
  529.     mListener(boost::bind(&LLEventPump::post,
  530.                           boost::ref(LLEventPumps::instance().obtain(pumpname)),
  531.                           _1))
  532. {
  533. }
  534. bool LLListenerOrPumpName::operator()(const LLSD& event) const
  535. {
  536.     if (! mListener)
  537.     {
  538.         throw Empty("attempting to call uninitialized");
  539.     }
  540.     return (*mListener)(event);
  541. }
  542. void LLReqID::stamp(LLSD& response) const
  543. {
  544.     if (! (response.isUndefined() || response.isMap()))
  545.     {
  546.         // If 'response' was previously completely empty, it's okay to
  547.         // turn it into a map. If it was already a map, then it should be
  548.         // okay to add a key. But if it was anything else (e.g. a scalar),
  549.         // assigning a ["reqid"] key will DISCARD the previous value,
  550.         // replacing it with a map. That would be Bad.
  551.         LL_INFOS("LLReqID") << "stamp(" << mReqid << ") leaving non-map response unmodified: "
  552.                             << response << LL_ENDL;
  553.         return;
  554.     }
  555.     LLSD oldReqid(response["reqid"]);
  556.     if (! (oldReqid.isUndefined() || llsd_equals(oldReqid, mReqid)))
  557.     {
  558.         LL_INFOS("LLReqID") << "stamp(" << mReqid << ") preserving existing ["reqid"] value "
  559.                             << oldReqid << " in response: " << response << LL_ENDL;
  560.         return;
  561.     }
  562.     response["reqid"] = mReqid;
  563. }