blob: e9d468f2fe143fa4fa52e49c91a730b45d693421 [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
Nobuaki Sukegawa28256642014-12-16 03:24:37 +090024#include <thrift/concurrency/PlatformThreadFactory.h>
Roger Meier2b1a5282012-05-11 10:12:39 +000025#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 Grochowski16a23a62014-11-13 15:33:38 +010030namespace apache {
31namespace thrift {
32namespace test {
Roger Meier2be7f242012-05-10 09:01:45 +000033
34void ServerThread::start() {
35 assert(!running_);
36 running_ = true;
37
38 // Start the other thread
Nobuaki Sukegawa28256642014-12-16 03:24:37 +090039 concurrency::PlatformThreadFactory threadFactory;
Roger Meier2be7f242012-05-10 09:01:45 +000040 threadFactory.setDetached(false);
41 thread_ = threadFactory.newThread(helper_);
42
43 thread_->start();
44
45 // Wait on the other thread to tell us that it has successfully
46 // bound to the port and started listening (or until an error occurs).
47 concurrency::Synchronized s(serverMonitor_);
48 while (!serving_ && !error_) {
49 serverMonitor_.waitForever();
50 }
51
52 if (error_) {
Konrad Grochowski16a23a62014-11-13 15:33:38 +010053 throw transport::TTransportException(transport::TTransportException::NOT_OPEN,
54 "failed to bind on server socket");
Roger Meier2be7f242012-05-10 09:01:45 +000055 }
56}
57
58void ServerThread::stop() {
59 if (!running_) {
60 return;
61 }
62
63 // Tell the server to stop
64 server_->stop();
65 running_ = false;
66
67 // Wait for the server thread to exit
68 //
69 // Note: this only works if all client connections have closed. The servers
70 // generally wait for everything to be closed before exiting; there currently
71 // isn't a way to tell them to just exit now, and shut down existing
72 // connections.
73 thread_->join();
74}
75
76void ServerThread::run() {
77 /*
78 * Try binding to several ports, in case the one we want is already in use.
79 */
80 port_ = 12345;
81 unsigned int maxRetries = 10;
82 for (unsigned int n = 0; n < maxRetries; ++n) {
83 // Create the server
84 server_ = serverState_->createServer(port_);
85 // Install our helper as the server event handler, so that our
86 // preServe() method will be called once we've successfully bound to
87 // the port and are about to start listening.
88 server_->setServerEventHandler(helper_);
89
90 try {
91 // Try to serve requests
92 server_->serve();
ben-craigfae08e72015-07-15 11:34:47 -050093 } catch (const TException&) {
Roger Meier2be7f242012-05-10 09:01:45 +000094 // TNonblockingServer throws a generic TException if it fails to bind.
95 // If we get a TException, we'll optimistically assume the bind failed.
96 ++port_;
97 continue;
98 }
99
100 // Seriously? serve() is pretty lame. If it fails to start serving it
101 // just returns rather than throwing an exception.
102 //
103 // We have to use our preServe() hook to tell if serve() successfully
104 // started serving and is returning because stop() is called, or if it just
105 // failed to start serving in the first place.
106 concurrency::Synchronized s(serverMonitor_);
107 if (serving_) {
108 // Oh good, we started serving and are exiting because
109 // we're trying to stop.
110 serving_ = false;
111 return;
112 } else {
113 // We never started serving, probably because we failed to bind to the
114 // port. Increment the port number and try again.
115 ++port_;
116 continue;
117 }
118 }
119
120 // We failed to bind on any port.
121 concurrency::Synchronized s(serverMonitor_);
122 error_ = true;
123 serverMonitor_.notify();
124}
125
126void ServerThread::preServe() {
127 // We bound to the port successfully, and are about to start serving requests
128 serverState_->bindSuccessful(port_);
129
130 // Set the real server event handler (replacing ourself)
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100131 boost::shared_ptr<server::TServerEventHandler> serverEventHandler
132 = serverState_->getServerEventHandler();
Roger Meier2be7f242012-05-10 09:01:45 +0000133 server_->setServerEventHandler(serverEventHandler);
134
135 // Notify the main thread that we have successfully started serving requests
136 concurrency::Synchronized s(serverMonitor_);
137 serving_ = true;
138 serverMonitor_.notify();
139
140 // Invoke preServe() on the real event handler, since we ate
141 // the original preServe() event.
142 if (serverEventHandler) {
143 serverEventHandler->preServe();
144 }
145}
Konrad Grochowski16a23a62014-11-13 15:33:38 +0100146}
147}
148} // apache::thrift::test
Roger Meier2be7f242012-05-10 09:01:45 +0000149
150#endif // _THRIFT_TEST_SERVERTHREAD_TCC_