/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ #ifndef _THRIFT_TEST_SERVERTHREAD_TCC_ #define _THRIFT_TEST_SERVERTHREAD_TCC_ 1 #include "ServerThread.h" #include #include #include #include #include namespace apache { namespace thrift { namespace test { void ServerThread::start() { assert(!running_); running_ = true; helper_.reset(new Helper(this)); // Start the other thread concurrency::ThreadFactory threadFactory; threadFactory.setDetached(false); thread_ = threadFactory.newThread(helper_); thread_->start(); // Wait on the other thread to tell us that it has successfully // bound to the port and started listening (or until an error occurs). concurrency::Synchronized s(serverMonitor_); while (!serving_ && !error_) { serverMonitor_.waitForever(); } if (error_) { throw transport::TTransportException(transport::TTransportException::NOT_OPEN, "failed to bind on server socket"); } } void ServerThread::stop() { if (!running_) { return; } // Tell the server to stop server_->stop(); running_ = false; // Wait for the server thread to exit // // Note: this only works if all client connections have closed. The servers // generally wait for everything to be closed before exiting; there currently // isn't a way to tell them to just exit now, and shut down existing // connections. thread_->join(); } void ServerThread::run() { /* * Try binding to several ports, in case the one we want is already in use. */ port_ = 12345; unsigned int maxRetries = 10; for (unsigned int n = 0; n < maxRetries; ++n) { // Create the server server_ = serverState_->createServer(port_); // Install our helper as the server event handler, so that our // preServe() method will be called once we've successfully bound to // the port and are about to start listening. server_->setServerEventHandler(helper_); try { // Try to serve requests server_->serve(); } catch (const TException&) { // TNonblockingServer throws a generic TException if it fails to bind. // If we get a TException, we'll optimistically assume the bind failed. ++port_; continue; } // Seriously? serve() is pretty lame. If it fails to start serving it // just returns rather than throwing an exception. // // We have to use our preServe() hook to tell if serve() successfully // started serving and is returning because stop() is called, or if it just // failed to start serving in the first place. concurrency::Synchronized s(serverMonitor_); if (serving_) { // Oh good, we started serving and are exiting because // we're trying to stop. serving_ = false; return; } else { // We never started serving, probably because we failed to bind to the // port. Increment the port number and try again. ++port_; continue; } } // We failed to bind on any port. concurrency::Synchronized s(serverMonitor_); error_ = true; serverMonitor_.notify(); } void ServerThread::preServe() { // We bound to the port successfully, and are about to start serving requests serverState_->bindSuccessful(port_); // Set the real server event handler (replacing ourself) std::shared_ptr serverEventHandler = serverState_->getServerEventHandler(); server_->setServerEventHandler(serverEventHandler); // Notify the main thread that we have successfully started serving requests concurrency::Synchronized s(serverMonitor_); serving_ = true; serverMonitor_.notify(); // Invoke preServe() on the real event handler, since we ate // the original preServe() event. if (serverEventHandler) { serverEventHandler->preServe(); } } } } } // apache::thrift::test #endif // _THRIFT_TEST_SERVERTHREAD_TCC_