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