From a27281591da9d4023d78f06823adaf2a7d51f724 Mon Sep 17 00:00:00 2001
From: Nicky <sl.nicky.ml@googlemail.com>
Date: Fri, 7 Jun 2019 11:11:56 +0200
Subject: [PATCH] Replace boost::fibers::unbuffered_channel with
 boost::fibers::buffered_channel. Using boost::fibers::unbuffered_channel can
 block the mainthread when calling mPendingCoprocs.push
 (LLCoprocedurePool::enqueueCoprocedure) From the documentation: - If a fiber
 attempts to send a value through an unbuffered channel and no fiber is
 waiting to receive the value, the channel will block the sending fiber.

This can happen if LLCoprocedurePool::coprocedureInvokerCoro is running a coroutine and this coroutine calls yield, resuming the viewers main loop. If inside
the main loop someone calls LLCoprocedurePool::enqueueCoprocedure now push will block, as there's no one waiting for a result right now.
The wait would be in LLCoprocedurePool::coprocedureInvokerCoro at the start of the while loop, but we have not reached that yet again as LLCoprocedurePool::coprocedureInvokerCoro
did yield before reaching pop_wait_for.
The result is a deadlock.

boost::fibers::buffered_channel will not block as long as there's space in the channel. A size of 4096 (DEFAULT_QUEUE_SIZE) should be plenty enough for this.
---
 indra/llmessage/llcoproceduremanager.cpp | 7 ++++---
 1 file changed, 4 insertions(+), 3 deletions(-)

diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp
index 13ee12b5bb8..bc7c982756d 100644
--- a/indra/llmessage/llcoproceduremanager.cpp
+++ b/indra/llmessage/llcoproceduremanager.cpp
@@ -34,7 +34,7 @@
 #include <chrono>
 
 #include <boost/assign.hpp>
-#include <boost/fiber/unbuffered_channel.hpp>
+#include <boost/fiber/buffered_channel.hpp>
 
 #include "llexception.h"
 #include "stringize.h"
@@ -48,6 +48,7 @@ static const std::map<std::string, U32> DefaultPoolSizes{
 };
 
 static const U32 DEFAULT_POOL_SIZE = 5;
+static const U32 DEFAULT_QUEUE_SIZE = 4096;
 
 //=========================================================================
 class LLCoprocedurePool: private boost::noncopyable
@@ -112,7 +113,7 @@ class LLCoprocedurePool: private boost::noncopyable
 
     // we use a deque here rather than std::queue since we want to be able to 
     // iterate through the queue and potentially erase an entry from the middle.
-    typedef boost::fibers::unbuffered_channel<QueuedCoproc::ptr_t>  CoprocQueue_t;
+    typedef boost::fibers::buffered_channel<QueuedCoproc::ptr_t>  CoprocQueue_t;
     typedef std::map<LLUUID, LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t> ActiveCoproc_t;
 
     std::string     mPoolName;
@@ -283,7 +284,7 @@ void LLCoprocedureManager::close(const std::string &pool)
 LLCoprocedurePool::LLCoprocedurePool(const std::string &poolName, size_t size):
     mPoolName(poolName),
     mPoolSize(size),
-    mPendingCoprocs(),
+    mPendingCoprocs(DEFAULT_QUEUE_SIZE),
     //mShutdown(false),
     mCoroMapping(),
     mHTTPPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID)
-- 
GitLab