aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Framework/Watchdog.cs
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Framework/Watchdog.cs183
1 files changed, 183 insertions, 0 deletions
diff --git a/OpenSim/Framework/Watchdog.cs b/OpenSim/Framework/Watchdog.cs
new file mode 100644
index 0000000..5d46905
--- /dev/null
+++ b/OpenSim/Framework/Watchdog.cs
@@ -0,0 +1,183 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using System.Threading;
31using log4net;
32
33namespace OpenSim.Framework
34{
35 /// <summary>
36 /// Manages launching threads and keeping watch over them for timeouts
37 /// </summary>
38 public static class Watchdog
39 {
40 /// <summary>Timer interval in milliseconds for the watchdog timer</summary>
41 const double WATCHDOG_INTERVAL_MS = 2500.0d;
42 /// <summary>Maximum timeout in milliseconds before a thread is considered dead</summary>
43 const int WATCHDOG_TIMEOUT_MS = 5000;
44
45 [System.Diagnostics.DebuggerDisplay("{Thread.Name}")]
46 private class ThreadWatchdogInfo
47 {
48 public Thread Thread;
49 public int LastTick;
50
51 public ThreadWatchdogInfo(Thread thread)
52 {
53 Thread = thread;
54 LastTick = Environment.TickCount & Int32.MaxValue;
55 }
56 }
57
58 /// <summary>
59 /// This event is called whenever a tracked thread is stopped or
60 /// has not called UpdateThread() in time
61 /// </summary>
62 /// <param name="thread">The thread that has been identified as dead</param>
63 /// <param name="lastTick">The last time this thread called UpdateThread()</param>
64 public delegate void WatchdogTimeout(Thread thread, int lastTick);
65
66 /// <summary>This event is called whenever a tracked thread is
67 /// stopped or has not called UpdateThread() in time</summary>
68 public static event WatchdogTimeout OnWatchdogTimeout;
69
70 private static readonly ILog m_log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
71 private static Dictionary<int, ThreadWatchdogInfo> m_threads;
72 private static System.Timers.Timer m_watchdogTimer;
73
74 static Watchdog()
75 {
76 m_threads = new Dictionary<int, ThreadWatchdogInfo>();
77 m_watchdogTimer = new System.Timers.Timer(WATCHDOG_INTERVAL_MS);
78 m_watchdogTimer.AutoReset = false;
79 m_watchdogTimer.Elapsed += WatchdogTimerElapsed;
80 m_watchdogTimer.Start();
81 }
82
83 /// <summary>
84 /// Start a new thread that is tracked by the watchdog timer
85 /// </summary>
86 /// <param name="start">The method that will be executed in a new thread</param>
87 /// <param name="name">A name to give to the new thread</param>
88 /// <param name="priority">Priority to run the thread at</param>
89 /// <param name="isBackground">True to run this thread as a background
90 /// thread, otherwise false</param>
91 /// <returns>The newly created Thread object</returns>
92 public static Thread StartThread(ThreadStart start, string name, ThreadPriority priority, bool isBackground)
93 {
94 Thread thread = new Thread(start);
95 thread.Name = name;
96 thread.Priority = priority;
97 thread.IsBackground = isBackground;
98 thread.Start();
99
100 return thread;
101 }
102
103 /// <summary>
104 /// Marks the current thread as alive
105 /// </summary>
106 public static void UpdateThread()
107 {
108 UpdateThread(Thread.CurrentThread.ManagedThreadId);
109 }
110
111 /// <summary>
112 /// Stops watchdog tracking on the current thread
113 /// </summary>
114 /// <returns>True if the thread was removed from the list of tracked
115 /// threads, otherwise false</returns>
116 public static bool RemoveThread()
117 {
118 return RemoveThread(Thread.CurrentThread.ManagedThreadId);
119 }
120
121 private static void AddThread(ThreadWatchdogInfo threadInfo)
122 {
123 m_log.Debug("[WATCHDOG]: Started tracking thread \"" + threadInfo.Thread.Name + "\" (ID " + threadInfo.Thread.ManagedThreadId + ")");
124
125 lock (m_threads)
126 m_threads.Add(threadInfo.Thread.ManagedThreadId, threadInfo);
127 }
128
129 private static bool RemoveThread(int threadID)
130 {
131 lock (m_threads)
132 return m_threads.Remove(threadID);
133 }
134
135 private static void UpdateThread(int threadID)
136 {
137 ThreadWatchdogInfo threadInfo;
138
139 // Although TryGetValue is not a thread safe operation, we use a try/catch here instead
140 // of a lock for speed. Adding/removing threads is a very rare operation compared to
141 // UpdateThread(), and a single UpdateThread() failure here and there won't break
142 // anything
143 try
144 {
145 if (m_threads.TryGetValue(threadID, out threadInfo))
146 threadInfo.LastTick = Environment.TickCount & Int32.MaxValue;
147 else
148 AddThread(new ThreadWatchdogInfo(Thread.CurrentThread));
149 }
150 catch { }
151 }
152
153 private static void WatchdogTimerElapsed(object sender, System.Timers.ElapsedEventArgs e)
154 {
155 WatchdogTimeout callback = OnWatchdogTimeout;
156
157 if (callback != null)
158 {
159 ThreadWatchdogInfo timedOut = null;
160
161 lock (m_threads)
162 {
163 int now = Environment.TickCount & Int32.MaxValue;
164
165 foreach (ThreadWatchdogInfo threadInfo in m_threads.Values)
166 {
167 if (threadInfo.Thread.ThreadState == ThreadState.Stopped || now - threadInfo.LastTick >= WATCHDOG_TIMEOUT_MS)
168 {
169 timedOut = threadInfo;
170 m_threads.Remove(threadInfo.Thread.ManagedThreadId);
171 break;
172 }
173 }
174 }
175
176 if (timedOut != null)
177 callback(timedOut.Thread, timedOut.LastTick);
178 }
179
180 m_watchdogTimer.Start();
181 }
182 }
183}