Sorry for the combined patch. The changes became too inter-dependent.
[beagle.git] / beagled / IndexHelper / IndexHelper.cs
blobb419f6ffc2a3acc6def1dccdfe056ae91e44ee54
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 ("Unhandled exception thrown. Exiting immediately.");
59 Logger.Log.Error (ex);
60 Environment.Exit (1);
64 private static void DoMain (string [] args)
66 SystemInformation.SetProcessName ("beagled-helper");
68 bool run_by_hand = (Environment.GetEnvironmentVariable ("BEAGLE_RUN_HELPER_BY_HAND") != null);
69 bool log_in_fg = (Environment.GetEnvironmentVariable ("BEAGLE_LOG_IN_THE_FOREGROUND_PLEASE") != null);
71 // FIXME: We always turn on full debugging output! We are still
72 // debugging this code, after all...
73 //bool debug = (Environment.GetEnvironmentVariable ("BEAGLE_DEBUG_FLAG_IS_SET") != null);
75 last_activity = DateTime.Now;
77 Log.Initialize (PathFinder.LogDir,
78 "IndexHelper",
79 //debug ? LogLevel.Debug : LogLevel.Warn,
80 LogLevel.Debug,
81 run_by_hand || log_in_fg);
83 // Intentionally unset DISPLAY so that we can't connect
84 // to the X server and aren't influenced by it if it
85 // goes away. It's important to do this before
86 // Application.InitCheck(), since that's what makes the
87 // connection.
88 //unsetenv ("DISPLAY");
90 SystemInformation.XssInit (false);
92 SetupSignalHandlers ();
94 Shutdown.ShutdownEvent += OnShutdown;
96 main_loop = new MainLoop ();
97 Shutdown.RegisterMainLoop (main_loop);
99 // Start the server
100 Logger.Log.Debug ("Starting messaging server");
101 bool server_has_been_started = false;
102 try {
103 server = new Server ("socket-helper");
104 server.Start ();
105 server_has_been_started = true;
106 } catch (InvalidOperationException ex) {
107 Logger.Log.Error ("Couldn't start server:");
108 Logger.Log.Error (ex);
111 if (server_has_been_started) {
112 // Set the IO priority to idle so we don't slow down the system
113 if (Environment.GetEnvironmentVariable ("BEAGLE_EXERCISE_THE_DOG") == null) {
114 if (! IoPriority.SetIdle ())
115 IoPriority.SetIoPriority (7);
118 // Start the monitor thread, which keeps an eye on memory usage and idle time.
119 ExceptionHandlingThread.Start (new ThreadStart (MemoryAndIdleMonitorWorker));
121 // Start a thread that watches the daemon and begins a shutdown
122 // if it terminates.
123 ExceptionHandlingThread.Start (new ThreadStart (DaemonMonitorWorker));
125 //Application.Run ();
126 main_loop.Run ();
128 // If we palced our sockets in a temp directory, try to clean it up
129 // Note: this may fail because the daemon is still running
130 if (PathFinder.GetRemoteStorageDir (false) != PathFinder.StorageDir) {
131 try {
132 Directory.Delete (PathFinder.GetRemoteStorageDir (false));
133 } catch (IOException) { }
138 public static void ReportActivity ()
140 last_activity = DateTime.Now;
143 private static void MemoryAndIdleMonitorWorker ()
145 int vmrss_original = SystemInformation.VmRss;
147 const double max_idle_time = 30; // minutes
149 const double threshold = 5.0;
150 const int max_request_count = 0;
151 int last_vmrss = 0;
153 while (! Shutdown.ShutdownRequested) {
155 double idle_time;
156 idle_time = (DateTime.Now - last_activity).TotalMinutes;
157 if (idle_time > max_idle_time && RemoteIndexerExecutor.Count > 0) {
158 Logger.Log.Debug ("No activity for {0:0.0} minutes, shutting down", idle_time);
159 Shutdown.BeginShutdown ();
160 return;
163 // Check resident memory usage
164 int vmrss = SystemInformation.VmRss;
165 double size = vmrss / (double) vmrss_original;
166 if (vmrss != last_vmrss)
167 Logger.Log.Debug ("Helper Size: VmRSS={0:0.0} MB, size={1:0.00}, {2:0.0}%",
168 vmrss/1024.0, size, 100.0 * (size - 1) / (threshold - 1));
169 last_vmrss = vmrss;
170 if (size > threshold
171 || (max_request_count > 0 && RemoteIndexerExecutor.Count > max_request_count)) {
172 if (RemoteIndexerExecutor.Count > 0) {
173 Logger.Log.Debug ("Process too big, shutting down!");
174 Shutdown.BeginShutdown ();
175 return;
176 } else {
177 // Paranoia: don't shut down if we haven't done anything yet
178 Logger.Log.Debug ("Deferring shutdown until we've actually done something.");
179 Thread.Sleep (1000);
181 } else {
182 Thread.Sleep (3000);
187 private static void DaemonMonitorWorker ()
189 string storage_dir = PathFinder.GetRemoteStorageDir (false);
191 if (storage_dir == null) {
192 Logger.Log.Debug ("The daemon doesn't appear to have started");
193 Logger.Log.Debug ("Shutting down helper.");
194 Shutdown.BeginShutdown ();
195 return;
198 // FIXME: We shouldn't need to know the name of the daemon's socket.
199 string socket_name;
200 socket_name = Path.Combine (storage_dir, "socket");
202 try {
203 SNS.Socket socket;
204 socket = new SNS.Socket (SNS.AddressFamily.Unix, SNS.SocketType.Stream, 0);
205 socket.Connect (new Mono.Unix.UnixEndPoint (socket_name));
207 ArrayList socket_list = new ArrayList ();
209 while (! Shutdown.ShutdownRequested) {
210 socket_list.Add (socket);
211 SNS.Socket.Select (socket_list, null, null, 1000000); // 1000000 microseconds = 1 second
212 if (socket_list.Count != 0) {
213 Logger.Log.Debug ("The daemon appears to have gone away.");
214 Logger.Log.Debug ("Shutting down helper.");
215 Shutdown.BeginShutdown ();
218 } catch (SNS.SocketException) {
219 Logger.Log.Debug ("Caught a SocketException while trying to monitor the daemon");
220 Logger.Log.Debug ("Shutting down");
221 Shutdown.BeginShutdown ();
225 /////////////////////////////////////////////////////////////////////////////
227 private static void SetupSignalHandlers ()
229 // Force OurSignalHandler to be JITed
230 OurSignalHandler (-1);
232 // Set up our signal handler
233 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGINT, OurSignalHandler);
234 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGTERM, OurSignalHandler);
235 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGUSR1, OurSignalHandler);
237 // Ignore SIGPIPE
238 Mono.Unix.Native.Stdlib.signal (Mono.Unix.Native.Signum.SIGPIPE, Mono.Unix.Native.Stdlib.SIG_IGN);
241 // Our handler triggers an orderly shutdown when it receives a signal.
242 // However, this can be annoying if the process gets wedged during
243 // shutdown. To deal with that case, we make a note of the time when
244 // the first signal comes in, and we allow signals to unconditionally
245 // kill the process after 5 seconds have passed.
246 private static DateTime signal_time = DateTime.MinValue;
247 private static void OurSignalHandler (int signal)
249 // This allows us to call OurSignalHandler w/o doing anything.
250 // We want to call it once to ensure that it is pre-JITed.
251 if (signal < 0)
252 return;
254 // Set shutdown flag to true so that other threads can stop initializing
255 if ((Mono.Unix.Native.Signum) signal != Mono.Unix.Native.Signum.SIGUSR1)
256 Shutdown.ShutdownRequested = true;
258 // Do all signal handling work in the main loop and not in the signal handler.
259 GLib.Idle.Add (new GLib.IdleHandler (delegate () { HandleSignal (signal); return false; }));
262 private static void HandleSignal (int signal)
264 Logger.Log.Debug ("Handling signal {0} ({1})", signal, (Mono.Unix.Native.Signum) signal);
266 // If we get SIGUSR1, turn the debugging level up.
267 if ((Mono.Unix.Native.Signum) signal == Mono.Unix.Native.Signum.SIGUSR1) {
268 LogLevel old_level = Log.Level;
269 Log.Level = LogLevel.Debug;
270 Log.Debug ("Moving from log level {0} to Debug", old_level);
271 return;
274 Logger.Log.Debug ("Initiating shutdown in response to signal.");
275 Shutdown.BeginShutdown ();
278 private static void OnShutdown ()
280 if (server != null)
281 server.Stop ();