Oops, fix a scale problem with the RSS size calculation
[beagle.git] / beagled / IndexHelper / IndexHelper.cs
blob16e5a5546c53b5e44c08f0d2b30dc84e8f6d9487
2 // IndexHelper.cs
3 //
4 // Copyright (C) 2005 Novell, Inc.
5 //
7 //
8 // Permission is hereby granted, free of charge, to any person obtaining a copy
9 // of this software and associated documentation files (the "Software"), to deal
10 // in the Software without restriction, including without limitation the rights
11 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 // copies of the Software, and to permit persons to whom the Software is
13 // furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in all
16 // copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 // SOFTWARE.
28 using System;
29 using System.Collections;
30 using System.IO;
31 using SNS = System.Net.Sockets;
32 using System.Runtime.InteropServices;
33 using System.Threading;
35 using GLib;
37 using Beagle.Daemon;
38 using Beagle.Util;
39 using Log = Beagle.Util.Log;
40 using Thread = System.Threading.Thread;
42 namespace Beagle.IndexHelper {
44 class IndexHelperTool {
45 private static MainLoop main_loop;
47 private static DateTime last_activity;
48 private static Server server;
50 [DllImport ("libc")]
51 extern static private int unsetenv (string name);
53 public static void Main (string [] args)
55 try {
56 DoMain (args);
57 } catch (Exception ex) {
58 Logger.Log.Error (ex, "Unhandled exception thrown. Exiting immediately.");
59 Environment.Exit (1);
63 [DllImport("libgobject-2.0.so.0")]
64 static extern void g_type_init ();
66 private static void DoMain (string [] args)
68 SystemInformation.SetProcessName ("beagled-helper");
70 bool run_by_hand = (Environment.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
71 bool log_in_fg = (Environment.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
73 // FIXME: We always turn on full debugging output! We are still
74 // debugging this code, after all...
75 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
77 last_activity = DateTime.Now;
79 Log.Initialize (PathFinder.LogDir,
80 "IndexHelper",
81 //debug ? LogLevel.Debug : LogLevel.Warn,
82 LogLevel.Debug,
83 run_by_hand || log_in_fg);
85 // Intentionally unset DISPLAY so that we can't connect
86 // to the X server and aren't influenced by it if it
87 // goes away. It's important to do this before
88 // Application.InitCheck(), since that's what makes the
89 // connection.
90 //unsetenv ("DISPLAY");
92 SystemInformation.XssInit (false);
94 // Initialize GObject type system
95 g_type_init ();
97 Server.Init ();
99 SetupSignalHandlers ();
101 Shutdown.ShutdownEvent += OnShutdown;
103 main_loop = new MainLoop ();
104 Shutdown.RegisterMainLoop (main_loop);
106 // Start the server
107 Logger.Log.Debug ("Starting messaging server");
108 bool server_has_been_started = false;
109 try {
110 server = new Server ("socket-helper");
111 server.Start ();
112 server_has_been_started = true;
113 } catch (InvalidOperationException ex) {
114 Logger.Log.Error (ex, "Couldn't start server. Exiting immediately.");
117 if (server_has_been_started) {
118 // Set the IO priority to idle so we don't slow down the system
119 if (Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null) {
120 IoPriority.ReduceIoPriority ();
122 int prio = Mono.Unix.Native.Syscall.nice (15);
124 if (prio < 0)
125 Log.Warn ("Unable to renice helper to +15");
126 else if (prio == 15)
127 Log.Debug ("Reniced helper to +15");
128 else
129 Log.Debug ("Helper was already niced to {0}, not renicing to +15", prio);
130 } else
131 Log.Info ("BEAGLE_EXERCISE_THE_DOG is set");
133 // Start the monitor thread, which keeps an eye on memory usage and idle time.
134 ExceptionHandlingThread.Start (new ThreadStart (MemoryAndIdleMonitorWorker));
136 // Start a thread that watches the daemon and begins a shutdown
137 // if it terminates.
138 ExceptionHandlingThread.Start (new ThreadStart (DaemonMonitorWorker));
140 // Start the main loop
141 main_loop.Run ();
143 ExceptionHandlingThread.JoinAllThreads ();
145 // If we placed our sockets in a temp directory, try to clean it up
146 // Note: this may fail because the daemon is still running
147 if (PathFinder.GetRemoteStorageDir (false) != PathFinder.StorageDir) {
148 try {
149 Directory.Delete (PathFinder.GetRemoteStorageDir (false));
150 } catch (IOException) { }
153 Log.Info ("Index helper process shut down cleanly.");
157 public static void ReportActivity ()
159 last_activity = DateTime.Now;
162 private static void MemoryAndIdleMonitorWorker ()
164 int vmrss_original = SystemInformation.VmRss;
166 const double max_idle_time = 30; // minutes
168 const double threshold = 5.0;
169 const int max_request_count = 0;
170 int last_vmrss = 0;
172 while (! Shutdown.ShutdownRequested) {
174 double idle_time;
175 idle_time = (DateTime.Now - last_activity).TotalMinutes;
176 if (idle_time > max_idle_time && RemoteIndexerExecutor.Count > 0) {
177 Logger.Log.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time);
178 Shutdown.BeginShutdown ();
179 return;
182 // Check resident memory usage
183 int vmrss = SystemInformation.VmRss;
184 double size = vmrss / (double) vmrss_original;
185 if (vmrss != last_vmrss)
186 Logger.Log.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
187 vmrss/1024.0, size, 100.0 * (size - 1) / (threshold - 1));
188 last_vmrss = vmrss;
189 if (size > threshold
190 || (max_request_count > 0 && RemoteIndexerExecutor.Count > max_request_count)) {
191 if (RemoteIndexerExecutor.Count > 0) {
192 Logger.Log.Debug ("Process too big, shutting down!");
193 Shutdown.BeginShutdown ();
194 return;
195 } else {
196 // Paranoia: don't shut down if we haven't done anything yet
197 Logger.Log.Debug ("Deferring shutdown until we've actually done something.");
198 Thread.Sleep (1000);
200 } else {
201 Thread.Sleep (3000);
206 private static void DaemonMonitorWorker ()
208 string storage_dir = PathFinder.GetRemoteStorageDir (false);
210 if (storage_dir == null) {
211 Logger.Log.Debug ("The daemon doesn't appear to have started");
212 Logger.Log.Debug ("Shutting down helper.");
213 Shutdown.BeginShutdown ();
214 return;
217 // FIXME: We shouldn't need to know the name of the daemon's socket.
218 string socket_name;
219 socket_name = Path.Combine (storage_dir, "socket");
221 try {
222 SNS.Socket socket;
223 socket = new SNS.Socket (SNS.AddressFamily.Unix, SNS.SocketType.Stream, 0);
224 socket.Connect (new Mono.Unix.UnixEndPoint (socket_name));
226 ArrayList socket_list = new ArrayList ();
228 while (! Shutdown.ShutdownRequested) {
229 socket_list.Add (socket);
230 SNS.Socket.Select (socket_list, null, null, 1000000); // 1000000 microseconds = 1 second
231 if (socket_list.Count != 0) {
232 Logger.Log.Debug ("The daemon appears to have gone away.");
233 Logger.Log.Debug ("Shutting down helper.");
234 Shutdown.BeginShutdown ();
237 } catch (SNS.SocketException) {
238 Logger.Log.Debug ("Caught a SocketException while trying to monitor the daemon");
239 Logger.Log.Debug ("Shutting down");
240 Shutdown.BeginShutdown ();
244 /////////////////////////////////////////////////////////////////////////////
246 private static void SetupSignalHandlers ()
248 // Force OurSignalHandler to be JITed
249 OurSignalHandler (-1);
251 // Set up our signal handler
252 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGINT, OurSignalHandler);
253 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGTERM, OurSignalHandler);
254 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGUSR1, OurSignalHandler);
256 // Ignore SIGPIPE
257 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGPIPE, Mono.Unix.Native.Stdlib.SIG_IGN);
260 // Our handler triggers an orderly shutdown when it receives a signal.
261 // However, this can be annoying if the process gets wedged during
262 // shutdown. To deal with that case, we make a note of the time when
263 // the first signal comes in, and we allow signals to unconditionally
264 // kill the process after 5 seconds have passed.
265 private static DateTime signal_time = DateTime.MinValue;
266 private static void OurSignalHandler (int signal)
268 // This allows us to call OurSignalHandler w/o doing anything.
269 // We want to call it once to ensure that it is pre-JITed.
270 if (signal < 0)
271 return;
273 // Set shutdown flag to true so that other threads can stop initializing
274 if ((Mono.Unix.Native.Signum) signal != Mono.Unix.Native.Signum.SIGUSR1)
275 Shutdown.ShutdownRequested = true;
277 // Do all signal handling work in the main loop and not in the signal handler.
278 GLib.Idle.Add (new GLib.IdleHandler (delegate () { HandleSignal (signal); return false; }));
281 private static void HandleSignal (int signal)
283 Logger.Log.Debug ("Handling signal {0} ({1})", signal, (Mono.Unix.Native.Signum) signal);
285 // If we get SIGUSR1, turn the debugging level up.
286 if ((Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR1) {
287 LogLevel old_level = Log.Level;
288 Log.Level = LogLevel.Debug;
289 Log.Debug ("Moving from log level {0} to Debug", old_level);
290 return;
293 Logger.Log.Debug ("Initiating shutdown in response to signal.");
294 Shutdown.BeginShutdown ();
297 private static void OnShutdown ()
299 if (server != null)
300 server.Stop ();