summaryrefslogtreecommitdiff
path: root/indra/llcommon/workqueue.cpp
blob: ffc9a97dc090a26b6845fc1ced85347aa1c2f4c0 (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
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
 * @file   workqueue.cpp
 * @author Nat Goodspeed
 * @date   2021-10-06
 * @brief  Implementation for WorkQueue.
 * 
 * $LicenseInfo:firstyear=2021&license=viewerlgpl$
 * Copyright (c) 2021, Linden Research, Inc.
 * $/LicenseInfo$
 */

// Precompiled header
#include "linden_common.h"
// associated header
#include "workqueue.h"
// STL headers
// std headers
// external library headers
// other Linden headers
#include "llcoros.h"
#include LLCOROS_MUTEX_HEADER
#include "llerror.h"
#include "llexception.h"
#include "stringize.h"

using Mutex = LLCoros::Mutex;
using Lock  = LLCoros::LockType;

LL::WorkQueue::WorkQueue(const std::string& name):
    super(makeName(name))
{
    // TODO: register for "LLApp" events so we can implicitly close() on
    // viewer shutdown.
}

void LL::WorkQueue::close()
{
    mQueue.close();
}

void LL::WorkQueue::runUntilClose()
{
    try
    {
        for (;;)
        {
            callWork(mQueue.pop());
        }
    }
    catch (const Queue::Closed&)
    {
    }
}

bool LL::WorkQueue::runPending()
{
    for (Work work; mQueue.tryPop(work); )
    {
        callWork(work);
    }
    return ! mQueue.done();
}

bool LL::WorkQueue::runOne()
{
    Work work;
    if (mQueue.tryPop(work))
    {
        callWork(work);
    }
    return ! mQueue.done();
}

bool LL::WorkQueue::runUntil(const TimePoint& until)
{
    // Should we subtract some slop to allow for typical Work execution time?
    // How much slop?
    Work work;
    while (TimePoint::clock::now() < until && mQueue.tryPopUntil(until, work))
    {
        callWork(work);
    }
    return ! mQueue.done();
}

std::string LL::WorkQueue::makeName(const std::string& name)
{
    if (! name.empty())
        return name;

    static U32 discriminator = 0;
    static Mutex mutex;
    U32 num;
    {
        // Protect discriminator from concurrent access by different threads.
        // It can't be thread_local, else two racing threads will come up with
        // the same name.
        Lock lk(mutex);
        num = discriminator++;
    }
    return STRINGIZE("WorkQueue" << num);
}

void LL::WorkQueue::callWork(const Queue::DataTuple& work)
{
    // ThreadSafeSchedule::pop() always delivers a tuple, even when
    // there's only one data field per item, as for us.
    callWork(std::get<0>(work));
}

void LL::WorkQueue::callWork(const Work& work)
{
    try
    {
        work();
    }
    catch (...)
    {
        // No matter what goes wrong with any individual work item, the worker
        // thread must go on! Log our own instance name with the exception.
        LOG_UNHANDLED_EXCEPTION(getKey());
    }
}

void LL::WorkQueue::error(const std::string& msg)
{
    LL_ERRS("WorkQueue") << msg << LL_ENDL;
}