< Summary

Information
Class: Ice.Internal.Timer
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/Timer.cs
Tag: 91_21789722663
Line coverage
70%
Covered lines: 103
Uncovered lines: 43
Coverable lines: 146
Total lines: 337
Line coverage: 70.5%
Branch coverage
66%
Covered branches: 44
Total branches: 66
Branch coverage: 66.6%
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()70.59%92.743462.96%
.ctor(...)100%11100%
CompareTo(...)87.5%8.06890%
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, long delay)
 40    {
 141        lock (_mutex)
 42        {
 143            if (_instance == null)
 44            {
 045                throw new Ice.CommunicatorDestroyedException();
 46            }
 47
 148            var token = new Token(Time.currentMonotonicTimeMillis() + delay, ++_tokenId, 0, task);
 49
 50            try
 51            {
 152                _tasks.Add(task, token);
 153                _tokens.Add(token, null);
 154            }
 055            catch (ArgumentException)
 56            {
 57                Debug.Assert(false);
 058            }
 59
 160            if (token.scheduledTime < _wakeUpTime)
 61            {
 162                Monitor.Pulse(_mutex);
 63            }
 164        }
 165    }
 66
 67    public void scheduleRepeated(TimerTask task, long period)
 68    {
 069        lock (_mutex)
 70        {
 071            if (_instance == null)
 72            {
 073                throw new Ice.CommunicatorDestroyedException();
 74            }
 75
 076            var token = new Token(Time.currentMonotonicTimeMillis() + period, ++_tokenId, period, task);
 77
 78            try
 79            {
 080                _tasks.Add(task, token);
 081                _tokens.Add(token, null);
 082            }
 083            catch (System.ArgumentException)
 84            {
 85                Debug.Assert(false);
 086            }
 87
 088            if (token.scheduledTime < _wakeUpTime)
 89            {
 090                Monitor.Pulse(_mutex);
 91            }
 092        }
 093    }
 94
 95    public bool cancel(TimerTask task)
 96    {
 197        lock (_mutex)
 98        {
 199            if (_instance == null)
 100            {
 0101                return false;
 102            }
 103
 1104            if (!_tasks.TryGetValue(task, out Token token))
 105            {
 1106                return false;
 107            }
 1108            _tasks.Remove(task);
 1109            _tokens.Remove(token);
 1110            return true;
 111        }
 1112    }
 113
 114    //
 115    // Only for use by Instance.
 116    //
 1117    internal Timer(Instance instance, ThreadPriority priority = ThreadPriority.Normal) =>
 1118        init(instance, priority, true);
 119
 120    internal void init(Instance instance, ThreadPriority priority, bool hasPriority)
 121    {
 1122        _instance = instance;
 123
 1124        string threadName = _instance.initializationData().properties.getIceProperty("Ice.ProgramName");
 1125        if (threadName.Length > 0)
 126        {
 1127            threadName += "-";
 128        }
 129
 1130        _thread = new Thread(new ThreadStart(Run));
 1131        _thread.IsBackground = true;
 1132        _thread.Name = threadName + "Ice.Timer";
 1133        if (hasPriority)
 134        {
 1135            _thread.Priority = priority;
 136        }
 1137        _thread.Start();
 1138    }
 139
 140    internal void updateObserver(Ice.Instrumentation.CommunicatorObserver obsv)
 141    {
 1142        lock (_mutex)
 143        {
 144            Debug.Assert(obsv != null);
 1145            _observer = obsv.getThreadObserver(
 1146                "Communicator",
 1147                _thread.Name,
 1148                Ice.Instrumentation.ThreadState.ThreadStateIdle,
 1149                _observer);
 1150            _observer?.attach();
 1151        }
 1152    }
 153
 154    public void Run()
 155    {
 1156        Token token = null;
 157        while (true)
 158        {
 1159            lock (_mutex)
 160            {
 1161                if (_instance != null)
 162                {
 163                    //
 164                    // If the task we just ran is a repeated task, schedule it
 165                    // again for execution if it wasn't canceled.
 166                    //
 1167                    if (token != null && token.delay > 0)
 168                    {
 0169                        if (_tasks.ContainsKey(token.task))
 170                        {
 0171                            token.scheduledTime = Time.currentMonotonicTimeMillis() + token.delay;
 0172                            _tokens.Add(token, null);
 173                        }
 174                    }
 175                }
 1176                token = null;
 177
 1178                if (_instance == null)
 179                {
 0180                    break;
 181                }
 182
 1183                if (_tokens.Count == 0)
 184                {
 1185                    _wakeUpTime = long.MaxValue;
 1186                    Monitor.Wait(_mutex);
 187                }
 188
 1189                if (_instance == null)
 190                {
 1191                    break;
 192                }
 193
 1194                while (_tokens.Count > 0 && _instance != null)
 195                {
 1196                    long now = Time.currentMonotonicTimeMillis();
 197
 1198                    Token first = null;
 1199                    foreach (Token t in _tokens.Keys)
 200                    {
 1201                        first = t;
 1202                        break;
 203                    }
 204                    Debug.Assert(first != null);
 205
 1206                    if (first.scheduledTime <= now)
 207                    {
 1208                        _tokens.Remove(first);
 1209                        token = first;
 1210                        if (token.delay == 0)
 211                        {
 1212                            _tasks.Remove(token.task);
 213                        }
 1214                        break;
 215                    }
 216
 1217                    _wakeUpTime = first.scheduledTime;
 1218                    Monitor.Wait(_mutex, (int)(first.scheduledTime - now));
 219                }
 220
 1221                if (_instance == null)
 222                {
 1223                    break;
 224                }
 1225            }
 226
 1227            if (token != null)
 228            {
 229                try
 230                {
 1231                    Ice.Instrumentation.ThreadObserver threadObserver = _observer;
 1232                    if (threadObserver != null)
 233                    {
 0234                        threadObserver.stateChanged(
 0235                            Ice.Instrumentation.ThreadState.ThreadStateIdle,
 0236                            Ice.Instrumentation.ThreadState.ThreadStateInUseForOther);
 237                        try
 238                        {
 0239                            token.task.runTimerTask();
 0240                        }
 241                        finally
 242                        {
 0243                            threadObserver.stateChanged(
 0244                                Ice.Instrumentation.ThreadState.ThreadStateInUseForOther,
 0245                                Ice.Instrumentation.ThreadState.ThreadStateIdle);
 0246                        }
 247                    }
 248                    else
 249                    {
 1250                        token.task.runTimerTask();
 251                    }
 1252                }
 0253                catch (System.Exception ex)
 254                {
 0255                    lock (_mutex)
 256                    {
 0257                        if (_instance != null)
 258                        {
 0259                            string s = "unexpected exception from task run method in timer thread:\n" + ex;
 0260                            _instance.initializationData().logger.error(s);
 261                        }
 0262                    }
 0263                }
 264            }
 265        }
 1266    }
 267
 268    private class Token : System.IComparable
 269    {
 1270        public
 1271        Token(long scheduledTime, int id, long delay, TimerTask task)
 272        {
 1273            this.scheduledTime = scheduledTime;
 1274            this.id = id;
 1275            this.delay = delay;
 1276            this.task = task;
 1277        }
 278
 279        public int CompareTo(object o)
 280        {
 281            //
 282            // Token are sorted by scheduled time and token id.
 283            //
 1284            var r = (Token)o;
 1285            if (scheduledTime < r.scheduledTime)
 286            {
 1287                return -1;
 288            }
 1289            else if (scheduledTime > r.scheduledTime)
 290            {
 1291                return 1;
 292            }
 293
 1294            if (id < r.id)
 295            {
 1296                return -1;
 297            }
 1298            else if (id > r.id)
 299            {
 1300                return 1;
 301            }
 302
 0303            return 0;
 304        }
 305
 306        public override bool Equals(object o)
 307        {
 0308            if (ReferenceEquals(this, o))
 309            {
 0310                return true;
 311            }
 0312            return o is not Token t ? false : CompareTo(t) == 0;
 313        }
 314
 0315        public override int GetHashCode() => HashCode.Combine(id, scheduledTime);
 316
 317        public long scheduledTime;
 318        public int id; // Since we can't compare references, we need to use another id.
 319        public long delay;
 320        public TimerTask task;
 321    }
 322
 1323    private readonly object _mutex = new object();
 1324    private readonly IDictionary<Token, object> _tokens = new SortedDictionary<Token, object>();
 1325    private readonly IDictionary<TimerTask, Token> _tasks = new Dictionary<TimerTask, Token>();
 326    private Instance _instance;
 1327    private long _wakeUpTime = long.MaxValue;
 328    private int _tokenId;
 329    private Thread _thread;
 330
 331    //
 332    // We use a volatile to avoid synchronization when reading
 333    // _observer. Reference assignment is atomic in Java so it
 334    // also doesn't need to be synchronized.
 335    //
 336    private volatile Ice.Instrumentation.ThreadObserver _observer;
 337}