TortoiseGitBlame: Diff shown in wrong order
[TortoiseGit.git] / src / TGitCache / ShellUpdater.cpp
blob8b1bc3f3017b0031406912cd8b4de2171f934a50
1 // TortoiseGit - a Windows shell extension for easy version control
3 // External Cache Copyright (C) 2005-2008 - TortoiseSVN
4 // Copyright (C) 2008-2011,2013,2015-2017 - TortoiseGit
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21 #include "stdafx.h"
22 #include <ShlObj.h>
23 #include "GitAdminDir.h"
24 #include "GitStatusCache.h"
26 CShellUpdater::CShellUpdater(void)
27 : m_bRunning(FALSE)
28 , m_bItemsAddedSinceLastUpdate(false)
30 m_hWakeEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
31 m_hTerminationEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr);
34 CShellUpdater::~CShellUpdater(void)
36 Stop();
39 void CShellUpdater::Stop()
41 InterlockedExchange(&m_bRunning, FALSE);
42 if (m_hTerminationEvent)
44 SetEvent(m_hTerminationEvent);
45 if(WaitForSingleObject(m_hThread, 200) != WAIT_OBJECT_0)
46 CTraceToOutputDebugString::Instance()(__FUNCTION__ ": Error terminating shell updater thread\n");
48 m_hThread.CloseHandle();
49 m_hTerminationEvent.CloseHandle();
50 m_hWakeEvent.CloseHandle();
53 void CShellUpdater::Initialise()
55 // Don't call Initialize more than once
56 ATLASSERT(!m_hThread);
58 // Just start the worker thread.
59 // It will wait for event being signaled.
60 // If m_hWakeEvent is already signaled the worker thread
61 // will behave properly (with normal priority at worst).
63 InterlockedExchange(&m_bRunning, TRUE);
64 unsigned int threadId;
65 m_hThread = (HANDLE)_beginthreadex(nullptr, 0, ThreadEntry, this, 0, &threadId);
66 SetThreadPriority(m_hThread, THREAD_PRIORITY_LOWEST);
69 void CShellUpdater::AddPathForUpdate(const CTGitPath& path)
72 AutoLocker lock(m_critSec);
74 m_pathsToUpdate.push_back(path);
76 // set this flag while we are synced
77 // with the worker thread
78 m_bItemsAddedSinceLastUpdate = true;
81 SetEvent(m_hWakeEvent);
85 unsigned int CShellUpdater::ThreadEntry(void* pContext)
87 reinterpret_cast<CShellUpdater*>(pContext)->WorkerThread();
88 return 0;
91 void CShellUpdater::WorkerThread()
93 HANDLE hWaitHandles[2];
94 hWaitHandles[0] = m_hTerminationEvent;
95 hWaitHandles[1] = m_hWakeEvent;
97 for(;;)
99 DWORD waitResult = WaitForMultipleObjects(_countof(hWaitHandles), hWaitHandles, FALSE, INFINITE);
101 // exit event/working loop if the first event (m_hTerminationEvent)
102 // has been signaled or if one of the events has been abandoned
103 // (i.e. ~CShellUpdater() is being executed)
104 if(waitResult == WAIT_OBJECT_0 || waitResult == WAIT_ABANDONED_0 || waitResult == WAIT_ABANDONED_0+1)
106 // Termination event
107 break;
109 // wait some time before we notify the shell
110 Sleep(50);
111 for(;;)
113 CTGitPath workingPath;
114 if (!m_bRunning)
115 return;
116 Sleep(0);
118 AutoLocker lock(m_critSec);
119 if(m_pathsToUpdate.empty())
121 // Nothing left to do
122 break;
125 if(m_bItemsAddedSinceLastUpdate)
127 m_pathsToUpdate.erase(std::unique(m_pathsToUpdate.begin(), m_pathsToUpdate.end(), &CTGitPath::PredLeftEquivalentToRight), m_pathsToUpdate.end());
128 m_bItemsAddedSinceLastUpdate = false;
131 workingPath = m_pathsToUpdate.front();
132 m_pathsToUpdate.pop_front();
134 if (workingPath.IsEmpty())
135 continue;
136 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) L": shell notification for %s\n", workingPath.GetWinPath());
137 if (workingPath.IsDirectory())
139 // first send a notification about a sub folder change, so explorer doesn't discard
140 // the folder notification. Since we only know for sure that the git admin
141 // dir is present, we send a notification for that folder.
142 CString admindir(workingPath.GetWinPathString());
143 admindir += L'\\';
144 admindir += GitAdminDir::GetAdminDirName();
145 if(::PathFileExists(admindir))
146 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, (LPCTSTR)admindir, nullptr);
148 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), nullptr);
149 // Sending an UPDATEDIR notification somehow overwrites/deletes the UPDATEITEM message. And without
150 // that message, the folder overlays in the current view don't get updated without hitting F5.
151 // Drawback is, without UPDATEDIR, the left tree view isn't always updated...
153 //SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), nullptr);
155 else
156 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), nullptr);
159 _endthread();