1 /*
2 * Copyright 2011, Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Clemens Zeidler <haiku@clemens-zeidler.de>
7 */
8
9 #include "FileMonitor.h"
10
11 #include <Looper.h>
12
13 #include <Messenger.h>
14 #include <NodeMonitor.h>
15
16
FileMonitor(EntryViewInterface * listener)17 FileMonitor::FileMonitor(EntryViewInterface* listener)
18 :
19 fListener(listener),
20 fCurrentReadList(NULL),
21 fCurrentReadIndex(0)
22 {
23
24 }
25
26
~FileMonitor()27 FileMonitor::~FileMonitor()
28 {
29 Reset();
30 }
31
32
33 void
SetReadThread(ReadThread * readThread)34 FileMonitor::SetReadThread(ReadThread* readThread)
35 {
36 fReadThread = readThread;
37 }
38
39
40 void
Reset()41 FileMonitor::Reset()
42 {
43 fWatchedFileList.clear();
44 stop_watching(this);
45
46 BMessenger messenger(this);
47 messenger.SendMessage(kMsgCleared);
48
49 if (fCurrentReadList != NULL)
50 fCurrentReadIndex = fCurrentReadList->size();
51 }
52
53
54 void
MessageReceived(BMessage * msg)55 FileMonitor::MessageReceived(BMessage* msg)
56 {
57 switch (msg->what) {
58 case kMsgAddRefs:
59 {
60 if (fCurrentReadList == NULL)
61 fCurrentReadList = fReadThread->ReadRefList();
62 uint32 terminate = fCurrentReadIndex + 50;
63 for (; fCurrentReadIndex < terminate; fCurrentReadIndex++) {
64 if (fCurrentReadIndex >= fCurrentReadList->size()) {
65 fCurrentReadList = NULL;
66 fCurrentReadIndex = 0;
67 fReadThread->ReadDone();
68 break;
69 }
70
71 entry_ref& entry = (*fCurrentReadList)[fCurrentReadIndex];
72 node_ref nodeRef;
73 BNode node(&entry);
74 if (node.GetNodeRef(&nodeRef) != B_OK)
75 continue;
76
77 EntryCreated(entry.name, entry.directory, entry.device,
78 nodeRef.node);
79 }
80 if (fCurrentReadList)
81 Looper()->PostMessage(kMsgAddRefs, this);
82
83 break;
84 }
85
86 case kMsgCleared:
87 fListener->EntriesCleared();
88 break;
89
90 default:
91 NodeMonitorHandler::MessageReceived(msg);
92 break;
93 }
94 }
95
96
97 void
EntryCreated(const char * name,ino_t directory,dev_t device,ino_t node)98 FileMonitor::EntryCreated(const char *name, ino_t directory, dev_t device,
99 ino_t node)
100 {
101 WatchedFile file;
102 NodeMonitorHandler::make_node_ref(device, node, &file.node);
103 if (fWatchedFileList.find(file.node) != fWatchedFileList.end())
104 return;
105
106 NodeMonitorHandler::make_entry_ref(device, directory, name, &file.entry);
107 fWatchedFileList[file.node] = file;
108
109 watch_node(&file.node, B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this);
110 fListener->EntryCreated(&fWatchedFileList[file.node]);
111 }
112
113
114 void
EntryRemoved(const char * name,ino_t directory,dev_t device,ino_t node)115 FileMonitor::EntryRemoved(const char *name, ino_t directory, dev_t device,
116 ino_t node)
117 {
118 WatchedFile* file = _FindFile(device, node);
119 if (file == NULL)
120 return;
121
122 fListener->EntryRemoved(file);
123 fWatchedFileList.erase(file->node);
124 }
125
126
127 void
EntryMoved(const char * name,const char * fromName,ino_t fromDirectory,ino_t toDirectory,dev_t device,ino_t node,dev_t nodeDevice)128 FileMonitor::EntryMoved(const char *name, const char *fromName,
129 ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node,
130 dev_t nodeDevice)
131 {
132 WatchedFile* file = _FindFile(device, node);
133 if (file == NULL)
134 return;
135 NodeMonitorHandler::make_entry_ref(device, toDirectory, name, &file->entry);
136 NodeMonitorHandler::make_node_ref(device, node, &file->node);
137 fListener->EntryMoved(file);
138 }
139
140
141 void
StatChanged(ino_t node,dev_t device,int32 statFields)142 FileMonitor::StatChanged(ino_t node, dev_t device, int32 statFields)
143 {
144 WatchedFile* file = _FindFile(device, node);
145 if (file == NULL)
146 return;
147 fListener->StatChanged(file);
148 }
149
150
151 void
AttrChanged(ino_t node,dev_t device)152 FileMonitor::AttrChanged(ino_t node, dev_t device)
153 {
154 WatchedFile* file = _FindFile(device, node);
155 if (file == NULL)
156 return;
157 fListener->AttrChanged(file);
158 }
159
160
161 WatchedFile*
_FindFile(dev_t device,ino_t node)162 FileMonitor::_FindFile(dev_t device, ino_t node)
163 {
164 node_ref nodeRef;
165 NodeMonitorHandler::make_node_ref(device, node, &nodeRef);
166
167 WatchedFileList::iterator it = fWatchedFileList.find(nodeRef);
168 if (it == fWatchedFileList.end())
169 return NULL;
170
171 return &it->second;
172 }
173
174
175 int32
ReadThreadFunction(void * data)176 ReadThreadFunction(void *data)
177 {
178 ReadThread* that = (ReadThread*)data;
179 return that->Process();
180 }
181
182
ReadThread(FileMonitor * target)183 ReadThread::ReadThread(FileMonitor* target)
184 :
185 fTarget(target),
186 fReading(false),
187 fStopped(false),
188 fThreadId(-1),
189 fNReaded(0),
190 fRunning(false)
191 {
192 fWriteRefList = &fRefList1;
193 fReadRefList = &fRefList2;
194 }
195
196
197 status_t
Run()198 ReadThread::Run()
199 {
200 if (fThreadId >= 0)
201 return B_ERROR;
202
203 fStopped = false;
204 fThreadId = spawn_thread(ReadThreadFunction, "file reader", B_LOW_PRIORITY,
205 this);
206 fRunning = true;
207 status_t status = resume_thread(fThreadId);
208 if (status != B_OK)
209 fRunning = false;
210 return status;
211 }
212
213
214 bool
Running()215 ReadThread::Running()
216 {
217 return fRunning;
218 }
219
220
221 status_t
Wait()222 ReadThread::Wait()
223 {
224 status_t exitValue;
225 return wait_for_thread(fThreadId, &exitValue);
226 }
227
228
229 void
Stop()230 ReadThread::Stop()
231 {
232 fStopped = true;
233 }
234
235
236 bool
Stopped()237 ReadThread::Stopped()
238 {
239 return fStopped;
240 }
241
242
243 RefList*
ReadRefList()244 ReadThread::ReadRefList()
245 {
246 return fReadRefList;
247 }
248
249
250 void
ReadDone()251 ReadThread::ReadDone()
252 {
253 fReadRefList->clear();
254 // and release the list
255 fReading = false;
256
257 if (!fRunning && fWriteRefList->size() != 0) {
258 BMessenger messenger(fTarget);
259 _PublishEntrys(messenger);
260 }
261 }
262
263
264 int32
Process()265 ReadThread::Process()
266 {
267 BMessenger messenger(fTarget);
268
269 entry_ref entry;
270 while (ReadNextEntry(entry)) {
271 if (Stopped()) {
272 fWriteRefList->clear();
273 break;
274 }
275
276 fWriteRefList->push_back(entry);
277
278 fNReaded++;
279 if (fNReaded >= 50)
280 _PublishEntrys(messenger);
281 }
282
283 fRunning = false;
284
285 _PublishEntrys(messenger);
286
287 fThreadId = -1;
288 return B_OK;
289 }
290
291
292 void
_SwapLists()293 ReadThread::_SwapLists()
294 {
295 RefList* lastReadList = fReadRefList;
296 fReadRefList = fWriteRefList;
297 fWriteRefList = lastReadList;
298 }
299
300
301 void
_PublishEntrys(BMessenger & messenger)302 ReadThread::_PublishEntrys(BMessenger& messenger)
303 {
304 if (fReading || Stopped())
305 return;
306 _SwapLists();
307 fReading = true;
308 fNReaded = 0;
309 messenger.SendMessage(kMsgAddRefs);
310 }
311