lleventcoro.cpp 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /**
  2. * @file lleventcoro.cpp
  3. * @author Nat Goodspeed
  4. * @date 2009-04-29
  5. * @brief Implementation for lleventcoro.
  6. *
  7. * $LicenseInfo:firstyear=2009&license=viewergpl$
  8. *
  9. * Copyright (c) 2009, 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. #include "linden_common.h"
  35. #include <chrono>
  36. #include "boost/fiber/operations.hpp"
  37. #include "lleventcoro.h"
  38. #include "llcoros.h"
  39. #include "llsdserialize.h"
  40. #include "llsdutil.h"
  41. namespace
  42. {
  43. // Implements behavior described for postAndSuspend()'s reply_pump_name_path
  44. // parameter:
  45. // * If path.isUndefined(), do nothing.
  46. // * If path.isString(), dest is an LLSD map, store value into
  47. // dest[path.asString()].
  48. // * If path.isInteger(), dest is an LLSD array: store value into
  49. // dest[path.asInteger()].
  50. // * If path.isArray(), iteratively apply the rules above to step down through
  51. // the structure of dest. The last array entry in path specifies the entry in
  52. // the lowest-level structure in dest into which to store value.
  53. //
  54. // Note: in the degenerate case in which path is an empty array, dest will
  55. // become value rather than containing it.
  56. void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value)
  57. {
  58. if (path.isDefined())
  59. {
  60. llsd::drill(dest, path) = value;
  61. }
  62. }
  63. } // anonymous
  64. void llcoro::suspend()
  65. {
  66. boost::this_fiber::yield();
  67. }
  68. void llcoro::suspendUntilTimeout(F32 seconds)
  69. {
  70. boost::this_fiber::sleep_for(std::chrono::milliseconds(long(seconds * 1000)));
  71. }
  72. namespace
  73. {
  74. LLBoundListener postAndSuspendSetup(const std::string& caller_name,
  75. const std::string& listener_name,
  76. LLCoros::Promise<LLSD>& promise,
  77. const LLSD& event,
  78. const LLEventPumpOrPumpName& request_pumpp,
  79. const LLEventPumpOrPumpName& reply_pumpp,
  80. const LLSD& reply_pump_name_path)
  81. {
  82. // Get the consuming attribute for THIS coroutine, the one that is about to
  83. // suspend. Do not call get_consuming() in the lambda body: that would
  84. // return the consuming attribute for some other coroutine, most likely
  85. // the main routine.
  86. bool consuming = LLCoros::get_consuming();
  87. // Listen on the specified LLEventPump with a lambda that will assign a
  88. // value to the promise, thus fulfilling its future.
  89. if (!reply_pumpp)
  90. {
  91. llerrs << "reply_pumpp required for " << caller_name << llendl;
  92. }
  93. LLEventPump& reply_pump(reply_pumpp.getPump());
  94. LLBoundListener connection(reply_pump.listen(listener_name,
  95. [&promise, consuming,
  96. listener_name](const LLSD& result)
  97. {
  98. try
  99. {
  100. promise.set_value(result);
  101. return consuming;
  102. }
  103. catch (boost::fibers::promise_already_satisfied& e)
  104. {
  105. llinfos << "Promise already satisfied in '"
  106. << listener_name << ": "
  107. << e.what() << llendl;
  108. return false;
  109. }
  110. }));
  111. // Skip the "post" part if request_pumpp is default-constructed
  112. if (request_pumpp)
  113. {
  114. LLEventPump& request_pump(request_pumpp.getPump());
  115. // If reply_pump_name_path is non-empty, store the reply_pump name in
  116. // the request event.
  117. LLSD modevent(event);
  118. storeToLLSDPath(modevent, reply_pump_name_path, reply_pump.getName());
  119. LL_DEBUGS("EventCoro") << caller_name << ": coroutine "
  120. << listener_name << " posting to "
  121. << request_pump.getName() << LL_ENDL;
  122. request_pump.post(modevent);
  123. }
  124. LL_DEBUGS("EventCoro") << caller_name << ": coroutine " << listener_name
  125. << " about to wait on LLEventPump "
  126. << reply_pump.getName() << LL_ENDL;
  127. return connection;
  128. }
  129. } // anonymous
  130. LLSD llcoro::postAndSuspend(const LLSD& event,
  131. const LLEventPumpOrPumpName& request_pump,
  132. const LLEventPumpOrPumpName& reply_pump,
  133. const LLSD& reply_pump_name_path)
  134. {
  135. LLCoros::Promise<LLSD> promise;
  136. std::string listener_name = LLCoros::getName();
  137. // Store connection into an LLTempBoundListener so we implicitly disconnect
  138. // on return from this function.
  139. LLTempBoundListener connection = postAndSuspendSetup("postAndSuspend()",
  140. listener_name,
  141. promise, event,
  142. request_pump,
  143. reply_pump,
  144. reply_pump_name_path);
  145. // Declare the future
  146. LLCoros::Future<LLSD> future = LLCoros::getFuture(promise);
  147. // Calling get() on the future makes us wait for it
  148. LLSD value(future.get());
  149. LL_DEBUGS("EventCoro") << "Coroutine '" << listener_name
  150. << "' resuming with: " << value << LL_ENDL;
  151. // Returning should disconnect the connection
  152. return value;
  153. }