< Summary

Information
Class: Ice.Internal.Timer.Token
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/Timer.cs
Tag: 71_18251537082
Line coverage
76%
Covered lines: 16
Uncovered lines: 5
Coverable lines: 21
Total lines: 339
Line coverage: 76.1%
Branch coverage
58%
Covered branches: 7
Total branches: 12
Branch coverage: 58.3%
Method coverage
50%
Covered methods: 2
Total methods: 4
Method coverage: 50%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
CompareTo(...)87.5%8.06890%
Equals(...)0%2040%
GetHashCode()100%210%

File(s)

/home/runner/work/ice/ice/csharp/src/Ice/Internal/Timer.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Collections.Generic;
 4using System.Diagnostics;
 5using System.Threading;
 6
 7//
 8// NOTE: We don't use C# timers, the API is quite a bit different from
 9// the C++ & Java timers and it's not clear what is the cost of
 10// scheduling and cancelling timers.
 11//
 12
 13namespace Ice.Internal;
 14
 15public interface TimerTask
 16{
 17    void runTimerTask();
 18}
 19
 20public sealed class Timer
 21{
 22    public void destroy()
 23    {
 24        lock (_mutex)
 25        {
 26            if (_instance == null)
 27            {
 28                return;
 29            }
 30
 31            _instance = null;
 32            Monitor.Pulse(_mutex);
 33
 34            _tokens.Clear();
 35            _tasks.Clear();
 36        }
 37
 38        _thread.Join();
 39    }
 40
 41    public void schedule(TimerTask task, long delay)
 42    {
 43        lock (_mutex)
 44        {
 45            if (_instance == null)
 46            {
 47                throw new Ice.CommunicatorDestroyedException();
 48            }
 49
 50            var token = new Token(Time.currentMonotonicTimeMillis() + delay, ++_tokenId, 0, task);
 51
 52            try
 53            {
 54                _tasks.Add(task, token);
 55                _tokens.Add(token, null);
 56            }
 57            catch (ArgumentException)
 58            {
 59                Debug.Assert(false);
 60            }
 61
 62            if (token.scheduledTime < _wakeUpTime)
 63            {
 64                Monitor.Pulse(_mutex);
 65            }
 66        }
 67    }
 68
 69    public void scheduleRepeated(TimerTask task, long period)
 70    {
 71        lock (_mutex)
 72        {
 73            if (_instance == null)
 74            {
 75                throw new Ice.CommunicatorDestroyedException();
 76            }
 77
 78            var token = new Token(Time.currentMonotonicTimeMillis() + period, ++_tokenId, period, task);
 79
 80            try
 81            {
 82                _tasks.Add(task, token);
 83                _tokens.Add(token, null);
 84            }
 85            catch (System.ArgumentException)
 86            {
 87                Debug.Assert(false);
 88            }
 89
 90            if (token.scheduledTime < _wakeUpTime)
 91            {
 92                Monitor.Pulse(_mutex);
 93            }
 94        }
 95    }
 96
 97    public bool cancel(TimerTask task)
 98    {
 99        lock (_mutex)
 100        {
 101            if (_instance == null)
 102            {
 103                return false;
 104            }
 105
 106            if (!_tasks.TryGetValue(task, out Token token))
 107            {
 108                return false;
 109            }
 110            _tasks.Remove(task);
 111            _tokens.Remove(token);
 112            return true;
 113        }
 114    }
 115
 116    //
 117    // Only for use by Instance.
 118    //
 119    internal Timer(Instance instance, ThreadPriority priority = ThreadPriority.Normal) =>
 120        init(instance, priority, true);
 121
 122    internal void init(Instance instance, ThreadPriority priority, bool hasPriority)
 123    {
 124        _instance = instance;
 125
 126        string threadName = _instance.initializationData().properties.getIceProperty("Ice.ProgramName");
 127        if (threadName.Length > 0)
 128        {
 129            threadName += "-";
 130        }
 131
 132        _thread = new Thread(new ThreadStart(Run));
 133        _thread.IsBackground = true;
 134        _thread.Name = threadName + "Ice.Timer";
 135        if (hasPriority)
 136        {
 137            _thread.Priority = priority;
 138        }
 139        _thread.Start();
 140    }
 141
 142    internal void updateObserver(Ice.Instrumentation.CommunicatorObserver obsv)
 143    {
 144        lock (_mutex)
 145        {
 146            Debug.Assert(obsv != null);
 147            _observer = obsv.getThreadObserver(
 148                "Communicator",
 149                _thread.Name,
 150                Ice.Instrumentation.ThreadState.ThreadStateIdle,
 151                _observer);
 152            _observer?.attach();
 153        }
 154    }
 155
 156    public void Run()
 157    {
 158        Token token = null;
 159        while (true)
 160        {
 161            lock (_mutex)
 162            {
 163                if (_instance != null)
 164                {
 165                    //
 166                    // If the task we just ran is a repeated task, schedule it
 167                    // again for execution if it wasn't canceled.
 168                    //
 169                    if (token != null && token.delay > 0)
 170                    {
 171                        if (_tasks.ContainsKey(token.task))
 172                        {
 173                            token.scheduledTime = Time.currentMonotonicTimeMillis() + token.delay;
 174                            _tokens.Add(token, null);
 175                        }
 176                    }
 177                }
 178                token = null;
 179
 180                if (_instance == null)
 181                {
 182                    break;
 183                }
 184
 185                if (_tokens.Count == 0)
 186                {
 187                    _wakeUpTime = long.MaxValue;
 188                    Monitor.Wait(_mutex);
 189                }
 190
 191                if (_instance == null)
 192                {
 193                    break;
 194                }
 195
 196                while (_tokens.Count > 0 && _instance != null)
 197                {
 198                    long now = Time.currentMonotonicTimeMillis();
 199
 200                    Token first = null;
 201                    foreach (Token t in _tokens.Keys)
 202                    {
 203                        first = t;
 204                        break;
 205                    }
 206                    Debug.Assert(first != null);
 207
 208                    if (first.scheduledTime <= now)
 209                    {
 210                        _tokens.Remove(first);
 211                        token = first;
 212                        if (token.delay == 0)
 213                        {
 214                            _tasks.Remove(token.task);
 215                        }
 216                        break;
 217                    }
 218
 219                    _wakeUpTime = first.scheduledTime;
 220                    Monitor.Wait(_mutex, (int)(first.scheduledTime - now));
 221                }
 222
 223                if (_instance == null)
 224                {
 225                    break;
 226                }
 227            }
 228
 229            if (token != null)
 230            {
 231                try
 232                {
 233                    Ice.Instrumentation.ThreadObserver threadObserver = _observer;
 234                    if (threadObserver != null)
 235                    {
 236                        threadObserver.stateChanged(
 237                            Ice.Instrumentation.ThreadState.ThreadStateIdle,
 238                            Ice.Instrumentation.ThreadState.ThreadStateInUseForOther);
 239                        try
 240                        {
 241                            token.task.runTimerTask();
 242                        }
 243                        finally
 244                        {
 245                            threadObserver.stateChanged(
 246                                Ice.Instrumentation.ThreadState.ThreadStateInUseForOther,
 247                                Ice.Instrumentation.ThreadState.ThreadStateIdle);
 248                        }
 249                    }
 250                    else
 251                    {
 252                        token.task.runTimerTask();
 253                    }
 254                }
 255                catch (System.Exception ex)
 256                {
 257                    lock (_mutex)
 258                    {
 259                        if (_instance != null)
 260                        {
 261                            string s = "unexpected exception from task run method in timer thread:\n" + ex;
 262                            _instance.initializationData().logger.error(s);
 263                        }
 264                    }
 265                }
 266            }
 267        }
 268    }
 269
 270    private class Token : System.IComparable
 271    {
 1272        public
 1273        Token(long scheduledTime, int id, long delay, TimerTask task)
 274        {
 1275            this.scheduledTime = scheduledTime;
 1276            this.id = id;
 1277            this.delay = delay;
 1278            this.task = task;
 1279        }
 280
 281        public int CompareTo(object o)
 282        {
 283            //
 284            // Token are sorted by scheduled time and token id.
 285            //
 1286            var r = (Token)o;
 1287            if (scheduledTime < r.scheduledTime)
 288            {
 1289                return -1;
 290            }
 1291            else if (scheduledTime > r.scheduledTime)
 292            {
 1293                return 1;
 294            }
 295
 1296            if (id < r.id)
 297            {
 1298                return -1;
 299            }
 1300            else if (id > r.id)
 301            {
 1302                return 1;
 303            }
 304
 0305            return 0;
 306        }
 307
 308        public override bool Equals(object o)
 309        {
 0310            if (ReferenceEquals(this, o))
 311            {
 0312                return true;
 313            }
 0314            return o is not Token t ? false : CompareTo(t) == 0;
 315        }
 316
 0317        public override int GetHashCode() => HashCode.Combine(id, scheduledTime);
 318
 319        public long scheduledTime;
 320        public int id; // Since we can't compare references, we need to use another id.
 321        public long delay;
 322        public TimerTask task;
 323    }
 324
 325    private readonly object _mutex = new object();
 326    private readonly IDictionary<Token, object> _tokens = new SortedDictionary<Token, object>();
 327    private readonly IDictionary<TimerTask, Token> _tasks = new Dictionary<TimerTask, Token>();
 328    private Instance _instance;
 329    private long _wakeUpTime = long.MaxValue;
 330    private int _tokenId;
 331    private Thread _thread;
 332
 333    //
 334    // We use a volatile to avoid synchronization when reading
 335    // _observer. Reference assignment is atomic in Java so it
 336    // also doesn't need to be synchronized.
 337    //
 338    private volatile Ice.Instrumentation.ThreadObserver _observer;
 339}