TGitCache test application basic work.
[TortoiseGit.git] / test / Cache / CacheDlg.cpp
blobccdf03701b93ae302f4365c944187c164abb0cc3
1 // TortoiseSVN - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2006, 2009 - 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.
19 #include "stdafx.h"
20 #include "Cache.h"
21 #include "DirFileEnum.h"
22 #include "CacheInterface.h"
23 #include <WinInet.h>
24 #include ".\cachedlg.h"
26 #ifdef _DEBUG
27 #define new DEBUG_NEW
28 #endif
34 CCacheDlg::CCacheDlg(CWnd* pParent /*=NULL*/)
35 : CDialog(CCacheDlg::IDD, pParent)
36 , m_sRootPath(_T(""))
37 , m_hPipe(INVALID_HANDLE_VALUE)
39 m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
42 void CCacheDlg::DoDataExchange(CDataExchange* pDX)
44 CDialog::DoDataExchange(pDX);
45 DDX_Text(pDX, IDC_ROOTPATH, m_sRootPath);
48 BEGIN_MESSAGE_MAP(CCacheDlg, CDialog)
49 ON_WM_PAINT()
50 ON_WM_QUERYDRAGICON()
51 //}}AFX_MSG_MAP
52 ON_BN_CLICKED(IDOK, OnBnClickedOk)
53 ON_BN_CLICKED(IDC_WATCHTESTBUTTON, OnBnClickedWatchtestbutton)
54 END_MESSAGE_MAP()
57 // CCacheDlg message handlers
59 BOOL CCacheDlg::OnInitDialog()
61 CDialog::OnInitDialog();
63 // Set the icon for this dialog. The framework does this automatically
64 // when the application's main window is not a dialog
65 SetIcon(m_hIcon, TRUE); // Set big icon
66 SetIcon(m_hIcon, FALSE); // Set small icon
68 // TODO: Add extra initialization here
70 return TRUE; // return TRUE unless you set the focus to a control
73 // If you add a minimize button to your dialog, you will need the code below
74 // to draw the icon. For MFC applications using the document/view model,
75 // this is automatically done for you by the framework.
77 void CCacheDlg::OnPaint()
79 if (IsIconic())
81 CPaintDC dc(this); // device context for painting
83 SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
85 // Center icon in client rectangle
86 int cxIcon = GetSystemMetrics(SM_CXICON);
87 int cyIcon = GetSystemMetrics(SM_CYICON);
88 CRect rect;
89 GetClientRect(&rect);
90 int x = (rect.Width() - cxIcon + 1) / 2;
91 int y = (rect.Height() - cyIcon + 1) / 2;
93 // Draw the icon
94 dc.DrawIcon(x, y, m_hIcon);
96 else
98 CDialog::OnPaint();
102 // The system calls this function to obtain the cursor to display while the user drags
103 // the minimized window.
104 HCURSOR CCacheDlg::OnQueryDragIcon()
106 return static_cast<HCURSOR>(m_hIcon);
109 void CCacheDlg::OnBnClickedOk()
111 UpdateData();
112 AfxBeginThread(TestThreadEntry, this);
114 UINT CCacheDlg::TestThreadEntry(LPVOID pVoid)
116 return ((CCacheDlg*)pVoid)->TestThread();
119 //this is the thread function which calls the subversion function
120 UINT CCacheDlg::TestThread()
122 CDirFileEnum direnum(m_sRootPath);
123 m_filelist.RemoveAll();
124 CString filepath;
125 bool bIsDir = false;
126 while (direnum.NextFile(filepath, &bIsDir))
127 if(filepath.Find(_T(".git"))<0)
128 m_filelist.Add(filepath);
130 CTime starttime = CTime::GetCurrentTime();
131 GetDlgItem(IDC_STARTTIME)->SetWindowText(starttime.Format(_T("%H:%M:%S")));
132 int filecounter = 0;
134 DWORD startticks = GetTickCount();
136 CString sNumber;
137 srand(GetTickCount());
138 for (int i=0; i < 1; ++i)
140 CString filepath;
141 //do {
142 filepath = m_filelist.GetAt(rand() % m_filelist.GetCount());
143 //}while(filepath.Find(_T(".git"))>=0);
144 GetDlgItem(IDC_FILEPATH)->SetWindowText(filepath);
145 GetStatusFromRemoteCache(CTGitPath(filepath), true);
146 sNumber.Format(_T("%d"), i);
147 GetDlgItem(IDC_DONE)->SetWindowText(sNumber);
148 if ((GetTickCount()%10)==1)
149 Sleep(10);
150 if ((rand()%10)==3)
151 RemoveFromCache(filepath);
153 CTime endtime = CTime::GetCurrentTime();
154 CString sEnd = endtime.Format(_T("%H:%M:%S"));
156 DWORD endticks = GetTickCount();
158 CString sEndText;
159 sEndText.Format(_T("%s - %d ms"), sEnd, endticks-startticks);
161 GetDlgItem(IDC_ENDTIME)->SetWindowText(sEndText);
163 return 0;
167 bool CCacheDlg::EnsurePipeOpen()
169 if(m_hPipe != INVALID_HANDLE_VALUE)
171 return true;
174 m_hPipe = CreateFile(
175 GetCachePipeName(), // pipe name
176 GENERIC_READ | // read and write access
177 GENERIC_WRITE,
178 0, // no sharing
179 NULL, // default security attributes
180 OPEN_EXISTING, // opens existing pipe
181 FILE_FLAG_OVERLAPPED, // default attributes
182 NULL); // no template file
184 if (m_hPipe == INVALID_HANDLE_VALUE && GetLastError() == ERROR_PIPE_BUSY)
186 // TSVNCache is running but is busy connecting a different client.
187 // Do not give up immediately but wait for a few milliseconds until
188 // the server has created the next pipe instance
189 if (WaitNamedPipe(GetCachePipeName(), 50))
191 m_hPipe = CreateFile(
192 GetCachePipeName(), // pipe name
193 GENERIC_READ | // read and write access
194 GENERIC_WRITE,
195 0, // no sharing
196 NULL, // default security attributes
197 OPEN_EXISTING, // opens existing pipe
198 FILE_FLAG_OVERLAPPED, // default attributes
199 NULL); // no template file
204 if (m_hPipe != INVALID_HANDLE_VALUE)
206 // The pipe connected; change to message-read mode.
207 DWORD dwMode;
209 dwMode = PIPE_READMODE_MESSAGE;
210 if(!SetNamedPipeHandleState(
211 m_hPipe, // pipe handle
212 &dwMode, // new pipe mode
213 NULL, // don't set maximum bytes
214 NULL)) // don't set maximum time
216 ATLTRACE("SetNamedPipeHandleState failed");
217 CloseHandle(m_hPipe);
218 m_hPipe = INVALID_HANDLE_VALUE;
219 return false;
221 // create an unnamed (=local) manual reset event for use in the overlapped structure
222 m_hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
223 if (m_hEvent)
224 return true;
225 ATLTRACE("CreateEvent failed");
226 ClosePipe();
227 return false;
230 return false;
234 void CCacheDlg::ClosePipe()
236 if(m_hPipe != INVALID_HANDLE_VALUE)
238 CloseHandle(m_hPipe);
239 CloseHandle(m_hEvent);
240 m_hPipe = INVALID_HANDLE_VALUE;
241 m_hEvent = INVALID_HANDLE_VALUE;
245 bool CCacheDlg::GetStatusFromRemoteCache(const CTGitPath& Path, bool bRecursive)
247 if(!EnsurePipeOpen())
249 STARTUPINFO startup;
250 PROCESS_INFORMATION process;
251 memset(&startup, 0, sizeof(startup));
252 startup.cb = sizeof(startup);
253 memset(&process, 0, sizeof(process));
255 CString sCachePath = _T("TSVNCache.exe");
256 if (CreateProcess(sCachePath.GetBuffer(sCachePath.GetLength()+1), _T(""), NULL, NULL, FALSE, 0, 0, 0, &startup, &process)==0)
258 // It's not appropriate to do a message box here, because there may be hundreds of calls
259 sCachePath.ReleaseBuffer();
260 ATLTRACE("Failed to start cache\n");
261 return false;
263 sCachePath.ReleaseBuffer();
265 // Wait for the cache to open
266 long endTime = (long)GetTickCount()+1000;
267 while(!EnsurePipeOpen())
269 if(((long)GetTickCount() - endTime) > 0)
271 return false;
277 DWORD nBytesRead;
278 TSVNCacheRequest request;
279 request.flags = TSVNCACHE_FLAGS_NONOTIFICATIONS;
280 if(bRecursive)
282 request.flags |= TSVNCACHE_FLAGS_RECUSIVE_STATUS;
284 wcsncpy(request.path, Path.GetWinPath(), MAX_PATH);
285 ZeroMemory(&m_Overlapped, sizeof(OVERLAPPED));
286 m_Overlapped.hEvent = m_hEvent;
287 // Do the transaction in overlapped mode.
288 // That way, if anything happens which might block this call
289 // we still can get out of it. We NEVER MUST BLOCK THE SHELL!
290 // A blocked shell is a very bad user impression, because users
291 // who don't know why it's blocked might find the only solution
292 // to such a problem is a reboot and therefore they might loose
293 // valuable data.
294 // Sure, it would be better to have no situations where the shell
295 // even can get blocked, but the timeout of 5 seconds is long enough
296 // so that users still recognize that something might be wrong and
297 // report back to us so we can investigate further.
299 TSVNCacheResponse ReturnedStatus;
300 BOOL fSuccess = TransactNamedPipe(m_hPipe,
301 &request, sizeof(request),
302 &ReturnedStatus, sizeof(ReturnedStatus),
303 &nBytesRead, &m_Overlapped);
305 if (!fSuccess)
307 if (GetLastError()!=ERROR_IO_PENDING)
309 ClosePipe();
310 return false;
313 // TransactNamedPipe is working in an overlapped operation.
314 // Wait for it to finish
315 DWORD dwWait = WaitForSingleObject(m_hEvent, INFINITE);
316 if (dwWait == WAIT_OBJECT_0)
318 fSuccess = GetOverlappedResult(m_hPipe, &m_Overlapped, &nBytesRead, FALSE);
319 return TRUE;
321 else
322 fSuccess = FALSE;
325 ClosePipe();
326 return false;
329 void CCacheDlg::RemoveFromCache(const CString& path)
331 // if we use the external cache, we tell the cache directly that something
332 // has changed, without the detour via the shell.
333 HANDLE hPipe = CreateFile(
334 GetCacheCommandPipeName(), // pipe name
335 GENERIC_READ | // read and write access
336 GENERIC_WRITE,
337 0, // no sharing
338 NULL, // default security attributes
339 OPEN_EXISTING, // opens existing pipe
340 FILE_FLAG_OVERLAPPED, // default attributes
341 NULL); // no template file
344 if (hPipe != INVALID_HANDLE_VALUE)
346 // The pipe connected; change to message-read mode.
347 DWORD dwMode;
349 dwMode = PIPE_READMODE_MESSAGE;
350 if(SetNamedPipeHandleState(
351 hPipe, // pipe handle
352 &dwMode, // new pipe mode
353 NULL, // don't set maximum bytes
354 NULL)) // don't set maximum time
356 DWORD cbWritten;
357 TSVNCacheCommand cmd;
358 cmd.command = TSVNCACHECOMMAND_CRAWL;
359 wcsncpy(cmd.path, path, MAX_PATH);
360 BOOL fSuccess = WriteFile(
361 hPipe, // handle to pipe
362 &cmd, // buffer to write from
363 sizeof(cmd), // number of bytes to write
364 &cbWritten, // number of bytes written
365 NULL); // not overlapped I/O
367 if (! fSuccess || sizeof(cmd) != cbWritten)
369 DisconnectNamedPipe(hPipe);
370 CloseHandle(hPipe);
371 hPipe = INVALID_HANDLE_VALUE;
373 if (hPipe != INVALID_HANDLE_VALUE)
375 // now tell the cache we don't need it's command thread anymore
376 DWORD cbWritten;
377 TSVNCacheCommand cmd;
378 cmd.command = TSVNCACHECOMMAND_END;
379 WriteFile(
380 hPipe, // handle to pipe
381 &cmd, // buffer to write from
382 sizeof(cmd), // number of bytes to write
383 &cbWritten, // number of bytes written
384 NULL); // not overlapped I/O
385 DisconnectNamedPipe(hPipe);
386 CloseHandle(hPipe);
387 hPipe = INVALID_HANDLE_VALUE;
390 else
392 ATLTRACE("SetNamedPipeHandleState failed");
393 CloseHandle(hPipe);
397 void CCacheDlg::OnBnClickedWatchtestbutton()
399 UpdateData();
400 AfxBeginThread(WatchTestThreadEntry, this);
403 UINT CCacheDlg::WatchTestThreadEntry(LPVOID pVoid)
405 return ((CCacheDlg*)pVoid)->WatchTestThread();
408 //this is the thread function which calls the subversion function
409 UINT CCacheDlg::WatchTestThread()
411 CDirFileEnum direnum(m_sRootPath);
412 m_filelist.RemoveAll();
413 CString filepath;
414 bool bIsDir = false;
415 while (direnum.NextFile(filepath, &bIsDir))
416 m_filelist.Add(filepath);
418 CTime starttime = CTime::GetCurrentTime();
419 GetDlgItem(IDC_STARTTIME)->SetWindowText(starttime.Format(_T("%H:%M:%S")));
420 int filecounter = 0;
422 DWORD startticks = GetTickCount();
424 CString sNumber;
425 srand(GetTickCount());
426 filepath = m_filelist.GetAt(rand() % m_filelist.GetCount());
427 GetStatusFromRemoteCache(CTGitPath(m_sRootPath), false);
428 for (int i=0; i < 10000; ++i)
430 filepath = m_filelist.GetAt(rand() % m_filelist.GetCount());
431 GetDlgItem(IDC_FILEPATH)->SetWindowText(filepath);
432 TouchFile(filepath);
433 CopyRemoveCopy(filepath);
434 sNumber.Format(_T("%d"), i);
435 GetDlgItem(IDC_DONE)->SetWindowText(sNumber);
438 // create dummy directories and remove them again several times
439 for (int outer = 0; outer<100; ++outer)
441 for (int i=0; i<10; ++i)
443 filepath.Format(_T("__MyDummyFolder%d"), i);
444 CreateDirectory(m_sRootPath+_T("\\")+filepath, NULL);
445 HANDLE hFile = CreateFile(m_sRootPath+_T("\\")+filepath+_T("\\file"), GENERIC_READ, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
446 CloseHandle(hFile);
447 SHChangeNotify(SHCNE_UPDATEITEM, SHCNF_PATH | SHCNF_FLUSHNOWAIT, m_sRootPath+_T("\\")+filepath+_T("\\file"), NULL);
449 Sleep(500);
450 for (int i=0; i<10; ++i)
452 filepath.Format(_T("__MyDummyFolder%d"), i);
453 DeleteFile(m_sRootPath+_T("\\")+filepath+_T("\\file"));
454 RemoveDirectory(m_sRootPath+_T("\\")+filepath);
456 sNumber.Format(_T("%d"), outer);
457 GetDlgItem(IDC_DONE)->SetWindowText(sNumber);
460 CTime endtime = CTime::GetCurrentTime();
461 CString sEnd = endtime.Format(_T("%H:%M:%S"));
463 DWORD endticks = GetTickCount();
465 CString sEndText;
466 sEndText.Format(_T("%s - %d ms"), sEnd, endticks-startticks);
468 GetDlgItem(IDC_ENDTIME)->SetWindowText(sEndText);
470 return 0;
473 void CCacheDlg::TouchFile(const CString& path)
475 SetFileAttributes(path, FILE_ATTRIBUTE_NORMAL);
476 HANDLE hFile = CreateFile(path, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
477 if (hFile == INVALID_HANDLE_VALUE)
478 return;
480 FILETIME ft;
481 SYSTEMTIME st;
483 GetSystemTime(&st); // gets current time
484 SystemTimeToFileTime(&st, &ft); // converts to file time format
485 SetFileTime(hFile, // sets last-write time for file
486 (LPFILETIME) NULL, (LPFILETIME) NULL, &ft);
488 CloseHandle(hFile);
491 void CCacheDlg::CopyRemoveCopy(const CString& path)
493 if (PathIsDirectory(path))
494 return;
495 if (path.Find(_T(".svn"))>=0)
496 return;
497 if (CopyFile(path, path+_T(".tmp"), FALSE))
499 if (DeleteFile(path))
501 if (MoveFile(path+_T(".tmp"), path))
502 return;
503 else
504 MessageBox(_T("could not move file!"), path);
506 else
507 MessageBox(_T("could not delete file!"), path);
509 else
510 MessageBox(_T("could not copy file!"), path);