< Summary

Information
Class: Ice.Internal.Timer.Token
Assembly: Ice
File(s): /_/csharp/src/Ice/Internal/Timer.cs
Tag: 91_21789722663
Line coverage
76%
Covered lines: 16
Uncovered lines: 5
Coverable lines: 21
Total lines: 337
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)

/_/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    {
 22        lock (_mutex)
 23        {
 24            if (_instance == null)
 25            {
 26                return;
 27            }
 28
 29            _instance = null;
 30            Monitor.Pulse(_mutex);
 31
 32            _tokens.Clear();
 33            _tasks.Clear();
 34        }
 35
 36        _thread.Join();
 37    }
 38
 39    public void schedule(TimerTask task, long delay)
 40    {
 41        lock (_mutex)
 42        {
 43            if (_instance == null)
 44            {
 45                throw new Ice.CommunicatorDestroyedException();
 46            }
 47
 48            var token = new Token(Time.currentMonotonicTimeMillis() + delay, ++_tokenId, 0, task);
 49
 50            try
 51            {
 52                _tasks.Add(task, token);
 53                _tokens.Add(token, null);
 54            }
 55            catch (ArgumentException)
 56            {
 57                Debug.Assert(false);
 58            }
 59
 60            if (token.scheduledTime < _wakeUpTime)
 61            {
 62                Monitor.Pulse(_mutex);
 63            }
 64        }
 65    }
 66
 67    public void scheduleRepeated(TimerTask task, long period)
 68    {
 69        lock (_mutex)
 70        {
 71            if (_instance == null)
 72            {
 73                throw new Ice.CommunicatorDestroyedException();
 74            }
 75
 76            var token = new Token(Time.currentMonotonicTimeMillis() + period, ++_tokenId, period, task);
 77
 78            try
 79            {
 80                _tasks.Add(task, token);
 81                _tokens.Add(token, null);
 82            }
 83            catch (System.ArgumentException)
 84            {
 85                Debug.Assert(false);
 86            }
 87
 88            if (token.scheduledTime < _wakeUpTime)
 89            {
 90                Monitor.Pulse(_mutex);
 91            }
 92        }
 93    }
 94
 95    public bool cancel(TimerTask task)
 96    {
 97        lock (_mutex)
 98        {
 99            if (_instance == null)
 100            {
 101                return false;
 102            }
 103
 104            if (!_tasks.TryGetValue(task, out Token token))
 105            {
 106                return false;
 107            }
 108            _tasks.Remove(task);
 109            _tokens.Remove(token);
 110            return true;
 111        }
 112    }
 113
 114    //
 115    // Only for use by Instance.
 116    //
 117    internal Timer(Instance instance, ThreadPriority priority = ThreadPriority.Normal) =>
 118        init(instance, priority, true);
 119
 120    internal void init(Instance instance, ThreadPriority priority, bool hasPriority)
 121    {
 122        _instance = instance;
 123
 124        string threadName = _instance.initializationData().properties.getIceProperty("Ice.ProgramName");
 125        if (threadName.Length > 0)
 126        {
 127            threadName += "-";
 128        }
 129
 130        _thread = new Thread(new ThreadStart(Run));
 131        _thread.IsBackground = true;
 132        _thread.Name = threadName + "Ice.Timer";
 133        if (hasPriority)
 134        {
 135            _thread.Priority = priority;
 136        }
 137        _thread.Start();
 138    }
 139
 140    internal void updateObserver(Ice.Instrumentation.CommunicatorObserver obsv)
 141    {
 142        lock (_mutex)
 143        {
 144            Debug.Assert(obsv != null);
 145            _observer = obsv.getThreadObserver(
 146                "Communicator",
 147                _thread.Name,
 148                Ice.Instrumentation.ThreadState.ThreadStateIdle,
 149                _observer);
 150            _observer?.attach();
 151        }
 152    }
 153
 154    public void Run()
 155    {
 156        Token token = null;
 157        while (true)
 158        {
 159            lock (_mutex)
 160            {
 161                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                    //
 167                    if (token != null && token.delay > 0)
 168                    {
 169                        if (_tasks.ContainsKey(token.task))
 170                        {
 171                            token.scheduledTime = Time.currentMonotonicTimeMillis() + token.delay;
 172                            _tokens.Add(token, null);
 173                        }
 174                    }
 175                }
 176                token = null;
 177
 178                if (_instance == null)
 179                {
 180                    break;
 181                }
 182
 183                if (_tokens.Count == 0)
 184                {
 185                    _wakeUpTime = long.MaxValue;
 186                    Monitor.Wait(_mutex);
 187                }
 188
 189                if (_instance == null)
 190                {
 191                    break;
 192                }
 193
 194                while (_tokens.Count > 0 && _instance != null)
 195                {
 196                    long now = Time.currentMonotonicTimeMillis();
 197
 198                    Token first = null;
 199                    foreach (Token t in _tokens.Keys)
 200                    {
 201                        first = t;
 202                        break;
 203                    }
 204                    Debug.Assert(first != null);
 205
 206                    if (first.scheduledTime <= now)
 207                    {
 208                        _tokens.Remove(first);
 209                        token = first;
 210                        if (token.delay == 0)
 211                        {
 212                            _tasks.Remove(token.task);
 213                        }
 214                        break;
 215                    }
 216
 217                    _wakeUpTime = first.scheduledTime;
 218                    Monitor.Wait(_mutex, (int)(first.scheduledTime - now));
 219                }
 220
 221                if (_instance == null)
 222                {
 223                    break;
 224                }
 225            }
 226
 227            if (token != null)
 228            {
 229                try
 230                {
 231                    Ice.Instrumentation.ThreadObserver threadObserver = _observer;
 232                    if (threadObserver != null)
 233                    {
 234                        threadObserver.stateChanged(
 235                            Ice.Instrumentation.ThreadState.ThreadStateIdle,
 236                            Ice.Instrumentation.ThreadState.ThreadStateInUseForOther);
 237                        try
 238                        {
 239                            token.task.runTimerTask();
 240                        }
 241                        finally
 242                        {
 243                            threadObserver.stateChanged(
 244                                Ice.Instrumentation.ThreadState.ThreadStateInUseForOther,
 245                                Ice.Instrumentation.ThreadState.ThreadStateIdle);
 246                        }
 247                    }
 248                    else
 249                    {
 250                        token.task.runTimerTask();
 251                    }
 252                }
 253                catch (System.Exception ex)
 254                {
 255                    lock (_mutex)
 256                    {
 257                        if (_instance != null)
 258                        {
 259                            string s = "unexpected exception from task run method in timer thread:\n" + ex;
 260                            _instance.initializationData().logger.error(s);
 261                        }
 262                    }
 263                }
 264            }
 265        }
 266    }
 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
 323    private readonly object _mutex = new object();
 324    private readonly IDictionary<Token, object> _tokens = new SortedDictionary<Token, object>();
 325    private readonly IDictionary<TimerTask, Token> _tasks = new Dictionary<TimerTask, Token>();
 326    private Instance _instance;
 327    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}