xref: /haiku/src/apps/musiccollection/FileMonitor.cpp (revision 5e96d7d537fbec23bad4ae9b4c8e7b02e769f0c6)
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 
17 FileMonitor::FileMonitor(EntryViewInterface* listener)
18 	:
19 	fListener(listener),
20 	fCurrentReadList(NULL),
21 	fCurrentReadIndex(0)
22 {
23 
24 }
25 
26 
27 FileMonitor::~FileMonitor()
28 {
29 	Reset();
30 }
31 
32 
33 void
34 FileMonitor::SetReadThread(ReadThread* readThread)
35 {
36 	fReadThread = readThread;
37 }
38 
39 
40 void
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
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 	}
93 }
94 
95 
96 void
97 FileMonitor::EntryCreated(const char *name, ino_t directory, dev_t device,
98 	ino_t node)
99 {
100 	WatchedFile file;
101 	NodeMonitorHandler::make_node_ref(device, node, &file.node);
102 	if (fWatchedFileList.find(file.node) != fWatchedFileList.end())
103 		return;
104 
105 	NodeMonitorHandler::make_entry_ref(device, directory, name, &file.entry);
106 	fWatchedFileList[file.node] = file;
107 
108 	watch_node(&file.node, B_WATCH_NAME | B_WATCH_STAT | B_WATCH_ATTR, this);
109 	fListener->EntryCreated(&fWatchedFileList[file.node]);
110 }
111 
112 
113 void
114 FileMonitor::EntryRemoved(const char *name, ino_t directory, dev_t device,
115 	ino_t node)
116 {
117 	WatchedFile* file = _FindFile(device, node);
118 	if (file == NULL)
119 		return;
120 
121 	fListener->EntryRemoved(file);
122 	fWatchedFileList.erase(file->node);
123 }
124 
125 
126 void
127 FileMonitor::EntryMoved(const char *name, const char *fromName,
128 	ino_t fromDirectory, ino_t toDirectory, dev_t device, ino_t node,
129 	dev_t nodeDevice)
130 {
131 	WatchedFile* file = _FindFile(device, node);
132 	if (file == NULL)
133 		return;
134 	NodeMonitorHandler::make_entry_ref(device, toDirectory, name, &file->entry);
135 	NodeMonitorHandler::make_node_ref(device, node, &file->node);
136 	fListener->EntryMoved(file);
137 }
138 
139 
140 void
141 FileMonitor::StatChanged(ino_t node, dev_t device, int32 statFields)
142 {
143 	WatchedFile* file = _FindFile(device, node);
144 	if (file == NULL)
145 		return;
146 	fListener->StatChanged(file);
147 }
148 
149 
150 void
151 FileMonitor::AttrChanged(ino_t node, dev_t device)
152 {
153 	WatchedFile* file = _FindFile(device, node);
154 	if (file == NULL)
155 		return;
156 	fListener->AttrChanged(file);
157 }
158 
159 
160 WatchedFile*
161 FileMonitor::_FindFile(dev_t device, ino_t node)
162 {
163 	node_ref nodeRef;
164 	NodeMonitorHandler::make_node_ref(device, node, &nodeRef);
165 
166 	WatchedFileList::iterator it = fWatchedFileList.find(nodeRef);
167 	if (it == fWatchedFileList.end())
168 		return NULL;
169 
170 	return &it->second;
171 }
172 
173 
174 int32
175 ReadThreadFunction(void *data)
176 {
177 	ReadThread* that = (ReadThread*)data;
178 	return that->Process();
179 }
180 
181 
182 ReadThread::ReadThread(FileMonitor* target)
183 	:
184 	fTarget(target),
185 	fReading(false),
186 	fStopped(false),
187 	fThreadId(-1),
188 	fNReaded(0),
189 	fRunning(false)
190 {
191 	fWriteRefList = &fRefList1;
192 	fReadRefList = &fRefList2;
193 }
194 
195 
196 status_t
197 ReadThread::Run()
198 {
199 	if (fThreadId >= 0)
200 		return B_ERROR;
201 
202 	fStopped = false;
203 	fThreadId = spawn_thread(ReadThreadFunction, "file reader", B_LOW_PRIORITY,
204 		this);
205 	fRunning = true;
206 	status_t status = resume_thread(fThreadId);
207 	if (status != B_OK)
208 		fRunning = false;
209 	return status;
210 }
211 
212 
213 bool
214 ReadThread::Running()
215 {
216 	return fRunning;
217 }
218 
219 
220 status_t
221 ReadThread::Wait()
222 {
223 	status_t exitValue;
224 	return wait_for_thread(fThreadId, &exitValue);
225 }
226 
227 
228 void
229 ReadThread::Stop()
230 {
231 	fStopped = true;
232 }
233 
234 
235 bool
236 ReadThread::Stopped()
237 {
238 	return fStopped;
239 }
240 
241 
242 RefList*
243 ReadThread::ReadRefList()
244 {
245 	return fReadRefList;
246 }
247 
248 
249 void
250 ReadThread::ReadDone()
251 {
252 	fReadRefList->clear();
253 	// and release the list
254 	fReading = false;
255 
256 	if (!fRunning && fWriteRefList->size() != 0) {
257 		BMessenger messenger(fTarget);
258 		_PublishEntrys(messenger);
259 	}
260 }
261 
262 
263 int32
264 ReadThread::Process()
265 {
266 	BMessenger messenger(fTarget);
267 
268 	entry_ref entry;
269 	while (ReadNextEntry(entry)) {
270 		if (Stopped()) {
271 			fWriteRefList->clear();
272 			break;
273 		}
274 
275 		fWriteRefList->push_back(entry);
276 
277 		fNReaded++;
278 		if (fNReaded >= 50)
279 			_PublishEntrys(messenger);
280 	}
281 
282 	fRunning = false;
283 
284 	_PublishEntrys(messenger);
285 
286 	fThreadId = -1;
287 	return B_OK;
288 }
289 
290 
291 void
292 ReadThread::_SwapLists()
293 {
294 	RefList* lastReadList = fReadRefList;
295 	fReadRefList = fWriteRefList;
296 	fWriteRefList = lastReadList;
297 }
298 
299 
300 void
301 ReadThread::_PublishEntrys(BMessenger& messenger)
302 {
303 	if (fReading || Stopped())
304 		return;
305 	_SwapLists();
306 	fReading = true;
307 	fNReaded = 0;
308 	messenger.SendMessage(kMsgAddRefs);
309 }
310 
311