2004-01-16 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / System / System.IO / FAMWatcher.cs
1 // 
2 // System.IO.FAM.cs: interface with libfam
3 //
4 // Authors:
5 //      Gonzalo Paniagua Javier (gonzalo@ximian.com)
6 //
7 // (c) 2004 Novell, Inc. (http://www.novell.com)
8 //
9
10 using System;
11 using System.Collections;
12 using System.ComponentModel;
13 using System.Runtime.CompilerServices;
14 using System.Runtime.InteropServices;
15 using System.Text;
16 using System.Threading;
17
18 namespace System.IO {
19         struct FAMConnection {
20                 public int FD;
21                 IntPtr opaque;
22         }
23
24         struct FAMRequest {
25                 public int ReqNum;
26         }
27
28         enum FAMCodes {
29                 Changed = 1,
30                 Deleted = 2,
31                 StartExecuting = 3, 
32                 StopExecuting = 4,
33                 Created = 5,
34                 Moved = 6, 
35                 Acknowledge = 7,
36                 Exists = 8,
37                 EndExist = 9
38         };
39
40         class FAMData {
41                 public FileSystemWatcher FSW;
42                 public string Directory;
43                 public string FileMask;
44                 public bool IncludeSubdirs;
45                 public bool Enabled;
46                 public FAMRequest Request;
47         }
48
49         class FAMWatcher : IFileWatcher
50         {
51                 static bool failed;
52                 static FAMWatcher instance;
53                 static Hashtable watches;
54                 static Hashtable requests;
55                 static FAMConnection conn;
56                 static Thread thread;
57                 static bool stop;
58                 
59                 private FAMWatcher ()
60                 {
61                 }
62                 
63                 public static bool GetInstance (out IFileWatcher watcher)
64                 {
65                         lock (typeof (FAMWatcher)) {
66                                 if (failed == true) {
67                                         watcher = null;
68                                         return false;
69                                 }
70
71                                 if (instance != null) {
72                                         watcher = instance;
73                                         return true;
74                                 }
75
76                                 watches = new Hashtable ();
77                                 requests = new Hashtable ();
78                                 if (FAMOpen (out conn) == -1) {
79                                         failed = true;
80                                         watcher = null;
81                                         return false;
82                                 }
83
84                                 instance = new FAMWatcher ();
85                                 watcher = instance;
86                                 return true;
87                         }
88                 }
89                 
90                 public void StartDispatching (FileSystemWatcher fsw)
91                 {
92                         FAMData data;
93                         lock (this) {
94                                 if (thread == null) {
95                                         thread = new Thread (new ThreadStart (Monitor));
96                                         thread.IsBackground = true;
97                                         thread.Start ();
98                                 }
99
100                                 data = (FAMData) watches [fsw];
101                         }
102
103                         if (data == null) {
104                                 data = new FAMData ();
105                                 data.FSW = fsw;
106                                 data.Directory = fsw.FullPath;
107                                 data.FileMask = fsw.Filter;
108                                 data.IncludeSubdirs = fsw.IncludeSubdirectories;
109                                 data.Enabled = true;
110                                 lock (this) {
111                                         StartMonitoringDirectory (data);
112                                         watches [fsw] = data;
113                                         requests [data.Request.ReqNum] = data;
114                                 }
115                         }
116                 }
117
118                 static void StartMonitoringDirectory (FAMData data)
119                 {
120                         FAMRequest fr;
121                         if (FAMMonitorDirectory (ref conn, data.Directory, out fr, IntPtr.Zero) == -1)
122                                 throw new Win32Exception ();
123
124                         data.Request = fr;
125                 }
126
127                 public void StopDispatching (FileSystemWatcher fsw)
128                 {
129                         FAMData data;
130                         lock (this) {
131                                 data = (FAMData) watches [fsw];
132                                 if (data == null)
133                                         return;
134
135                                 StopMonitoringDirectory (data);
136                                 watches.Remove (fsw);
137                                 requests.Remove (data.Request.ReqNum);
138                                 if (watches.Count == 0)
139                                         stop = true;
140                         }
141                 }
142
143                 static void StopMonitoringDirectory (FAMData data)
144                 {
145                         FAMRequest fr;
146                         if (FAMCancelMonitor (ref conn, ref data.Request) == -1)
147                                 throw new Win32Exception ();
148                 }
149
150                 void Monitor ()
151                 {
152                         while (!stop) {
153                                 int haveEvents;
154                                 lock (this) {
155                                         haveEvents = FAMPending (ref conn);
156                                 }
157
158                                 if (haveEvents > 0) {
159                                         ProcessEvents ();
160                                 } else {
161                                         Thread.Sleep (500);
162                                 }
163                         }
164
165                         lock (this) {
166                                 thread = null;
167                                 stop = false;
168                         }
169                 }
170
171                 const NotifyFilters changed =   NotifyFilters.Attributes |
172                                                 NotifyFilters.LastAccess |
173                                                 NotifyFilters.Size      |
174                                                 NotifyFilters.LastWrite;
175
176                 void ProcessEvents ()
177                 {
178                         lock (this) {
179                                 do {
180                                         int code;
181                                         string filename;
182                                         int requestNumber;
183                                         FileSystemWatcher fsw;
184
185                                         if (InternalFAMNextEvent (ref conn, out filename,
186                                                                   out code, out requestNumber) != 1)
187                                                 return;
188
189                                         bool found = false;
190                                         switch ((FAMCodes) code) {
191                                         case FAMCodes.Changed:
192                                         case FAMCodes.Deleted:
193                                         case FAMCodes.Created:
194                                                 found = requests.ContainsKey (requestNumber);
195                                                 break;
196                                         case FAMCodes.Moved:
197                                         case FAMCodes.StartExecuting:
198                                         case FAMCodes.StopExecuting:
199                                         case FAMCodes.Acknowledge:
200                                         case FAMCodes.Exists:
201                                         case FAMCodes.EndExist:
202                                         default:
203                                                 found = false;
204                                                 break;
205                                         }
206
207                                         if (!found)
208                                                 continue;
209                                         
210                                         FAMData data = (FAMData) requests [requestNumber];
211                                         if (!data.Enabled)
212                                                 continue;
213
214                                         fsw = data.FSW;
215                                         NotifyFilters flt = fsw.NotifyFilter;
216                                         RenamedEventArgs renamed = null;
217                                         FileAction fa = 0;
218                                         if (code == (int) FAMCodes.Changed && (flt & changed) != 0)
219                                                 fa = FileAction.Modified;
220                                         else if (code == (int) FAMCodes.Deleted)
221                                                 fa = FileAction.Removed;
222                                         else if (code == (int) FAMCodes.Created)
223                                                 fa = FileAction.Added;
224                                         
225                                         if (fa != 0) {
226                                                 if (filename != data.Directory && !fsw.Pattern.IsMatch (filename))
227                                                         continue;
228
229                                                 lock (fsw) {
230                                                         fsw.DispatchEvents (fa, filename, ref renamed);
231                                                         if (fsw.Waiting) {
232                                                                 fsw.Waiting = false;
233                                                                 System.Threading.Monitor.PulseAll (fsw);
234                                                         }
235                                                 }
236                                         }
237
238                                 } while (FAMPending (ref conn) > 0);
239                         }
240                 }
241                 
242                 [DllImport ("fam")]
243                 extern static int FAMOpen (out FAMConnection fc);
244
245                 [DllImport ("fam")]
246                 extern static int FAMClose (ref FAMConnection fc);
247
248                 [DllImport ("fam")]
249                 extern static int FAMMonitorDirectory (ref FAMConnection fc, string filename,
250                                                         out FAMRequest fr, IntPtr user_data);
251
252                 [DllImport ("fam")]
253                 extern static int FAMCancelMonitor (ref FAMConnection fc, ref FAMRequest fr);
254
255                 [MethodImplAttribute(MethodImplOptions.InternalCall)]
256                 extern static int InternalFAMNextEvent (ref FAMConnection fc, out string filename,
257                                                         out int code, out int reqnum);
258
259                 [DllImport ("fam")]
260                 extern static int FAMPending (ref FAMConnection fc);
261         }
262 }
263