OSDN Git Service

igit added as submodule
[tortoisegit/TortoiseGitJp.git] / src / TGitCache / ShellUpdater.cpp
1 // TortoiseSVN - a Windows shell extension for easy version control\r
2 \r
3 // External Cache Copyright (C) 2005-2008 - TortoiseSVN\r
4 \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
9 \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
14 \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
18 //\r
19 \r
20 #include "StdAfx.h"\r
21 #include "shlobj.h"\r
22 #include "GitStatusCache.h"\r
23 \r
24 CShellUpdater::CShellUpdater(void)\r
25 {\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
29         m_bRunning = FALSE;\r
30         m_bItemsAddedSinceLastUpdate = false;\r
31 }\r
32 \r
33 CShellUpdater::~CShellUpdater(void)\r
34 {\r
35         Stop();\r
36 }\r
37 \r
38 void CShellUpdater::Stop()\r
39 {\r
40         InterlockedExchange(&m_bRunning, FALSE);\r
41         if (m_hTerminationEvent != INVALID_HANDLE_VALUE)\r
42         {\r
43                 SetEvent(m_hTerminationEvent);\r
44                 if(WaitForSingleObject(m_hThread, 200) != WAIT_OBJECT_0)\r
45                 {\r
46                         ATLTRACE("Error terminating shell updater thread\n");\r
47                 }\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
54         }\r
55 }\r
56 \r
57 void CShellUpdater::Initialise()\r
58 {\r
59         // Don't call Initialize more than once\r
60         ATLASSERT(m_hThread == INVALID_HANDLE_VALUE);\r
61 \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
66 \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
71 }\r
72 \r
73 void CShellUpdater::AddPathForUpdate(const CTGitPath& path)\r
74 {\r
75         {\r
76                 AutoLocker lock(m_critSec);\r
77                 m_pathsToUpdate.push_back(path);\r
78                 \r
79                 // set this flag while we are synced \r
80                 // with the worker thread\r
81                 m_bItemsAddedSinceLastUpdate = true;\r
82         }\r
83 \r
84         SetEvent(m_hWakeEvent);\r
85 }\r
86 \r
87 \r
88 unsigned int CShellUpdater::ThreadEntry(void* pContext)\r
89 {\r
90         ((CShellUpdater*)pContext)->WorkerThread();\r
91         return 0;\r
92 }\r
93 \r
94 void CShellUpdater::WorkerThread()\r
95 {\r
96         HANDLE hWaitHandles[2];\r
97         hWaitHandles[0] = m_hTerminationEvent;  \r
98         hWaitHandles[1] = m_hWakeEvent;\r
99 \r
100         for(;;)\r
101         {\r
102                 DWORD waitResult = WaitForMultipleObjects(sizeof(hWaitHandles)/sizeof(hWaitHandles[0]), hWaitHandles, FALSE, INFINITE);\r
103                 \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
108                 {\r
109                         // Termination event\r
110                         break;\r
111                 }\r
112                 // wait some time before we notify the shell\r
113                 Sleep(50);\r
114                 for(;;)\r
115                 {\r
116                         CTGitPath workingPath;\r
117                         if (!m_bRunning)\r
118                                 return;\r
119                         Sleep(0);\r
120                         {\r
121                                 AutoLocker lock(m_critSec);\r
122                                 if(m_pathsToUpdate.empty())\r
123                                 {\r
124                                         // Nothing left to do \r
125                                         break;\r
126                                 }\r
127 \r
128                                 if(m_bItemsAddedSinceLastUpdate)\r
129                                 {\r
130                                         m_pathsToUpdate.erase(std::unique(m_pathsToUpdate.begin(), m_pathsToUpdate.end(), &CTGitPath::PredLeftEquivalentToRight), m_pathsToUpdate.end());\r
131                                         m_bItemsAddedSinceLastUpdate = false;\r
132                                 }\r
133 \r
134                                 workingPath = m_pathsToUpdate.front();\r
135                                 m_pathsToUpdate.pop_front();\r
136                         }\r
137                         if (workingPath.IsEmpty())\r
138                                 continue;\r
139                         ATLTRACE(_T("Update notifications for: %s\n"), workingPath.GetWinPath());\r
140                         if (workingPath.IsDirectory())\r
141                         {\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 (!CGitStatusCache::Instance().IsPathWatched(workingPath))\r
145                                 {\r
146                                         if (workingPath.HasAdminDir())\r
147                                                 CGitStatusCache::Instance().AddPathToWatch(workingPath);\r
148                                 }\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_GitAdminDir.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
158                                 \r
159                                 //SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), NULL);\r
160                         }\r
161                         else\r
162                                 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, workingPath.GetWinPath(), NULL);\r
163                 }\r
164         }\r
165         _endthread();\r
166 }\r
167 \r