1 // TortoiseSVN - a Windows shell extension for easy version control
\r
3 // External Cache Copyright (C) 2005-2008 - TortoiseSVN
\r
5 // This program is free software; you can redistribute it and/or
\r
6 // modify it under the terms of the GNU General Public License
\r
7 // as published by the Free Software Foundation; either version 2
\r
8 // of the License, or (at your option) any later version.
\r
10 // This program is distributed in the hope that it will be useful,
\r
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
13 // GNU General Public License for more details.
\r
15 // You should have received a copy of the GNU General Public License
\r
16 // along with this program; if not, write to the Free Software Foundation,
\r
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
\r
22 #include "SVNStatusCache.h"
\r
24 CShellUpdater::CShellUpdater(void)
\r
26 m_hWakeEvent = CreateEvent(NULL,FALSE,FALSE,NULL);
\r
27 m_hTerminationEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
\r
28 m_hThread = INVALID_HANDLE_VALUE;
\r
30 m_bItemsAddedSinceLastUpdate = false;
\r
33 CShellUpdater::~CShellUpdater(void)
\r
38 void CShellUpdater::Stop()
\r
40 InterlockedExchange(&m_bRunning, FALSE);
\r
41 if (m_hTerminationEvent != INVALID_HANDLE_VALUE)
\r
43 SetEvent(m_hTerminationEvent);
\r
44 if(WaitForSingleObject(m_hThread, 200) != WAIT_OBJECT_0)
\r
46 ATLTRACE("Error terminating shell updater thread\n");
\r
48 CloseHandle(m_hThread);
\r
49 m_hThread = INVALID_HANDLE_VALUE;
\r
50 CloseHandle(m_hTerminationEvent);
\r
51 m_hTerminationEvent = INVALID_HANDLE_VALUE;
\r
52 CloseHandle(m_hWakeEvent);
\r
53 m_hWakeEvent = INVALID_HANDLE_VALUE;
\r
57 void CShellUpdater::Initialise()
\r
59 // Don't call Initialize more than once
\r
60 ATLASSERT(m_hThread == INVALID_HANDLE_VALUE);
\r
62 // Just start the worker thread.
\r
63 // It will wait for event being signaled.
\r
64 // If m_hWakeEvent is already signaled the worker thread
\r
65 // will behave properly (with normal priority at worst).
\r
67 InterlockedExchange(&m_bRunning, TRUE);
\r
68 unsigned int threadId;
\r
69 m_hThread = (HANDLE)_beginthreadex(NULL,0,ThreadEntry,this,0,&threadId);
\r
70 SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST);
\r
73 void CShellUpdater::AddPathForUpdate(const CTSVNPath& path)
\r
76 AutoLocker lock(m_critSec);
\r
77 m_pathsToUpdate.push_back(path);
\r
79 // set this flag while we are synced
\r
80 // with the worker thread
\r
81 m_bItemsAddedSinceLastUpdate = true;
\r
84 SetEvent(m_hWakeEvent);
\r
88 unsigned int CShellUpdater::ThreadEntry(void* pContext)
\r
90 ((CShellUpdater*)pContext)->WorkerThread();
\r
94 void CShellUpdater::WorkerThread()
\r
96 HANDLE hWaitHandles[2];
\r
97 hWaitHandles[0] = m_hTerminationEvent;
\r
98 hWaitHandles[1] = m_hWakeEvent;
\r
102 DWORD waitResult = WaitForMultipleObjects(sizeof(hWaitHandles)/sizeof(hWaitHandles[0]), hWaitHandles, FALSE, INFINITE);
\r
104 // exit event/working loop if the first event (m_hTerminationEvent)
\r
105 // has been signaled or if one of the events has been abandoned
\r
106 // (i.e. ~CShellUpdater() is being executed)
\r
107 if(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED_0 || waitResult == WAIT_ABANDONED_0+1)
\r
109 // Termination event
\r
112 // wait some time before we notify the shell
\r
116 CTSVNPath workingPath;
\r
121 AutoLocker lock(m_critSec);
\r
122 if(m_pathsToUpdate.empty())
\r
124 // Nothing left to do
\r
128 if(m_bItemsAddedSinceLastUpdate)
\r
130 m_pathsToUpdate.erase(std::unique(m_pathsToUpdate.begin(), m_pathsToUpdate.end(), &CTSVNPath::PredLeftEquivalentToRight), m_pathsToUpdate.end());
\r
131 m_bItemsAddedSinceLastUpdate = false;
\r
134 workingPath = m_pathsToUpdate.front();
\r
135 m_pathsToUpdate.pop_front();
\r
137 if (workingPath.IsEmpty())
\r
139 ATLTRACE(_T("Update notifications for: %s\n"), workingPath.GetWinPath());
\r
140 if (workingPath.IsDirectory())
\r
142 // check if the path is monitored by the watcher. If it isn't, then we have to invalidate the cache
\r
143 // for that path and add it to the watcher.
\r
144 if (!CSVNStatusCache::Instance().IsPathWatched(workingPath))
\r
146 if (workingPath.HasAdminDir())
\r
147 CSVNStatusCache::Instance().AddPathToWatch(workingPath);
\r
149 // first send a notification about a sub folder change, so explorer doesn't discard
\r
150 // the folder notification. Since we only know for sure that the subversion admin
\r
151 // dir is present, we send a notification for that folder.
\r
152 CString admindir = workingPath.GetWinPathString() + _T("\\") + g_SVNAdminDir.GetAdminDirName();
\r
153 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, (LPCTSTR)admindir, NULL);
\r
154 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), NULL);
\r
155 // Sending an UPDATEDIR notification somehow overwrites/deletes the UPDATEITEM message. And without
\r
156 // that message, the folder overlays in the current view don't get updated without hitting F5.
\r
157 // Drawback is, without UPDATEDIR, the left tree view isn't always updated...
\r
159 //SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), NULL);
\r
162 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), NULL);
\r