From: Andrew Knight Date: Thu, 6 Mar 2014 15:06:54 +0000 (+0200) Subject: qtd3dservice: Support Appx device monitoring X-Git-Tag: upstream/5.2.90+alpha~8 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=ded1b5c10db7e2b7ce6a8a88c6d1ae7641888abc;p=platform%2Fupstream%2Fqttools.git qtd3dservice: Support Appx device monitoring Like Xap monitoring, local appx packages can be automatically monitored. This is accomplished without polling by: - Waiting for changes in the package registry - Checking the list of developer apps from the Appx manager - Waiting for changes in the application's shader directory Without polling, the service has 0% CPU utilization when idle, regardless of the number of Appx packages monitored. This is compared to Xap devices, which may incur several fractions of a percent of CPU time during package polling. Change-Id: I3b0fc6fc7a8beee854c2511295b6d33b102eee88 Reviewed-by: Oliver Wolff --- diff --git a/src/qtd3dservice/appxhandler.cpp b/src/qtd3dservice/appxhandler.cpp index 281ffb8..c12790d 100644 --- a/src/qtd3dservice/appxhandler.cpp +++ b/src/qtd3dservice/appxhandler.cpp @@ -43,6 +43,7 @@ #include #include +#include #include #include @@ -56,6 +57,7 @@ using namespace Microsoft::WRL::Wrappers; using namespace ABI::Windows::Management::Deployment; using namespace ABI::Windows::ApplicationModel; using namespace ABI::Windows::Storage; +using namespace ABI::Windows::Foundation::Collections; struct ComInitializer { @@ -70,7 +72,7 @@ struct ComInitializer if (isValid()) CoUninitialize(); } - bool isValid() + bool isValid() const { return SUCCEEDED(m_hr); } @@ -78,6 +80,87 @@ private: HRESULT m_hr; }; +extern int appxAppNames(int deviceIndex, QSet &apps) +{ + if (deviceIndex) { + qCWarning(lcD3DService) << "Unsupported device index:" << deviceIndex; + return 1; + } + + ComInitializer com; + if (!com.isValid()) + return 1; + + ComPtr packageManager; + HRESULT hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_Management_Deployment_PackageManager).Get(), + &packageManager); + if (FAILED(hr)) { + qCWarning(lcD3DService) << "Unable to instantiate package manager:" + << qt_error_string(hr); + return 1; + } + + ComPtr> packageCollection; + hr = packageManager->FindPackagesByUserSecurityId(NULL, &packageCollection); + if (FAILED(hr)) { + qCWarning(lcD3DService) << "Failed to find Appx packages:" + << qt_error_string(hr); + return 1; + } + ComPtr> iterator; + hr = packageCollection->First(&iterator); + if (FAILED(hr)) { + qCWarning(lcD3DService) << "Failed to get package iterator:" + << qt_error_string(hr); + return 1; + } + boolean hasCurrent; + hr = iterator->get_HasCurrent(&hasCurrent); + while (SUCCEEDED(hr) && hasCurrent) { + ComPtr package; + hr = iterator->get_Current(&package); + if (FAILED(hr)) { + qCWarning(lcD3DService) << qt_error_string(hr); + return 1; + } + + ComPtr package2; + hr = package.As(&package2); + if (FAILED(hr)) { + qCWarning(lcD3DService) << qt_error_string(hr); + return 1; + } + + boolean isDevelopmentMode; + hr = package2->get_IsDevelopmentMode(&isDevelopmentMode); + if (FAILED(hr)) { + qCWarning(lcD3DService) << qt_error_string(hr); + return 1; + } + if (!isDevelopmentMode) { + hr = iterator->MoveNext(&hasCurrent); + continue; + } + + ComPtr id; + hr = package->get_Id(&id); + if (FAILED(hr)) { + qCWarning(lcD3DService) << qt_error_string(hr); + return 1; + } + + HSTRING fullName; + hr = id->get_FullName(&fullName); + if (FAILED(hr)) { + qCWarning(lcD3DService) << qt_error_string(hr); + return 1; + } + apps.insert(QString::fromWCharArray(WindowsGetStringRawBuffer(fullName, Q_NULLPTR))); + hr = iterator->MoveNext(&hasCurrent); + } + return 0; +} + /* This function handles a worker thread servicing an Appx application */ extern int handleAppxDevice(int deviceIndex, const QString &app, const QString &localBase, HANDLE runLock) { @@ -134,7 +217,6 @@ extern int handleAppxDevice(int deviceIndex, const QString &app, const QString & const QString remoteSourcePath = remoteBase + QStringLiteral("\\source\\"); const QString remoteBinaryPath = remoteBase + QStringLiteral("\\binary\\"); - int round = 0; bool checkDirectories = true; forever { // If the run lock is signaled, it's time to quit @@ -185,50 +267,59 @@ extern int handleAppxDevice(int deviceIndex, const QString &app, const QString & checkDirectories = false; } - // Update roughly every 30 seconds - if (round++ % 30 == 0) { - QFile file(remoteControlFile); - if (!file.open(QFile::WriteOnly)) { - qCWarning(lcD3DService) << "Could not create control file."; - Sleep(1000); - continue; - } - file.write("Qt D3D compilation service"); + QFile file(remoteControlFile); + if (!file.open(QFile::WriteOnly)) { + qCWarning(lcD3DService) << "Could not create control file:" + << file.errorString(); + checkDirectories = true; + Sleep(1000); + continue; } + file.write("Qt D3D compilation service"); // Ok, ready to check for shaders - QDir remoteSourceDir(remoteSourcePath); - const QStringList shaderSources = remoteSourceDir.entryList(QDir::Files); - foreach (const QString &shaderSource, shaderSources) { - const QString remoteSource = remoteSourceDir.absoluteFilePath(shaderSource); - const QString shaderFileName = QFileInfo(remoteSource).fileName(); + QDirIterator it(remoteSourcePath); + while (it.hasNext()) { + const QString remoteSource = it.next(); + if (!it.fileInfo().isFile()) + continue; + const QString shaderFileName = it.fileName(); const QString localSource = localSourcePath + shaderFileName; const QString localBinary = localBinaryPath + shaderFileName; // Copy remote source to local - if (!QFile::copy(remoteSource, localSource)) { + if (QFile::exists(localSource)) + QFile::remove(localSource); + QFile remoteSourceFile(remoteSource); + if (!remoteSourceFile.copy(localSource)) { qCWarning(lcD3DService) << "Unable to copy shader source:" << remoteSource; + qCWarning(lcD3DService) << remoteSourceFile.errorString(); continue; } // Remove the remote file - if (!QFile::remove(remoteSource)) { + if (!remoteSourceFile.remove()) { qCWarning(lcD3DService) << "Unable to remove shader source:" << remoteSource; + qCWarning(lcD3DService) << remoteSourceFile.errorString(); continue; } // Compile shader hr = D3DService::compileShader(localSource, localBinary); if (FAILED(hr)) { - qCWarning(lcD3DService) << "Unable to compile shader:" << shaderSource; + qCWarning(lcD3DService) << "Unable to compile shader:" << localSource; qCWarning(lcD3DService) << qt_error_string(hr); continue; } // All went well, copy the blob to the device const QString remoteBinary = remoteBinaryPath + shaderFileName; - if (!QFile::copy(localBinary, remoteBinary)) { + if (QFile::exists(remoteBinary)) + QFile::remove(remoteBinary); + QFile localBinaryFile(localBinary); + if (!localBinaryFile.copy(remoteBinary)) { qCWarning(lcD3DService) << "Unable to copy to remote: " << localBinary; + qCWarning(lcD3DService) << localBinaryFile.errorString(); continue; } @@ -236,7 +327,35 @@ extern int handleAppxDevice(int deviceIndex, const QString &app, const QString & << "and uploaded to:" << remoteBinary; } - // Done, take a break. - Sleep(1000); + HANDLE notification = FindFirstChangeNotification( + reinterpret_cast(remoteSourcePath.utf16()), + FALSE, FILE_NOTIFY_CHANGE_FILE_NAME); + if (!notification) { + qCCritical(lcD3DService) << "Failed to set up shader directory notification:" + << qt_error_string(GetLastError()); + return 1; + } + + // Sleep for up to 30 seconds; wake if a new shader appears + HANDLE waitHandles[] = { notification, runLock }; + DWORD event = WaitForMultipleObjects(2, waitHandles, FALSE, 30000); + FindCloseChangeNotification(notification); + // Timeout or directory change; loop and update + if (event == WAIT_TIMEOUT || event == WAIT_OBJECT_0) + continue; + // runLock set; exit + if (event == WAIT_OBJECT_0 + 1) + return 0; + + hr = GetLastError(); + // If the app was uninstalled, this is expected + if (hr == ERROR_INVALID_HANDLE) { + qCDebug(lcD3DService) << "The wait handle was invalidated; worker exiting."; + return 1; + } + + qCWarning(lcD3DService) << "Appx handler wait failed:" + << qt_error_string(hr); + return 1; } } diff --git a/src/qtd3dservice/d3dservice.cpp b/src/qtd3dservice/d3dservice.cpp index 127d04c..962cc4d 100644 --- a/src/qtd3dservice/d3dservice.cpp +++ b/src/qtd3dservice/d3dservice.cpp @@ -70,6 +70,7 @@ extern int handleAppxDevice(int deviceIndex, const QString &app, const QString & extern int handleXapDevice(int deviceIndex, const QString &app, const QString &cacheDir, HANDLE runLock); typedef int (*AppListFunction)(int, QSet &); extern int xapAppNames(int deviceIndex, QSet &apps); +extern int appxAppNames(int deviceIndex, QSet &app); extern QStringList xapDeviceNames(); @@ -438,7 +439,8 @@ void __stdcall run(DWORD argc, LPWSTR argv[]) } // App monitoring threads - QVector workers; + QHash workers; + QHash workerThreads; // Device monitoring threads - one per device QVector deviceWorkers(emulatorNames.size() + 1, NULL); @@ -446,7 +448,8 @@ void __stdcall run(DWORD argc, LPWSTR argv[]) // Static list of registrations foreach (const QStringPair ®istration, D3DService::registrations()) { Worker *worker = new Worker(registration, &appWorker); - workers.append(worker); + workers.insert(registration, worker); + workerThreads.insert(worker->thread(), registration); waitHandles.append(worker->thread()); } @@ -462,6 +465,10 @@ void __stdcall run(DWORD argc, LPWSTR argv[]) SetupDiDestroyDeviceInfoList(info); } + // Create a monitoring thread for local Appx packages + Worker appxWorker(qMakePair(QStringLiteral("local"), QString()), &deviceWorker); + Q_UNUSED(appxWorker); + // Master loop // This loop handles incoming events from the service controller and // worker threads. It also creates new worker threads as needed. @@ -488,8 +495,16 @@ void __stdcall run(DWORD argc, LPWSTR argv[]) // A new worker is in the queue if (controlEvent == NewWorker) { while (!d->workerQueue.isEmpty()) { - Worker *worker = new Worker(d->workerQueue.takeFirst(), &appWorker); - workers.append(worker); + const QStringPair config = d->workerQueue.takeFirst(); + if (workers.contains(config)) { // The config is already running + qCDebug(lcD3DService) << "Discarded worker configuration:" + << config.first << config.second; + continue; + } + + Worker *worker = new Worker(config, &appWorker); + workers.insert(config, worker); + workerThreads.insert(worker->thread(), config); waitHandles.append(worker->thread()); maxWorker = minWorker + workers.size() - 1; } @@ -575,8 +590,9 @@ void __stdcall run(DWORD argc, LPWSTR argv[]) // A worker exited if (event >= minWorker && event <= maxWorker) { // Delete the worker and clear the handle (TODO: remove it?) - waitHandles.remove(event - WAIT_OBJECT_0); - Worker *worker = workers.takeAt(event - minWorker); + HANDLE thread = waitHandles.takeAt(event - WAIT_OBJECT_0); + QStringPair config = workerThreads.take(thread); + Worker *worker = workers.take(config); delete worker; continue; } @@ -611,9 +627,18 @@ DWORD __stdcall deviceWorker(LPVOID param) AppListFunction appList; int deviceIndex = 0; + HKEY waitKey = 0; if (args->deviceName.isEmpty() || args->deviceName == QStringLiteral("local")) { - // Not implemented - return 0; + appList = appxAppNames; + LONG result = RegOpenKeyEx( + HKEY_CLASSES_ROOT, + L"\\Local Settings\\Software\\Microsoft\\Windows\\CurrentVersion\\AppModel\\Repository\\Packages", + 0, KEY_NOTIFY, &waitKey); + if (result != ERROR_SUCCESS) { + qCWarning(lcD3DService) << "Unable to open registry key for Appx discovery:" + << qt_error_string(result); + waitKey = 0; + } } else { // CoreCon (Windows Phone) bool ok; @@ -647,9 +672,37 @@ DWORD __stdcall deviceWorker(LPVOID param) } appNames = latestAppNames; + + // If possible, wait for the registry event (otherwise, go straight to sleep) + if (waitKey) { + HANDLE waitEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + LONG result = RegNotifyChangeKeyValue(waitKey, TRUE, REG_NOTIFY_CHANGE_NAME, waitEvent, TRUE); + if (result != ERROR_SUCCESS) { + qCWarning(lcD3DService) << "Unable to create registry notifier:" + << qt_error_string(result); + RegCloseKey(waitKey); // Revert to polling + waitKey = 0; + } + + HANDLE waitHandles[] = { args->runLock, waitEvent }; + result = WaitForMultipleObjects(2, waitHandles, FALSE, INFINITE); + CloseHandle(waitEvent); + if (result == WAIT_OBJECT_0) // runLock, exit + return 0; + if (result == WAIT_OBJECT_0 + 1) { // registry changed, reset app list + appNames.clear(); + } else { + qCWarning(lcD3DService) << "Unexpected wait result:" << result + << qt_error_string(GetLastError()); + RegCloseKey(waitKey); // Revert to polling + waitKey = 0; + } + } Sleep(1000); } + if (waitKey) + RegCloseKey(waitKey); return 0; } @@ -682,7 +735,7 @@ DWORD __stdcall appWorker(LPVOID param) handleDevice = &handleXapDevice; } - return handleXapDevice(deviceIndex, args->app, cachePath, args->runLock); + return handleDevice(deviceIndex, args->app, cachePath, args->runLock); } // Service controller