xref: /haiku/src/apps/musiccollection/FileMonitor.cpp (revision 68d37cfb3a755a7270d772b505ee15c8b18aa5e0)
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 			break;
93 	}
94 }
95 
96 
97 void
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
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
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
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
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*
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
176 ReadThreadFunction(void *data)
177 {
178 	ReadThread* that = (ReadThread*)data;
179 	return that->Process();
180 }
181 
182 
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
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
215 ReadThread::Running()
216 {
217 	return fRunning;
218 }
219 
220 
221 status_t
222 ReadThread::Wait()
223 {
224 	status_t exitValue;
225 	return wait_for_thread(fThreadId, &exitValue);
226 }
227 
228 
229 void
230 ReadThread::Stop()
231 {
232 	fStopped = true;
233 }
234 
235 
236 bool
237 ReadThread::Stopped()
238 {
239 	return fStopped;
240 }
241 
242 
243 RefList*
244 ReadThread::ReadRefList()
245 {
246 	return fReadRefList;
247 }
248 
249 
250 void
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
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
293 ReadThread::_SwapLists()
294 {
295 	RefList* lastReadList = fReadRefList;
296 	fReadRefList = fWriteRefList;
297 	fWriteRefList = lastReadList;
298 }
299 
300 
301 void
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