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
17FileMonitor::FileMonitor(EntryViewInterface* listener)
18	:
19	fListener(listener),
20	fCurrentReadList(NULL),
21	fCurrentReadIndex(0)
22{
23
24}
25
26
27FileMonitor::~FileMonitor()
28{
29	Reset();
30}
31
32
33void
34FileMonitor::SetReadThread(ReadThread* readThread)
35{
36	fReadThread = readThread;
37}
38
39
40void
41FileMonitor::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
54void
55FileMonitor::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
96void
97FileMonitor::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
113void
114FileMonitor::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
126void
127FileMonitor::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
140void
141FileMonitor::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
150void
151FileMonitor::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
160WatchedFile*
161FileMonitor::_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
174int32
175ReadThreadFunction(void *data)
176{
177	ReadThread* that = (ReadThread*)data;
178	return that->Process();
179}
180
181
182ReadThread::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
196status_t
197ReadThread::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
213bool
214ReadThread::Running()
215{
216	return fRunning;
217}
218
219
220status_t
221ReadThread::Wait()
222{
223	status_t exitValue;
224	return wait_for_thread(fThreadId, &exitValue);
225}
226
227
228void
229ReadThread::Stop()
230{
231	fStopped = true;
232}
233
234
235bool
236ReadThread::Stopped()
237{
238	return fStopped;
239}
240
241
242RefList*
243ReadThread::ReadRefList()
244{
245	return fReadRefList;
246}
247
248
249void
250ReadThread::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
263int32
264ReadThread::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
291void
292ReadThread::_SwapLists()
293{
294	RefList* lastReadList = fReadRefList;
295	fReadRefList = fWriteRefList;
296	fWriteRefList = lastReadList;
297}
298
299
300void
301ReadThread::_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