< Summary

Information
Class: Ice.Internal.Timer
Assembly: Ice
File(s): /home/runner/work/ice/ice/csharp/src/Ice/Internal/Timer.cs
Tag: 71_18251537082
Line coverage
71%
Covered lines: 104
Uncovered lines: 42
Coverable lines: 146
Total lines: 339
Line coverage: 71.2%
Branch coverage
68%
Covered branches: 45
Total branches: 66
Branch coverage: 68.1%
Method coverage
75%
Covered methods: 9
Total methods: 12
Method coverage: 75%

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
destroy()50%2290%
schedule(...)75%4.2476.92%
scheduleRepeated(...)0%2040%
cancel(...)75%4.02488.89%
.ctor(...)100%11100%
init(...)100%44100%
updateObserver(...)100%22100%
Run()73.53%84.383464.81%
.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    {
 124        lock (_mutex)
 25        {
 126            if (_instance == null)
 27            {
 028                return;
 29            }
 30
 131            _instance = null;
 132            Monitor.Pulse(_mutex);
 33
 134            _tokens.Clear();
 135            _tasks.Clear();
 136        }
 37
 138        _thread.Join();
 139    }
 40
 41    public void schedule(TimerTask task, long delay)
 42    {
 143        lock (_mutex)
 44        {
 145            if (_instance == null)
 46            {
 047                throw new Ice.CommunicatorDestroyedException();
 48            }
 49
 150            var token = new Token(Time.currentMonotonicTimeMillis() + delay, ++_tokenId, 0, task);
 51
 52            try
 53            {
 154                _tasks.Add(task, token);
 155                _tokens.Add(token, null);
 156            }
 057            catch (ArgumentException)
 58            {
 59                Debug.Assert(false);
 060            }
 61
 162            if (token.scheduledTime < _wakeUpTime)
 63            {
 164                Monitor.Pulse(_mutex);
 65            }
 166        }
 167    }
 68
 69    public void scheduleRepeated(TimerTask task, long period)
 70    {
 071        lock (_mutex)
 72        {
 073            if (_instance == null)
 74            {
 075                throw new Ice.CommunicatorDestroyedException();
 76            }
 77
 078            var token = new Token(Time.currentMonotonicTimeMillis() + period, ++_tokenId, period, task);
 79
 80            try
 81            {
 082                _tasks.Add(task, token);
 083                _tokens.Add(token, null);
 084            }
 085            catch (System.ArgumentException)
 86            {
 87                Debug.Assert(false);
 088            }
 89
 090            if (token.scheduledTime < _wakeUpTime)
 91            {
 092                Monitor.Pulse(_mutex);
 93            }
 094        }
 095    }
 96
 97    public bool cancel(TimerTask task)
 98    {
 199        lock (_mutex)
 100        {
 1101            if (_instance == null)
 102            {
 0103                return false;
 104            }
 105
 1106            if (!_tasks.TryGetValue(task, out Token token))
 107            {
 1108                return false;
 109            }
 1110            _tasks.Remove(task);
 1111            _tokens.Remove(token);
 1112            return true;
 113        }
 1114    }
 115
 116    //
 117    // Only for use by Instance.
 118    //
 1119    internal Timer(Instance instance, ThreadPriority priority = ThreadPriority.Normal) =>
 1120        init(instance, priority, true);
 121
 122    internal void init(Instance instance, ThreadPriority priority, bool hasPriority)
 123    {
 1124        _instance = instance;
 125
 1126        string threadName = _instance.initializationData().properties.getIceProperty("Ice.ProgramName");
 1127        if (threadName.Length > 0)
 128        {
 1129            threadName += "-";
 130        }
 131
 1132        _thread = new Thread(new ThreadStart(Run));
 1133        _thread.IsBackground = true;
 1134        _thread.Name = threadName + "Ice.Timer";
 1135        if (hasPriority)
 136        {
 1137            _thread.Priority = priority;
 138        }
 1139        _thread.Start();
 1140    }
 141
 142    internal void updateObserver(Ice.Instrumentation.CommunicatorObserver obsv)
 143    {
 1144        lock (_mutex)
 145        {
 146            Debug.Assert(obsv != null);
 1147            _observer = obsv.getThreadObserver(
 1148                "Communicator",
 1149                _thread.Name,
 1150                Ice.Instrumentation.ThreadState.ThreadStateIdle,
 1151                _observer);
 1152            _observer?.attach();
 1153        }
 1154    }
 155
 156    public void Run()
 157    {
 1158        Token token = null;
 159        while (true)
 160        {
 1161            lock (_mutex)
 162            {
 1163                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                    //
 1169                    if (token != null && token.delay > 0)
 170                    {
 0171                        if (_tasks.ContainsKey(token.task))
 172                        {
 0173                            token.scheduledTime = Time.currentMonotonicTimeMillis() + token.delay;
 0174                            _tokens.Add(token, null);
 175                        }
 176                    }
 177                }
 1178                token = null;
 179
 1180                if (_instance == null)
 181                {
 1182                    break;
 183                }
 184
 1185                if (_tokens.Count == 0)
 186                {
 1187                    _wakeUpTime = long.MaxValue;
 1188                    Monitor.Wait(_mutex);
 189                }
 190
 1191                if (_instance == null)
 192                {
 1193                    break;
 194                }
 195
 1196                while (_tokens.Count > 0 && _instance != null)
 197                {
 1198                    long now = Time.currentMonotonicTimeMillis();
 199
 1200                    Token first = null;
 1201                    foreach (Token t in _tokens.Keys)
 202                    {
 1203                        first = t;
 1204                        break;
 205                    }
 206                    Debug.Assert(first != null);
 207
 1208                    if (first.scheduledTime <= now)
 209                    {
 1210                        _tokens.Remove(first);
 1211                        token = first;
 1212                        if (token.delay == 0)
 213                        {
 1214                            _tasks.Remove(token.task);
 215                        }
 1216                        break;
 217                    }
 218
 1219                    _wakeUpTime = first.scheduledTime;
 1220                    Monitor.Wait(_mutex, (int)(first.scheduledTime - now));
 221                }
 222
 1223                if (_instance == null)
 224                {
 1225                    break;
 226                }
 1227            }
 228
 1229            if (token != null)
 230            {
 231                try
 232                {
 1233                    Ice.Instrumentation.ThreadObserver threadObserver = _observer;
 1234                    if (threadObserver != null)
 235                    {
 0236                        threadObserver.stateChanged(
 0237                            Ice.Instrumentation.ThreadState.ThreadStateIdle,
 0238                            Ice.Instrumentation.ThreadState.ThreadStateInUseForOther);
 239                        try
 240                        {
 0241                            token.task.runTimerTask();
 0242                        }
 243                        finally
 244                        {
 0245                            threadObserver.stateChanged(
 0246                                Ice.Instrumentation.ThreadState.ThreadStateInUseForOther,
 0247                                Ice.Instrumentation.ThreadState.ThreadStateIdle);
 0248                        }
 249                    }
 250                    else
 251                    {
 1252                        token.task.runTimerTask();
 253                    }
 1254                }
 0255                catch (System.Exception ex)
 256                {
 0257                    lock (_mutex)
 258                    {
 0259                        if (_instance != null)
 260                        {
 0261                            string s = "unexpected exception from task run method in timer thread:\n" + ex;
 0262                            _instance.initializationData().logger.error(s);
 263                        }
 0264                    }
 0265                }
 266            }
 267        }
 1268    }
 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
 1325    private readonly object _mutex = new object();
 1326    private readonly IDictionary<Token, object> _tokens = new SortedDictionary<Token, object>();
 1327    private readonly IDictionary<TimerTask, Token> _tasks = new Dictionary<TimerTask, Token>();
 328    private Instance _instance;
 1329    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}