// // Copyright (c) ZeroC, Inc. All rights reserved. // #include "ControllerView.xaml.h" #include #include #include #include #include #include #include using namespace std; using namespace Test; using namespace Test::Common; using namespace Platform; using namespace Windows::Foundation; using namespace Windows::Foundation::Collections; using namespace Windows::UI::Core; using namespace Windows::UI::Xaml; using namespace Windows::UI::Xaml::Controls; using namespace Windows::UI::Xaml::Controls::Primitives; using namespace Windows::UI::Xaml::Data; using namespace Windows::UI::Xaml::Input; using namespace Windows::UI::Xaml::Media; using namespace Windows::UI::Xaml::Navigation; using namespace Windows::Networking::Connectivity; using namespace Platform::Collections; namespace { typedef Test::TestHelper* (*CREATE_HELPER_ENTRY_POINT)(); class ControllerHelperI : public Test::ControllerHelper, public enable_shared_from_this { public: ControllerHelperI(const string&, const StringSeq&); virtual std::string loggerPrefix() const; virtual void print(const std::string&); virtual void serverReady(); virtual void communicatorInitialized(const shared_ptr&); void run(); void completed(int); int waitSuccess(int) const; void waitReady(int) const; std::string getOutput() const; void join(); void shutdown(); private: string _dll; StringSeq _args; bool _ready; bool _completed; int _status; ostringstream _out; thread _thread; mutable mutex _mutex; mutable condition_variable _condition; // // DLL cache // static std::map _dlls; HINSTANCE loadDll(const string&); shared_ptr _communicator; }; std::map ControllerHelperI::_dlls; class ProcessI : public Process { public: ProcessI(const shared_ptr&); void waitReady(int, const Ice::Current&); int waitSuccess(int, const Ice::Current&); string terminate(const Ice::Current&); private: shared_ptr _controllerHelper; }; class ProcessControllerI : public ProcessController { public: ProcessControllerI(ControllerView^); shared_ptr start(string, string, StringSeq, const Ice::Current&); virtual string getHost(string, bool, const Ice::Current&); private: ControllerView^ _controller; string _host; }; class ControllerI { public: ControllerI(ControllerView^); virtual ~ControllerI(); void registerProcessController(ControllerView^, const shared_ptr&, const shared_ptr&, const shared_ptr&); private: shared_ptr _communicator; shared_ptr _registry; }; Test::StreamHelper streamRedirect; } ControllerHelperI::ControllerHelperI(const string& dll, const StringSeq& args) : _dll(dll), _args(args), _ready(false), _completed(false), _status(0) { } string ControllerHelperI::loggerPrefix() const { return _dll; } void ControllerHelperI::print(const std::string& msg) { _out << msg; } void ControllerHelperI::serverReady() { unique_lock lock(_mutex); _ready = true; _condition.notify_all(); } void ControllerHelperI::communicatorInitialized(const shared_ptr& communicator) { unique_lock lock(_mutex); _communicator = communicator; } void ControllerHelperI::run() { thread t([this]() { HINSTANCE hnd = loadDll(_dll); if(hnd == 0) { ostringstream os; os << "failed to load `" << _dll << "': error code: " << GetLastError(); print(os.str()); completed(1); return; } FARPROC sym = GetProcAddress(hnd, "createHelper"); if(sym == 0) { print("failed to find createHelper function from `" + _dll + "'"); completed(1); return; } CREATE_HELPER_ENTRY_POINT createHelper = reinterpret_cast(sym); char** argv = new char*[_args.size() + 1]; for(unsigned int i = 0; i < _args.size(); ++i) { argv[i] = const_cast(_args[i].c_str()); } argv[_args.size()] = 0; if(_dll.find("client") != string::npos || _dll.find("collocated") != string::npos) { streamRedirect.setControllerHelper(this); } try { auto helper = unique_ptr(createHelper()); helper->setControllerHelper(this); helper->run(static_cast(_args.size()), argv); completed(0); } catch(const std::exception& ex) { print("unexpected exception while running `" + _args[0] + "':\n" + ex.what()); completed(1); } catch(...) { print("unexpected unknown exception while running `" + _args[0] + "'"); completed(1); } if(_dll.find("client") != string::npos || _dll.find("collocated") != string::npos) { streamRedirect.setControllerHelper(0); } delete[] argv; }); _thread = move(t); } void ControllerHelperI::join() { _thread.join(); } void ControllerHelperI::completed(int status) { unique_lock lock(_mutex); _completed = true; _status = status; _communicator = nullptr; _condition.notify_all(); } void ControllerHelperI::waitReady(int timeout) const { unique_lock lock(_mutex); while(!_ready && !_completed) { if(_condition.wait_for(lock, chrono::seconds(timeout)) == cv_status::timeout) { throw ProcessFailedException("timed out waiting for the process to be ready"); } } if(_completed && _status == EXIT_FAILURE) { throw ProcessFailedException(_out.str()); } } int ControllerHelperI::waitSuccess(int timeout) const { unique_lock lock(_mutex); while(!_completed) { if(_condition.wait_for(lock, chrono::seconds(timeout)) == cv_status::timeout) { throw ProcessFailedException("timed out waiting for the process to succeed"); } } return _status; } string ControllerHelperI::getOutput() const { assert(_completed); return _out.str(); } void ControllerHelperI::shutdown() { unique_lock lock(_mutex); if(_communicator) { _communicator->shutdown(); } } HINSTANCE ControllerHelperI::loadDll(const string& name) { unique_lock lock(_mutex); map::iterator p = _dlls.find(name); if (p == _dlls.end()) { HINSTANCE hnd = LoadPackagedLibrary(Ice::stringToWstring(name).c_str(), 0); p = _dlls.insert(make_pair(name, hnd)).first; } return p->second; } ProcessI::ProcessI(const shared_ptr& controllerHelper) : _controllerHelper(controllerHelper) { } void ProcessI::waitReady(int timeout, const Ice::Current&) { _controllerHelper->waitReady(timeout); } int ProcessI::waitSuccess(int timeout, const Ice::Current&) { return _controllerHelper->waitSuccess(timeout); } string ProcessI::terminate(const Ice::Current& current) { _controllerHelper->shutdown(); current.adapter->remove(current.id); _controllerHelper->join(); return _controllerHelper->getOutput(); } ProcessControllerI::ProcessControllerI(ControllerView^ controller) : _controller(controller), _host(_controller->getHost()) { } shared_ptr ProcessControllerI::start(string testSuite, string exe, StringSeq args, const Ice::Current& c) { ostringstream os; os << "starting " << testSuite << " " << exe << "... "; _controller->println(os.str()); replace(testSuite.begin(), testSuite.end(), '/', '_'); args.insert(args.begin(), testSuite + '_' + exe + ".dll"); auto helper = make_shared(testSuite + '_' + exe + ".dll", args); helper->run(); return Ice::uncheckedCast(c.adapter->addWithUUID(make_shared(helper))); } string ProcessControllerI::getHost(string, bool, const Ice::Current&) { return _host; } ControllerI::ControllerI(ControllerView^ controller) { Ice::InitializationData initData = Ice::InitializationData(); initData.properties = Ice::createProperties(); initData.properties->setProperty("Ice.ThreadPool.Client.SizeMax", "10"); initData.properties->setProperty("Ice.Default.Host", "127.0.0.1"); initData.properties->setProperty("Ice.Override.ConnectTimeout", "1000"); //initData.properties->setProperty("Ice.Trace.Network", "3"); //initData.properties->setProperty("Ice.Trace.Protocol", "1"); initData.properties->setProperty("ControllerAdapter.AdapterId", Ice::generateUUID()); _communicator = Ice::initialize(initData); auto registry = Ice::uncheckedCast( _communicator->stringToProxy("Util/ProcessControllerRegistry:tcp -h 127.0.0.1 -p 15001")); shared_ptr adapter = _communicator->createObjectAdapterWithEndpoints("ControllerAdapter", ""); Ice::Identity ident = { "ProcessController", "UWP"}; auto processController = Ice::uncheckedCast(adapter->add(make_shared(controller), ident)); adapter->activate(); registerProcessController(controller, adapter, registry, processController); } void ControllerI::registerProcessController(ControllerView^ controller, const shared_ptr& adapter, const shared_ptr& registry, const shared_ptr& processController) { registry->ice_pingAsync( [this, controller, adapter, registry, processController]() { auto connection = registry->ice_getCachedConnection(); connection->setAdapter(adapter); connection->setACM(5, Ice::ACMClose::CloseOff, Ice::ACMHeartbeat::HeartbeatAlways); connection->setCloseCallback([=](const shared_ptr&) { controller->println("connection with process controller registry closed"); std::this_thread::sleep_for(2s); registerProcessController(controller, adapter, registry, processController); }); registry->setProcessControllerAsync(processController, nullptr, [controller](exception_ptr e) { try { rethrow_exception(e); } catch(const Ice::CommunicatorDestroyedException&) { } catch(const std::exception& ex) { ostringstream os; os << "unexpected exception while connecting to process controller registry:\n" << ex.what(); controller->println(os.str()); } }); }, [this, controller, adapter, registry, processController](exception_ptr e) { try { rethrow_exception(e); } catch(const Ice::ConnectFailedException&) { std::this_thread::sleep_for(2s); registerProcessController(controller, adapter, registry, processController); } catch(const Ice::TimeoutException&) { std::this_thread::sleep_for(2s); registerProcessController(controller, adapter, registry, processController); } catch(const Ice::CommunicatorDestroyedException&) { } catch(const std::exception& ex) { ostringstream os; os << "unexpected exception while connecting to process controller registry:\n" << ex.what(); controller->println(os.str()); } }); } ControllerI::~ControllerI() { _communicator->destroy(); } static ControllerI* controllerI = 0; ControllerView::ControllerView() { InitializeComponent(); auto hostnames = NetworkInformation::GetHostNames(); ipv4Addresses->Items->Append("127.0.0.1"); for(unsigned int i = 0; i < hostnames->Size; ++i) { auto hostname = hostnames->GetAt(i); if(hostname->Type == Windows::Networking::HostNameType::Ipv4) { ipv4Addresses->Items->Append(hostname->RawName); } } ipv4Addresses->SelectedIndex = 0; } string ControllerView::getHost() const { return Ice::wstringToString(ipv4Addresses->SelectedItem->ToString()->Data()); } void ControllerView::Hostname_SelectionChanged(Platform::Object^, Windows::UI::Xaml::Controls::SelectionChangedEventArgs^) { if(controllerI) { delete controllerI; controllerI = 0; } controllerI = new ControllerI(this); } void ControllerView::println(const string& s) { if(!s.empty()) { Dispatcher->RunAsync( CoreDispatcherPriority::Normal, ref new DispatchedHandler([=]() { Output->Items->Append(ref new String(Ice::stringToWstring(s).c_str())); Output->SelectedIndex = Output->Items->Size - 1; Output->ScrollIntoView(Output->SelectedItem); })); } } ControllerView::~ControllerView() { if(controllerI) { delete controllerI; controllerI = 0; } }