• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

realm / realm-core / thomas.goyne_484

05 Aug 2024 04:20PM UTC coverage: 91.097% (-0.01%) from 91.108%
thomas.goyne_484

Pull #7912

Evergreen

tgoyne
Extract some duplicated code for sync triggers and timers
Pull Request #7912: Extract some duplicated code for sync triggers and timers

102644 of 181486 branches covered (56.56%)

62 of 71 new or added lines in 6 files covered. (87.32%)

87 existing lines in 14 files now uncovered.

216695 of 237872 relevant lines covered (91.1%)

5798402.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.68
/src/realm/sync/trigger.hpp
1
///////////////////////////////////////////////////////////////////////////////
2
//
3
// Copyright 2022 Realm Inc.
4
//
5
// Licensed under the Apache License, Version 2.0 (the "License");
6
// you may not use this file except in compliance with the License.
7
// You may obtain a copy of the License at
8
//
9
// http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing, software
12
// distributed under the License is distributed on an "AS IS" BASIS,
13
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
// See the License for the specific language governing permissions and
15
// limitations under the License.
16
//
17
///////////////////////////////////////////////////////////////////////////////
18

19
#pragma once
20

21
#include <realm/status.hpp>
22
#include <realm/util/bind_ptr.hpp>
23

24
namespace realm::sync {
25

26
/// Register a function whose invocation can be triggered repeatedly.
27
///
28
/// While the function is always executed by the event loop thread, the
29
/// triggering of its execution can be done by any thread. The event loop
30
/// is provided through Service type and it must have
31
/// `post(SyncSocketProvider::FunctionHandler&&)` method.
32
///
33
/// The function is guaranteed to not be called after the Trigger object is
34
/// destroyed.
35
///
36
/// Note that even though the trigger() function is thread-safe, the Trigger
37
/// object, as a whole, is not. In particular, construction and destruction must
38
/// not be considered thread-safe.
39
template <class Service>
40
class Trigger final {
41
public:
42
    template <typename Handler, typename... Args>
43
    Trigger(Service& service, Handler&& handler, Args&&... args);
44
    ~Trigger() noexcept;
45

46
    Trigger() noexcept = delete;
47
    Trigger(Trigger&&) noexcept = default;
48
    Trigger& operator=(Trigger&&) noexcept = default;
49

50
    /// Trigger another invocation of the associated function.
51
    ///
52
    /// An invocation of trigger() puts the Trigger object into the triggered
53
    /// state. It remains in the triggered state until shortly before the
54
    /// function starts to execute. While the Trigger object is in the triggered
55
    /// state, trigger() has no effect. This means that the number of executions
56
    /// of the function will generally be less that the number of times
57
    /// trigger() is invoked.
58
    ///
59
    /// A particular invocation of trigger() ensures that there will be at least
60
    /// one invocation of the associated function whose execution begins after
61
    /// the beginning of the execution of trigger(), so long as the event loop
62
    /// thread does not exit prematurely from run().
63
    ///
64
    /// If trigger() is invoked from the event loop thread, the next execution
65
    /// of the associated function will not begin until after trigger() returns,
66
    /// effectively preventing reentrancy for the associated function.
67
    ///
68
    /// If trigger() is invoked from another thread, the associated function may
69
    /// start to execute before trigger() returns.
70
    ///
71
    /// Note that the associated function can retrigger itself, i.e., if the
72
    /// associated function calls trigger(), then that will lead to another
73
    /// invocation of the associated function, but not until the first
74
    /// invocation ends (no reentrance).
75
    ///
76
    /// This function is thread-safe.
77
    void trigger();
78

79
private:
80
    Service& m_service;
81

82
    struct HandlerBase : public util::AtomicRefCountBase {
83
        enum class State { Idle, Triggered, Destroyed };
84
        std::mutex mutex;
85
        State state = State::Idle;
86
        virtual void call() = 0;
87
    };
88

89
    template <typename Handler, typename... Args>
90
    struct HandlerImpl : HandlerBase {
91
        Handler handler;
92
        std::tuple<Args...> args;
93
        HandlerImpl(Handler&& h, Args&&... a)
94
            : handler(std::forward<Handler>(h))
7,142✔
95
            , args(std::forward<Args>(a)...)
7,148✔
96
        {
14,874✔
97
        }
14,874✔
98
        void call() override
99
        {
127,576✔
100
            std::apply(handler, args);
127,576✔
101
        }
127,576✔
102
    };
103
    util::bind_ptr<HandlerBase> m_handler;
104
};
105

106
template <class Service>
107
template <typename H, typename... A>
108
inline Trigger<Service>::Trigger(Service& service, H&& handler, A&&... args)
109
    : m_service(service)
7,146✔
110
    , m_handler(new HandlerImpl<H, A...>(std::forward<H>(handler), std::forward<A>(args)...))
7,146✔
111
{
14,872✔
112
}
14,872✔
113

114
template <class Service>
115
inline Trigger<Service>::~Trigger() noexcept
116
{
14,882✔
117
    if (m_handler) {
14,884✔
118
        std::lock_guard lock{m_handler->mutex};
14,880✔
119
        REALM_ASSERT(m_handler->state != HandlerBase::State::Destroyed);
14,880✔
120
        m_handler->state = HandlerBase::State::Destroyed;
14,880✔
121
    }
14,880✔
122
}
14,882✔
123

124
template <class Service>
125
inline void Trigger<Service>::trigger()
126
{
4,140,818✔
127
    REALM_ASSERT(m_handler);
4,140,818✔
128

129
    std::lock_guard lock{m_handler->mutex};
4,140,818✔
130
    REALM_ASSERT(m_handler->state != HandlerBase::State::Destroyed);
4,140,818✔
131

132
    if (m_handler->state == HandlerBase::State::Triggered) {
4,140,818✔
133
        return;
4,002,430✔
134
    }
4,002,430✔
135
    m_handler->state = HandlerBase::State::Triggered;
138,388✔
136

137
    m_service.post([handler = util::bind_ptr(m_handler)](Status status) {
138,404✔
138
        if (status == ErrorCodes::OperationAborted)
138,404✔
NEW
139
            return;
×
140
        if (!status.is_ok())
138,404✔
NEW
141
            throw Exception(status);
×
142

143
        {
138,404✔
144
            std::lock_guard lock{handler->mutex};
138,404✔
145
            // Do not execute the handler if the Trigger does not exist anymore.
146
            if (handler->state == HandlerBase::State::Destroyed) {
138,404✔
147
                return;
102✔
148
            }
102✔
149
            handler->state = HandlerBase::State::Idle;
138,302✔
150
        }
138,302✔
NEW
151
        handler->call();
×
152
    });
138,302✔
153
}
138,388✔
154

155
} // namespace realm::sync
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc