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			break;
93	}
94}
95
96
97void
98FileMonitor::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
114void
115FileMonitor::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
127void
128FileMonitor::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
141void
142FileMonitor::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
151void
152FileMonitor::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
161WatchedFile*
162FileMonitor::_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
175int32
176ReadThreadFunction(void *data)
177{
178	ReadThread* that = (ReadThread*)data;
179	return that->Process();
180}
181
182
183ReadThread::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
197status_t
198ReadThread::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
214bool
215ReadThread::Running()
216{
217	return fRunning;
218}
219
220
221status_t
222ReadThread::Wait()
223{
224	status_t exitValue;
225	return wait_for_thread(fThreadId, &exitValue);
226}
227
228
229void
230ReadThread::Stop()
231{
232	fStopped = true;
233}
234
235
236bool
237ReadThread::Stopped()
238{
239	return fStopped;
240}
241
242
243RefList*
244ReadThread::ReadRefList()
245{
246	return fReadRefList;
247}
248
249
250void
251ReadThread::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
264int32
265ReadThread::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
292void
293ReadThread::_SwapLists()
294{
295	RefList* lastReadList = fReadRefList;
296	fReadRefList = fWriteRefList;
297	fWriteRefList = lastReadList;
298}
299
300
301void
302ReadThread::_PublishEntrys(BMessenger& messenger)
303{
304	if (fReading || Stopped())
305		return;
306	_SwapLists();
307	fReading = true;
308	fNReaded = 0;
309	messenger.SendMessage(kMsgAddRefs);
310}
311