153 lines
4.7 KiB
C++
153 lines
4.7 KiB
C++
/*
|
|
* 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 <thrift/concurrency/ThreadFactory.h>
|
|
#include <thrift/concurrency/ThreadManager.h>
|
|
#include <thrift/server/TThreadPoolServer.h>
|
|
#include <thrift/transport/TBufferTransports.h>
|
|
#include <thrift/transport/TServerSocket.h>
|
|
|
|
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<server::TServerEventHandler> 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_
|