summaryrefslogtreecommitdiff
path: root/indra/llcommon/llmainthreadtask.h
blob: eccf11fcbe4f545ceed212b28a116795cf3f217d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**
 * @file   llmainthreadtask.h
 * @author Nat Goodspeed
 * @date   2019-12-04
 * @brief  LLMainThreadTask dispatches work to the main thread. When invoked on
 *         the main thread, it performs the work inline.
 *
 * $LicenseInfo:firstyear=2019&license=viewerlgpl$
 * Copyright (c) 2019, Linden Research, Inc.
 * $/LicenseInfo$
 */

#if ! defined(LL_LLMAINTHREADTASK_H)
#define LL_LLMAINTHREADTASK_H

#include "llthread.h"
#include "workqueue.h"

/**
 * LLMainThreadTask provides a way to perform some task specifically on the
 * main thread, waiting for it to complete. A task consists of a C++ nullary
 * invocable (i.e. any callable that requires no arguments) with arbitrary
 * return type.
 *
 * Instead of instantiating LLMainThreadTask, pass your invocable to its
 * static dispatch() method. dispatch() returns the result of calling your
 * task. (Or, if your task throws an exception, dispatch() throws that
 * exception.)
 *
 * When you call dispatch() on the main thread (as determined by
 * on_main_thread() in llthread.h), it simply calls your task and returns the
 * result.
 *
 * When you call dispatch() on a secondary thread, it posts your task to
 * gMainloopWork, the WorkQueue serviced by the main thread, using
 * WorkQueue::waitForResult() to block the caller. Next time the main loop
 * calls gMainloopWork.runFor(), your task will be run, and waitForResult()
 * will return its result.
 */
class LLMainThreadTask
{
private:
    // Don't instantiate this class -- use dispatch() instead.
    LLMainThreadTask() {}

public:
    /// dispatch() is the only way to invoke this functionality.
    template <typename CALLABLE>
    static auto dispatch(CALLABLE&& callable) -> decltype(callable())
    {
        if (on_main_thread())
        {
            // we're already running on the main thread, perfect
            return callable();
        }
        else
        {
            auto queue{ LL::WorkQueue::getInstance("mainloop") };
            // If this needs a null check and a message, please introduce a
            // method in the .cpp file so consumers of this header don't drag
            // in llerror.h.
            // Use waitForResult_() so dispatch() can be used even from the
            // calling thread's default coroutine.
            return queue->waitForResult_(std::forward<CALLABLE>(callable));
        }
    }
};

#endif /* ! defined(LL_LLMAINTHREADTASK_H) */