< Summary

Information
Class: Ice.Internal.Timer
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/Timer.cs
Tag: 125_37167941578
Line coverage
76%
Covered lines: 108
Uncovered lines: 33
Coverable lines: 141
Total lines: 323
Line coverage: 76.5%
Branch coverage
69%
Covered branches: 46
Total branches: 66
Branch coverage: 69.6%
Method coverage
81%
Covered methods: 9
Fully covered methods: 3
Total methods: 11
Method coverage: 81.8%
Full method coverage: 27.2%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
destroy()50%2290%
schedule(...)62.5%9871.43%
cancel(...)75%4488.89%
.ctor(...)100%11100%
init(...)100%44100%
updateObserver(...)100%22100%
Run()70.59%933462.96%
.ctor(...)100%11100%
CompareTo(...)87.5%8890%
Equals(...)0%2040%
GetHashCode()100%210%

File(s)

/_/csharp/src/Ice/Internal/Timer.cs

#LineLine coverage
 1// Copyright (c) ZeroC, Inc.
 2
 3using System.Diagnostics;
 4
 5//
 6// NOTE: We don't use C# timers, the API is quite a bit different from
 7// the C++ & Java timers and it's not clear what is the cost of
 8// scheduling and cancelling timers.
 9//
 10
 11namespace Ice.Internal;
 12
 13public interface TimerTask
 14{
 15    void runTimerTask();
 16}
 17
 18public sealed class Timer
 19{
 20    public void destroy()
 21    {
 122        lock (_mutex)
 23        {
 124            if (_instance == null)
 25            {
 026                return;
 27            }
 28
 129            _instance = null;
 130            Monitor.Pulse(_mutex);
 31
 132            _tokens.Clear();
 133            _tasks.Clear();
 134        }
 35
 136        _thread.Join();
 137    }
 38
 39    public void schedule(TimerTask task, TimeSpan delay)
 40    {
 141        if (delay < TimeSpan.Zero || delay > TimeSpan.FromMilliseconds(int.MaxValue))
 42        {
 043            throw new ArgumentOutOfRangeException(
 044                nameof(delay),
 045                $"The timer delay must be between 0 and {int.MaxValue} milliseconds.");
 46        }
 47
 148        lock (_mutex)
 49        {
 150            if (_instance == null)
 51            {
 052                throw new Ice.CommunicatorDestroyedException();
 53            }
 54
 155            var token = new Token(
 156                Time.currentMonotonicTimeMillis() + (long)delay.TotalMilliseconds,
 157                ++_tokenId,
 158                0,
 159                task);
 60
 61            try
 62            {
 163                _tasks.Add(task, token);
 164                _tokens.Add(token, null);
 165            }
 066            catch (ArgumentException)
 67            {
 68                Debug.Assert(false);
 069            }
 70
 171            if (token.scheduledTime < _wakeUpTime)
 72            {
 173                Monitor.Pulse(_mutex);
 74            }
 175        }
 176    }
 77
 78    public bool cancel(TimerTask task)
 79    {
 180        lock (_mutex)
 81        {
 182            if (_instance == null)
 83            {
 084                return false;
 85            }
 86
 187            if (!_tasks.TryGetValue(task, out Token token))
 88            {
 189                return false;
 90            }
 191            _tasks.Remove(task);
 192            _tokens.Remove(token);
 193            return true;
 94        }
 195    }
 96
 97    //
 98    // Only for use by Instance.
 99    //
 1100    internal Timer(Instance instance, ThreadPriority priority = ThreadPriority.Normal) =>
 1101        init(instance, priority, true);
 102
 103    internal void init(Instance instance, ThreadPriority priority, bool hasPriority)
 104    {
 1105        _instance = instance;
 106
 1107        string threadName = _instance.initializationData().properties.getIceProperty("Ice.ProgramName");
 1108        if (threadName.Length > 0)
 109        {
 1110            threadName += "-";
 111        }
 112
 1113        _thread = new Thread(new ThreadStart(Run));
 1114        _thread.IsBackground = true;
 1115        _thread.Name = threadName + "Ice.Timer";
 1116        if (hasPriority)
 117        {
 1118            _thread.Priority = priority;
 119        }
 1120        _thread.Start();
 1121    }
 122
 123    internal void updateObserver(Ice.Instrumentation.CommunicatorObserver obsv)
 124    {
 1125        lock (_mutex)
 126        {
 127            Debug.Assert(obsv != null);
 1128            _observer = obsv.getThreadObserver(
 1129                "Communicator",
 1130                _thread.Name,
 1131                Ice.Instrumentation.ThreadState.ThreadStateIdle,
 1132                _observer);
 1133            _observer?.attach();
 1134        }
 1135    }
 136
 137    public void Run()
 138    {
 1139        Token token = null;
 140        while (true)
 141        {
 1142            lock (_mutex)
 143            {
 1144                if (_instance != null)
 145                {
 146                    //
 147                    // If the task we just ran is a repeated task, schedule it
 148                    // again for execution if it wasn't canceled.
 149                    //
 1150                    if (token != null && token.delay > 0)
 151                    {
 0152                        if (_tasks.ContainsKey(token.task))
 153                        {
 0154                            token.scheduledTime = Time.currentMonotonicTimeMillis() + token.delay;
 0155                            _tokens.Add(token, null);
 156                        }
 157                    }
 158                }
 1159                token = null;
 160
 1161                if (_instance == null)
 162                {
 0163                    break;
 164                }
 165
 1166                if (_tokens.Count == 0)
 167                {
 1168                    _wakeUpTime = long.MaxValue;
 1169                    Monitor.Wait(_mutex);
 170                }
 171
 1172                if (_instance == null)
 173                {
 1174                    break;
 175                }
 176
 1177                while (_tokens.Count > 0 && _instance != null)
 178                {
 1179                    long now = Time.currentMonotonicTimeMillis();
 180
 1181                    Token first = null;
 1182                    foreach (Token t in _tokens.Keys)
 183                    {
 1184                        first = t;
 1185                        break;
 186                    }
 187                    Debug.Assert(first != null);
 188
 1189                    if (first.scheduledTime <= now)
 190                    {
 1191                        _tokens.Remove(first);
 1192                        token = first;
 1193                        if (token.delay == 0)
 194                        {
 1195                            _tasks.Remove(token.task);
 196                        }
 1197                        break;
 198                    }
 199
 1200                    _wakeUpTime = first.scheduledTime;
 201                    // The wait duration fits in an int: schedule rejects delays larger than int.MaxValue, and the
 202                    // monotonic clock only moves forward after that.
 203                    Debug.Assert(first.scheduledTime - now <= int.MaxValue);
 1204                    Monitor.Wait(_mutex, (int)(first.scheduledTime - now));
 205                }
 206
 1207                if (_instance == null)
 208                {
 1209                    break;
 210                }
 1211            }
 212
 1213            if (token != null)
 214            {
 215                try
 216                {
 1217                    Ice.Instrumentation.ThreadObserver threadObserver = _observer;
 1218                    if (threadObserver != null)
 219                    {
 0220                        threadObserver.stateChanged(
 0221                            Ice.Instrumentation.ThreadState.ThreadStateIdle,
 0222                            Ice.Instrumentation.ThreadState.ThreadStateInUseForOther);
 223                        try
 224                        {
 0225                            token.task.runTimerTask();
 0226                        }
 227                        finally
 228                        {
 0229                            threadObserver.stateChanged(
 0230                                Ice.Instrumentation.ThreadState.ThreadStateInUseForOther,
 0231                                Ice.Instrumentation.ThreadState.ThreadStateIdle);
 0232                        }
 233                    }
 234                    else
 235                    {
 1236                        token.task.runTimerTask();
 237                    }
 1238                }
 0239                catch (System.Exception ex)
 240                {
 0241                    lock (_mutex)
 242                    {
 0243                        if (_instance != null)
 244                        {
 0245                            string s = "unexpected exception from task run method in timer thread:\n" + ex;
 0246                            _instance.initializationData().logger.error(s);
 247                        }
 0248                    }
 0249                }
 250            }
 251        }
 1252    }
 253
 254    private class Token : System.IComparable
 255    {
 1256        public
 1257        Token(long scheduledTime, int id, long delay, TimerTask task)
 258        {
 1259            this.scheduledTime = scheduledTime;
 1260            this.id = id;
 1261            this.delay = delay;
 1262            this.task = task;
 1263        }
 264
 265        public int CompareTo(object o)
 266        {
 267            //
 268            // Token are sorted by scheduled time and token id.
 269            //
 1270            var r = (Token)o;
 1271            if (scheduledTime < r.scheduledTime)
 272            {
 1273                return -1;
 274            }
 1275            else if (scheduledTime > r.scheduledTime)
 276            {
 1277                return 1;
 278            }
 279
 1280            if (id < r.id)
 281            {
 1282                return -1;
 283            }
 1284            else if (id > r.id)
 285            {
 1286                return 1;
 287            }
 288
 0289            return 0;
 290        }
 291
 292        public override bool Equals(object o)
 293        {
 0294            if (ReferenceEquals(this, o))
 295            {
 0296                return true;
 297            }
 0298            return o is not Token t ? false : CompareTo(t) == 0;
 299        }
 300
 0301        public override int GetHashCode() => HashCode.Combine(id, scheduledTime);
 302
 303        public long scheduledTime;
 304        public int id; // Since we can't compare references, we need to use another id.
 305        public long delay;
 306        public TimerTask task;
 307    }
 308
 1309    private readonly object _mutex = new object();
 1310    private readonly IDictionary<Token, object> _tokens = new SortedDictionary<Token, object>();
 1311    private readonly IDictionary<TimerTask, Token> _tasks = new Dictionary<TimerTask, Token>();
 312    private Instance _instance;
 1313    private long _wakeUpTime = long.MaxValue;
 314    private int _tokenId;
 315    private Thread _thread;
 316
 317    //
 318    // We use a volatile to avoid synchronization when reading
 319    // _observer. Reference assignment is atomic in Java so it
 320    // also doesn't need to be synchronized.
 321    //
 322    private volatile Ice.Instrumentation.ThreadObserver _observer;
 323}