blob: 9f2087c80e42d3cfdc29a83c72c03d4682bfaf33 [file] [log] [blame]
Roger Meier2be7f242012-05-10 09:01:45 +00001/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19#ifndef _THRIFT_TEST_SERVERTHREAD_TCC_
20#define _THRIFT_TEST_SERVERTHREAD_TCC_ 1
21
Roger Meier2b1a5282012-05-11 10:12:39 +000022#include "ServerThread.h"
Roger Meier2be7f242012-05-10 09:01:45 +000023
Roger Meier2b1a5282012-05-11 10:12:39 +000024#include <thrift/concurrency/PosixThreadFactory.h>
25#include <thrift/concurrency/ThreadManager.h>
26#include <thrift/server/TThreadPoolServer.h>
27#include <thrift/transport/TBufferTransports.h>
28#include <thrift/transport/TServerSocket.h>
Roger Meier2be7f242012-05-10 09:01:45 +000029
Konrad Grochowski240120c2014-11-18 11:33:31 +010030namespace apache { namespace thrift { namespace test {
Roger Meier2be7f242012-05-10 09:01:45 +000031
32void ServerThread::start() {
33 assert(!running_);
34 running_ = true;
35
36 // Start the other thread
37 concurrency::PosixThreadFactory threadFactory;
38 threadFactory.setDetached(false);
39 thread_ = threadFactory.newThread(helper_);
40
41 thread_->start();
42
43 // Wait on the other thread to tell us that it has successfully
44 // bound to the port and started listening (or until an error occurs).
45 concurrency::Synchronized s(serverMonitor_);
46 while (!serving_ && !error_) {
47 serverMonitor_.waitForever();
48 }
49
50 if (error_) {
Konrad Grochowski240120c2014-11-18 11:33:31 +010051 throw transport::TTransportException(
52 transport::TTransportException::NOT_OPEN,
53 "failed to bind on server socket");
Roger Meier2be7f242012-05-10 09:01:45 +000054 }
55}
56
57void ServerThread::stop() {
58 if (!running_) {
59 return;
60 }
61
62 // Tell the server to stop
63 server_->stop();
64 running_ = false;
65
66 // Wait for the server thread to exit
67 //
68 // Note: this only works if all client connections have closed. The servers
69 // generally wait for everything to be closed before exiting; there currently
70 // isn't a way to tell them to just exit now, and shut down existing
71 // connections.
72 thread_->join();
73}
74
75void ServerThread::run() {
76 /*
77 * Try binding to several ports, in case the one we want is already in use.
78 */
79 port_ = 12345;
80 unsigned int maxRetries = 10;
81 for (unsigned int n = 0; n < maxRetries; ++n) {
82 // Create the server
83 server_ = serverState_->createServer(port_);
84 // Install our helper as the server event handler, so that our
85 // preServe() method will be called once we've successfully bound to
86 // the port and are about to start listening.
87 server_->setServerEventHandler(helper_);
88
89 try {
90 // Try to serve requests
91 server_->serve();
92 } catch (const TException& x) {
93 // TNonblockingServer throws a generic TException if it fails to bind.
94 // If we get a TException, we'll optimistically assume the bind failed.
95 ++port_;
96 continue;
97 }
98
99 // Seriously? serve() is pretty lame. If it fails to start serving it
100 // just returns rather than throwing an exception.
101 //
102 // We have to use our preServe() hook to tell if serve() successfully
103 // started serving and is returning because stop() is called, or if it just
104 // failed to start serving in the first place.
105 concurrency::Synchronized s(serverMonitor_);
106 if (serving_) {
107 // Oh good, we started serving and are exiting because
108 // we're trying to stop.
109 serving_ = false;
110 return;
111 } else {
112 // We never started serving, probably because we failed to bind to the
113 // port. Increment the port number and try again.
114 ++port_;
115 continue;
116 }
117 }
118
119 // We failed to bind on any port.
120 concurrency::Synchronized s(serverMonitor_);
121 error_ = true;
122 serverMonitor_.notify();
123}
124
125void ServerThread::preServe() {
126 // We bound to the port successfully, and are about to start serving requests
127 serverState_->bindSuccessful(port_);
128
129 // Set the real server event handler (replacing ourself)
Konrad Grochowski240120c2014-11-18 11:33:31 +0100130 boost::shared_ptr<server::TServerEventHandler> serverEventHandler =
131 serverState_->getServerEventHandler();
Roger Meier2be7f242012-05-10 09:01:45 +0000132 server_->setServerEventHandler(serverEventHandler);
133
134 // Notify the main thread that we have successfully started serving requests
135 concurrency::Synchronized s(serverMonitor_);
136 serving_ = true;
137 serverMonitor_.notify();
138
139 // Invoke preServe() on the real event handler, since we ate
140 // the original preServe() event.
141 if (serverEventHandler) {
142 serverEventHandler->preServe();
143 }
144}
Konrad Grochowski240120c2014-11-18 11:33:31 +0100145
146}}} // apache::thrift::test
Roger Meier2be7f242012-05-10 09:01:45 +0000147
148#endif // _THRIFT_TEST_SERVERTHREAD_TCC_