1 //
2 // Mono.Unix/UnixSignal.cs
3 //
4 // Authors:
5 //   Jonathan Pryor (jpryor@novell.com)
6 //   Tim Jenks (tim.jenks@realtimeworlds.com)
7 //
8 // (C) 2008 Novell, Inc.
9 //
10 // Permission is hereby granted, free of charge, to any person obtaining
11 // a copy of this software and associated documentation files (the
12 // "Software"), to deal in the Software without restriction, including
13 // without limitation the rights to use, copy, modify, merge, publish,
14 // distribute, sublicense, and/or sell copies of the Software, and to
15 // permit persons to whom the Software is furnished to do so, subject to
16 // the following conditions:
17 //
18 // The above copyright notice and this permission notice shall be
19 // included in all copies or substantial portions of the Software.
20 //
21 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 //
29 
30 using System;
31 using System.Runtime.InteropServices;
32 using System.Threading;
33 
34 using Mono.Unix.Native;
35 
36 namespace Mono.Unix {
37 
38 	public class UnixSignal : WaitHandle {
39 		private int signum;
40 		private IntPtr signal_info;
41 
UnixSignal()42 		static UnixSignal ()
43 		{
44 			Stdlib.VersionCheck ();
45 		}
46 
UnixSignal(Signum signum)47 		public UnixSignal (Signum signum)
48 		{
49 			this.signum = NativeConvert.FromSignum (signum);
50 			this.signal_info = install (this.signum);
51 			if (this.signal_info == IntPtr.Zero) {
52 				throw new ArgumentException ("Unable to handle signal", "signum");
53 			}
54 		}
55 
UnixSignal(Mono.Unix.Native.RealTimeSignum rtsig)56 		public UnixSignal (Mono.Unix.Native.RealTimeSignum rtsig)
57 		{
58 			signum = NativeConvert.FromRealTimeSignum (rtsig);
59 			this.signal_info = install (this.signum);
60 			Native.Errno err = Native.Stdlib.GetLastError ();
61 			if (this.signal_info == IntPtr.Zero) {
62 				if (err == Native.Errno.EADDRINUSE)
63 					throw new ArgumentException ("Signal registered outside of Mono.Posix", "signum");
64 				throw new ArgumentException ("Unable to handle signal", "signum");
65 			}
66 		}
67 
68 		public Signum Signum {
69 			get {
70 				if (IsRealTimeSignal)
71 					throw new InvalidOperationException ("This signal is a RealTimeSignum");
72 				return NativeConvert.ToSignum (signum);
73 			}
74 		}
75 
76 		public RealTimeSignum RealTimeSignum {
77 			get {
78 				if (!IsRealTimeSignal)
79 					throw new InvalidOperationException ("This signal is not a RealTimeSignum");
80 				return NativeConvert.ToRealTimeSignum (signum-GetSIGRTMIN ());
81 			}
82 		}
83 
84 		public bool IsRealTimeSignal {
85 			get {
86 				AssertValid ();
87 				int sigrtmin = GetSIGRTMIN ();
88 				if (sigrtmin == -1)
89 					return false;
90 				return signum >= sigrtmin;
91 			}
92 		}
93 
94 		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
95 				EntryPoint="Mono_Unix_UnixSignal_install", SetLastError=true)]
install(int signum)96 		private static extern IntPtr install (int signum);
97 
98 		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
99 				EntryPoint="Mono_Unix_UnixSignal_uninstall")]
uninstall(IntPtr info)100 		private static extern int uninstall (IntPtr info);
101 
102 		[UnmanagedFunctionPointer (CallingConvention.Cdecl)]
Mono_Posix_RuntimeIsShuttingDown()103 		delegate int Mono_Posix_RuntimeIsShuttingDown ();
104 		static Mono_Posix_RuntimeIsShuttingDown ShuttingDown = RuntimeShuttingDownCallback;
105 
RuntimeShuttingDownCallback()106 		static int RuntimeShuttingDownCallback ()
107 		{
108 			return Environment.HasShutdownStarted ? 1 : 0;
109 		}
110 
111 		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
112 				EntryPoint="Mono_Unix_UnixSignal_WaitAny")]
WaitAny(IntPtr[] infos, int count, int timeout, Mono_Posix_RuntimeIsShuttingDown shutting_down)113 		private static extern int WaitAny (IntPtr[] infos, int count, int timeout, Mono_Posix_RuntimeIsShuttingDown shutting_down);
114 
115 		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
116                                 EntryPoint="Mono_Posix_SIGRTMIN")]
GetSIGRTMIN()117 		internal static extern int GetSIGRTMIN ();
118 
119 		[DllImport (Stdlib.MPH, CallingConvention=CallingConvention.Cdecl,
120                                 EntryPoint="Mono_Posix_SIGRTMAX")]
GetSIGRTMAX()121 		internal static extern int GetSIGRTMAX ();
122 
AssertValid()123 		private void AssertValid ()
124 		{
125 			if (signal_info == IntPtr.Zero)
126 				throw new ObjectDisposedException (GetType().FullName);
127 		}
128 
129 		private unsafe SignalInfo* Info {
130 			get {
131 				AssertValid ();
132 				return (SignalInfo*) signal_info;
133 			}
134 		}
135 
136 		public bool IsSet {
137 			get {
138 				return Count > 0;
139 			}
140 		}
141 
Reset()142 		public unsafe bool Reset ()
143 		{
144 			int n = Interlocked.Exchange (ref Info->count, 0);
145 			return n != 0;
146 		}
147 
148 		public unsafe int Count {
149 			get {return Info->count;}
150 			set {Interlocked.Exchange (ref Info->count, value);}
151 		}
152 
153 		// signum, count, write_fd, pipecnt, and pipelock are read from a signal handler thread
154 		// count and pipelock are both read and written from the signal handler thread
155 #pragma warning disable 649
156 		[Map]
157 		struct SignalInfo {
158 			public int signum, count, read_fd, write_fd, pipecnt, pipelock, have_handler;
159 			public IntPtr handler; // Backed-up handler to restore when signal unregistered
160 		}
161 #pragma warning restore 649
162 
163 		#region WaitHandle overrides
Dispose(bool disposing)164 		protected unsafe override void Dispose (bool disposing)
165 		{
166 			base.Dispose (disposing);
167 			if (signal_info == IntPtr.Zero)
168 				return;
169 			uninstall (signal_info);
170 			signal_info = IntPtr.Zero;
171 		}
172 
WaitOne()173 		public override bool WaitOne ()
174 		{
175 			return WaitOne (-1, false);
176 		}
177 
WaitOne(TimeSpan timeout, bool exitContext)178 		public override bool WaitOne (TimeSpan timeout, bool exitContext)
179 		{
180 			long ms = (long) timeout.TotalMilliseconds;
181 			if (ms < -1 || ms > Int32.MaxValue)
182 				throw new ArgumentOutOfRangeException ("timeout");
183 			return WaitOne ((int) ms, exitContext);
184 		}
185 
WaitOne(int millisecondsTimeout, bool exitContext)186 		public override bool WaitOne (int millisecondsTimeout, bool exitContext)
187 		{
188 			AssertValid ();
189 			if (exitContext)
190 				throw new InvalidOperationException ("exitContext is not supported");
191 			if (millisecondsTimeout == 0)
192 				return IsSet;
193 			return WaitAny (new UnixSignal[]{this}, millisecondsTimeout) == 0;
194 		}
195 		#endregion
196 
WaitAny(UnixSignal[] signals)197 		public static int WaitAny (UnixSignal[] signals)
198 		{
199 			return WaitAny (signals, -1);
200 		}
201 
WaitAny(UnixSignal[] signals, TimeSpan timeout)202 		public static int WaitAny (UnixSignal[] signals, TimeSpan timeout)
203 		{
204 			long ms = (long) timeout.TotalMilliseconds;
205 			if (ms < -1 || ms > Int32.MaxValue)
206 				throw new ArgumentOutOfRangeException ("timeout");
207 			return WaitAny (signals, (int) ms);
208 		}
209 
210 
WaitAny(UnixSignal[] signals, int millisecondsTimeout)211 		public static unsafe int WaitAny (UnixSignal[] signals, int millisecondsTimeout)
212 		{
213 			if (signals == null)
214 				throw new ArgumentNullException ("signals");
215 			if (millisecondsTimeout < -1)
216 				throw new ArgumentOutOfRangeException ("millisecondsTimeout");
217 			IntPtr[] infos = new IntPtr [signals.Length];
218 			for (int i = 0; i < signals.Length; ++i) {
219 				infos [i] = signals [i].signal_info;
220 				if (infos [i] == IntPtr.Zero)
221 					throw new InvalidOperationException ("Disposed UnixSignal");
222 			}
223 			return WaitAny (infos, infos.Length, millisecondsTimeout, ShuttingDown);
224 		}
225 	}
226 }
227 
228