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