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

microsoft / botbuilder-dotnet / 371447

05 Oct 2023 07:41PM UTC coverage: 73.455% (+0.004%) from 73.451%
371447

Pull #6696

CI-PR build

web-flow
Merge faa7f42a2 into c2f3aea4a
Pull Request #6696: fix: [#6683] Timeout issue when using DLASE

24147 of 32873 relevant lines covered (73.46%)

0.73 hits per line

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

68.25
/libraries/Microsoft.Bot.Connector.Streaming/Application/TimerAwaitable.cs
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
// Licensed under the MIT License.
3

4
using System;
5
using System.Runtime.CompilerServices;
6
using System.Threading;
7
using System.Threading.Tasks;
8

9
namespace Microsoft.Bot.Connector.Streaming.Application
10
{
11
    // Reusing internal awaitable timer from https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/common/Shared/TimerAwaitable.cs
12
    internal class TimerAwaitable : IDisposable, INotifyCompletion
13
    {
14
        private static readonly Action _callbackCompleted = () => { };
×
15

16
        private Timer _timer;
17
        private Action _callback;
18

19
        private readonly TimeSpan _period;
20

21
        private readonly TimeSpan _dueTime;
22
        private readonly object _lockObj = new object();
1✔
23
        private bool _disposed;
24
        private bool _running = true;
1✔
25

26
        public TimerAwaitable(TimeSpan dueTime, TimeSpan period)
1✔
27
        {
28
            _dueTime = dueTime;
1✔
29
            _period = period;
1✔
30
        }
1✔
31

32
        public bool IsCompleted => ReferenceEquals(_callback, _callbackCompleted);
1✔
33

34
        public void Start()
35
        {
36
            if (_timer == null)
1✔
37
            {
38
                lock (_lockObj)
1✔
39
                {
40
                    if (_disposed)
1✔
41
                    {
42
                        return;
×
43
                    }
44

45
                    if (_timer == null)
1✔
46
                    {
47
                        // This fixes the cycle by using a WeakReference to the state object. The object graph now looks like this:
48
                        // Timer -> TimerHolder -> TimerQueueTimer -> WeakReference<TimerAwaitable> -> Timer -> ...
49
                        // If TimerAwaitable falls out of scope, the timer should be released.
50
                        _timer = NonCapturingTimer.Create(
1✔
51
                            state =>
1✔
52
                            {
1✔
53
                                var weakRef = (WeakReference<TimerAwaitable>)state!;
1✔
54
                                if (weakRef.TryGetTarget(out var thisRef))
1✔
55
                                {
1✔
56
                                    thisRef.Tick();
1✔
57
                                }
1✔
58
                            },
1✔
59
                            state: new WeakReference<TimerAwaitable>(this),
1✔
60
                            dueTime: _dueTime,
1✔
61
                            period: _period);
1✔
62
                    }
63
                }
1✔
64
            }
65
        }
1✔
66

67
        public TimerAwaitable GetAwaiter() => this;
1✔
68

69
        public bool GetResult()
70
        {
71
            _callback = null;
1✔
72

73
            return _running;
1✔
74
        }
75

76
        public void OnCompleted(Action continuation)
77
        {
78
            if (ReferenceEquals(_callback, _callbackCompleted) ||
1✔
79
                ReferenceEquals(Interlocked.CompareExchange(ref _callback, continuation, null), _callbackCompleted))
1✔
80
            {
81
                _ = Task.Run(continuation);
×
82
            }
83
        }
1✔
84

85
        public void UnsafeOnCompleted(Action continuation)
86
        {
87
            OnCompleted(continuation);
×
88
        }
×
89

90
        public void Stop()
91
        {
92
            lock (_lockObj)
×
93
            {
94
                // Stop should be used to trigger the call to end the loop which disposes
95
                if (_disposed)
×
96
                {
97
                    throw new ObjectDisposedException(GetType().FullName);
×
98
                }
99

100
                _running = false;
×
101
            }
×
102

103
            // Call tick here to make sure that we yield the callback,
104
            // if it's currently waiting, we don't need to wait for the next period
105
            Tick();
×
106
        }
×
107

108
        void IDisposable.Dispose()
109
        {
110
            lock (_lockObj)
×
111
            {
112
                _disposed = true;
×
113

114
                _timer?.Dispose();
×
115

116
                _timer = null;
×
117
            }
×
118
        }
×
119

120
        private void Tick()
121
        {
122
            var continuation = Interlocked.Exchange(ref _callback, _callbackCompleted);
1✔
123
            continuation?.Invoke();
×
124
        }
1✔
125

126
        // A convenience API for interacting with System.Threading.Timer in a way
127
        // that doesn't capture the ExecutionContext. We should be using this (or equivalent)
128
        // everywhere we use timers to avoid rooting any values stored in asynclocals.
129
        private static class NonCapturingTimer
130
        {
131
            public static Timer Create(TimerCallback callback, object state, TimeSpan dueTime, TimeSpan period)
132
            {
133
                if (callback == null)
1✔
134
                {
135
                    throw new ArgumentNullException(nameof(callback));
×
136
                }
137

138
                // Don't capture the current ExecutionContext and its AsyncLocals onto the timer
139
                bool restoreFlow = false;
1✔
140
                try
141
                {
142
                    if (!ExecutionContext.IsFlowSuppressed())
1✔
143
                    {
144
                        ExecutionContext.SuppressFlow();
1✔
145
                        restoreFlow = true;
1✔
146
                    }
147

148
                    return new Timer(callback, state, dueTime, period);
1✔
149
                }
150
                finally
151
                {
152
                    // Restore the current ExecutionContext
153
                    if (restoreFlow)
1✔
154
                    {
155
                        ExecutionContext.RestoreFlow();
1✔
156
                    }
157
                }
1✔
158
            }
1✔
159
        }
160
    }
161
}
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