winlin

refine the thread to three category.

@@ -41,12 +41,11 @@ ISrsAsyncCallTask::~ISrsAsyncCallTask() @@ -41,12 +41,11 @@ ISrsAsyncCallTask::~ISrsAsyncCallTask()
41 41
42 SrsAsyncCallWorker::SrsAsyncCallWorker() 42 SrsAsyncCallWorker::SrsAsyncCallWorker()
43 { 43 {
44 - pthread = new SrsThread("async", this, SRS_AUTO_ASYNC_CALLBACL_SLEEP_US, true); 44 + pthread = new SrsReusableThread("async", this, SRS_AUTO_ASYNC_CALLBACL_SLEEP_US);
45 } 45 }
46 46
47 SrsAsyncCallWorker::~SrsAsyncCallWorker() 47 SrsAsyncCallWorker::~SrsAsyncCallWorker()
48 { 48 {
49 - stop();  
50 srs_freep(pthread); 49 srs_freep(pthread);
51 50
52 std::vector<ISrsAsyncCallTask*>::iterator it; 51 std::vector<ISrsAsyncCallTask*>::iterator it;
@@ -57,10 +57,10 @@ public: @@ -57,10 +57,10 @@ public:
57 * when worker call with the task, the worker will do it in isolate thread. 57 * when worker call with the task, the worker will do it in isolate thread.
58 * that is, the task is execute/call in async mode. 58 * that is, the task is execute/call in async mode.
59 */ 59 */
60 -class SrsAsyncCallWorker : public ISrsThreadHandler 60 +class SrsAsyncCallWorker : public ISrsReusableThreadHandler
61 { 61 {
62 private: 62 private:
63 - SrsThread* pthread; 63 + SrsReusableThread* pthread;
64 std::vector<ISrsAsyncCallTask*> tasks; 64 std::vector<ISrsAsyncCallTask*> tasks;
65 public: 65 public:
66 SrsAsyncCallWorker(); 66 SrsAsyncCallWorker();
@@ -70,6 +70,8 @@ public: @@ -70,6 +70,8 @@ public:
70 public: 70 public:
71 virtual int start(); 71 virtual int start();
72 virtual void stop(); 72 virtual void stop();
  73 +// interface ISrsReusableThreadHandler
  74 +public:
73 virtual int cycle(); 75 virtual int cycle();
74 }; 76 };
75 77
@@ -45,12 +45,17 @@ SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c) @@ -45,12 +45,17 @@ SrsConnection::SrsConnection(IConnectionManager* cm, st_netfd_t c)
45 // so we never use joinable. 45 // so we never use joinable.
46 // TODO: FIXME: maybe other thread need to stop it. 46 // TODO: FIXME: maybe other thread need to stop it.
47 // @see: https://github.com/simple-rtmp-server/srs/issues/78 47 // @see: https://github.com/simple-rtmp-server/srs/issues/78
48 - pthread = new SrsThread("conn", this, 0, false); 48 + pthread = new SrsOneCycleThread("conn", this);
49 } 49 }
50 50
51 SrsConnection::~SrsConnection() 51 SrsConnection::~SrsConnection()
52 { 52 {
53 - stop(); 53 + /**
  54 + * when delete the connection, stop the connection,
  55 + * close the underlayer socket, delete the thread.
  56 + */
  57 + srs_close_stfd(stfd);
  58 + srs_freep(pthread);
54 } 59 }
55 60
56 int SrsConnection::start() 61 int SrsConnection::start()
@@ -83,9 +88,6 @@ int SrsConnection::cycle() @@ -83,9 +88,6 @@ int SrsConnection::cycle()
83 if (ret == ERROR_SOCKET_CLOSED) { 88 if (ret == ERROR_SOCKET_CLOSED) {
84 srs_warn("client disconnect peer. ret=%d", ret); 89 srs_warn("client disconnect peer. ret=%d", ret);
85 } 90 }
86 -  
87 - // set loop to stop to quit.  
88 - pthread->stop_loop();  
89 91
90 return ERROR_SUCCESS; 92 return ERROR_SUCCESS;
91 } 93 }
@@ -101,10 +103,4 @@ int SrsConnection::srs_id() @@ -101,10 +103,4 @@ int SrsConnection::srs_id()
101 return id; 103 return id;
102 } 104 }
103 105
104 -void SrsConnection::stop()  
105 -{  
106 - srs_close_stfd(stfd);  
107 - srs_freep(pthread);  
108 -}  
109 -  
110 106
@@ -58,14 +58,14 @@ public: @@ -58,14 +58,14 @@ public:
58 * all connections accept from listener must extends from this base class, 58 * all connections accept from listener must extends from this base class,
59 * server will add the connection to manager, and delete it when remove. 59 * server will add the connection to manager, and delete it when remove.
60 */ 60 */
61 -class SrsConnection : public virtual ISrsThreadHandler, public virtual IKbpsDelta 61 +class SrsConnection : public virtual ISrsOneCycleThreadHandler, public virtual IKbpsDelta
62 { 62 {
63 private: 63 private:
64 /** 64 /**
65 * each connection start a green thread, 65 * each connection start a green thread,
66 * when thread stop, the connection will be delete by server. 66 * when thread stop, the connection will be delete by server.
67 */ 67 */
68 - SrsThread* pthread; 68 + SrsOneCycleThread* pthread;
69 /** 69 /**
70 * the id of connection. 70 * the id of connection.
71 */ 71 */
@@ -97,6 +97,8 @@ public: @@ -97,6 +97,8 @@ public:
97 * to remove the client by server->remove(this). 97 * to remove the client by server->remove(this).
98 */ 98 */
99 virtual int start(); 99 virtual int start();
  100 +// interface ISrsOneCycleThreadHandler
  101 +public:
100 /** 102 /**
101 * the thread cycle function, 103 * the thread cycle function,
102 * when serve connection completed, terminate the loop which will terminate the thread, 104 * when serve connection completed, terminate the loop which will terminate the thread,
@@ -119,12 +121,6 @@ protected: @@ -119,12 +121,6 @@ protected:
119 * for concrete connection to do the cycle. 121 * for concrete connection to do the cycle.
120 */ 122 */
121 virtual int do_cycle() = 0; 123 virtual int do_cycle() = 0;
122 -private:  
123 - /**  
124 - * when delete the connection, stop the connection,  
125 - * close the underlayer socket, delete the thread.  
126 - */  
127 - virtual void stop();  
128 }; 124 };
129 125
130 #endif 126 #endif
@@ -70,7 +70,7 @@ SrsEdgeIngester::SrsEdgeIngester() @@ -70,7 +70,7 @@ SrsEdgeIngester::SrsEdgeIngester()
70 origin_index = 0; 70 origin_index = 0;
71 stream_id = 0; 71 stream_id = 0;
72 stfd = NULL; 72 stfd = NULL;
73 - pthread = new SrsThread("edge-igs", this, SRS_EDGE_INGESTER_SLEEP_US, true); 73 + pthread = new SrsReusableThread("edge-igs", this, SRS_EDGE_INGESTER_SLEEP_US);
74 } 74 }
75 75
76 SrsEdgeIngester::~SrsEdgeIngester() 76 SrsEdgeIngester::~SrsEdgeIngester()
@@ -171,7 +171,7 @@ int SrsEdgeIngester::ingest() @@ -171,7 +171,7 @@ int SrsEdgeIngester::ingest()
171 SrsPithyPrint* pprint = SrsPithyPrint::create_edge(); 171 SrsPithyPrint* pprint = SrsPithyPrint::create_edge();
172 SrsAutoFree(SrsPithyPrint, pprint); 172 SrsAutoFree(SrsPithyPrint, pprint);
173 173
174 - while (pthread->can_loop()) { 174 + while (!pthread->interrupted()) {
175 pprint->elapse(); 175 pprint->elapse();
176 176
177 // pithy print 177 // pithy print
@@ -397,7 +397,7 @@ SrsEdgeForwarder::SrsEdgeForwarder() @@ -397,7 +397,7 @@ SrsEdgeForwarder::SrsEdgeForwarder()
397 origin_index = 0; 397 origin_index = 0;
398 stream_id = 0; 398 stream_id = 0;
399 stfd = NULL; 399 stfd = NULL;
400 - pthread = new SrsThread("edge-fwr", this, SRS_EDGE_FORWARDER_SLEEP_US, true); 400 + pthread = new SrsReusableThread("edge-fwr", this, SRS_EDGE_FORWARDER_SLEEP_US);
401 queue = new SrsMessageQueue(); 401 queue = new SrsMessageQueue();
402 send_error_code = ERROR_SUCCESS; 402 send_error_code = ERROR_SUCCESS;
403 } 403 }
@@ -489,7 +489,7 @@ int SrsEdgeForwarder::cycle() @@ -489,7 +489,7 @@ int SrsEdgeForwarder::cycle()
489 489
490 SrsMessageArray msgs(SYS_MAX_EDGE_SEND_MSGS); 490 SrsMessageArray msgs(SYS_MAX_EDGE_SEND_MSGS);
491 491
492 - while (pthread->can_loop()) { 492 + while (!pthread->interrupted()) {
493 if (send_error_code != ERROR_SUCCESS) { 493 if (send_error_code != ERROR_SUCCESS) {
494 st_usleep(SRS_EDGE_FORWARDER_ERROR_US); 494 st_usleep(SRS_EDGE_FORWARDER_ERROR_US);
495 continue; 495 continue;
@@ -75,7 +75,7 @@ enum SrsEdgeUserState @@ -75,7 +75,7 @@ enum SrsEdgeUserState
75 /** 75 /**
76 * edge used to ingest stream from origin. 76 * edge used to ingest stream from origin.
77 */ 77 */
78 -class SrsEdgeIngester : public ISrsThreadHandler 78 +class SrsEdgeIngester : public ISrsReusableThreadHandler
79 { 79 {
80 private: 80 private:
81 int stream_id; 81 int stream_id;
@@ -83,7 +83,7 @@ private: @@ -83,7 +83,7 @@ private:
83 SrsSource* _source; 83 SrsSource* _source;
84 SrsPlayEdge* _edge; 84 SrsPlayEdge* _edge;
85 SrsRequest* _req; 85 SrsRequest* _req;
86 - SrsThread* pthread; 86 + SrsReusableThread* pthread;
87 st_netfd_t stfd; 87 st_netfd_t stfd;
88 ISrsProtocolReaderWriter* io; 88 ISrsProtocolReaderWriter* io;
89 SrsKbps* kbps; 89 SrsKbps* kbps;
@@ -96,7 +96,7 @@ public: @@ -96,7 +96,7 @@ public:
96 virtual int initialize(SrsSource* source, SrsPlayEdge* edge, SrsRequest* req); 96 virtual int initialize(SrsSource* source, SrsPlayEdge* edge, SrsRequest* req);
97 virtual int start(); 97 virtual int start();
98 virtual void stop(); 98 virtual void stop();
99 -// interface ISrsThreadHandler 99 +// interface ISrsReusableThreadHandler
100 public: 100 public:
101 virtual int cycle(); 101 virtual int cycle();
102 private: 102 private:
@@ -110,7 +110,7 @@ private: @@ -110,7 +110,7 @@ private:
110 /** 110 /**
111 * edge used to forward stream to origin. 111 * edge used to forward stream to origin.
112 */ 112 */
113 -class SrsEdgeForwarder : public ISrsThreadHandler 113 +class SrsEdgeForwarder : public ISrsReusableThreadHandler
114 { 114 {
115 private: 115 private:
116 int stream_id; 116 int stream_id;
@@ -118,7 +118,7 @@ private: @@ -118,7 +118,7 @@ private:
118 SrsSource* _source; 118 SrsSource* _source;
119 SrsPublishEdge* _edge; 119 SrsPublishEdge* _edge;
120 SrsRequest* _req; 120 SrsRequest* _req;
121 - SrsThread* pthread; 121 + SrsReusableThread* pthread;
122 st_netfd_t stfd; 122 st_netfd_t stfd;
123 ISrsProtocolReaderWriter* io; 123 ISrsProtocolReaderWriter* io;
124 SrsKbps* kbps; 124 SrsKbps* kbps;
@@ -144,7 +144,7 @@ public: @@ -144,7 +144,7 @@ public:
144 virtual int initialize(SrsSource* source, SrsPublishEdge* edge, SrsRequest* req); 144 virtual int initialize(SrsSource* source, SrsPublishEdge* edge, SrsRequest* req);
145 virtual int start(); 145 virtual int start();
146 virtual void stop(); 146 virtual void stop();
147 -// interface ISrsThreadHandler 147 +// interface ISrsReusableThreadHandler
148 public: 148 public:
149 virtual int cycle(); 149 virtual int cycle();
150 public: 150 public:
@@ -44,7 +44,7 @@ static std::vector<std::string> _transcoded_url; @@ -44,7 +44,7 @@ static std::vector<std::string> _transcoded_url;
44 44
45 SrsEncoder::SrsEncoder() 45 SrsEncoder::SrsEncoder()
46 { 46 {
47 - pthread = new SrsThread("encoder", this, SRS_RTMP_ENCODER_SLEEP_US, true); 47 + pthread = new SrsReusableThread("encoder", this, SRS_RTMP_ENCODER_SLEEP_US);
48 pprint = SrsPithyPrint::create_encoder(); 48 pprint = SrsPithyPrint::create_encoder();
49 } 49 }
50 50
@@ -45,13 +45,13 @@ class SrsFFMPEG; @@ -45,13 +45,13 @@ class SrsFFMPEG;
45 * the encoder for a stream, 45 * the encoder for a stream,
46 * may use multiple ffmpegs to transcode the specified stream. 46 * may use multiple ffmpegs to transcode the specified stream.
47 */ 47 */
48 -class SrsEncoder : public ISrsThreadHandler 48 +class SrsEncoder : public ISrsReusableThreadHandler
49 { 49 {
50 private: 50 private:
51 std::string input_stream_name; 51 std::string input_stream_name;
52 std::vector<SrsFFMPEG*> ffmpegs; 52 std::vector<SrsFFMPEG*> ffmpegs;
53 private: 53 private:
54 - SrsThread* pthread; 54 + SrsReusableThread* pthread;
55 SrsPithyPrint* pprint; 55 SrsPithyPrint* pprint;
56 public: 56 public:
57 SrsEncoder(); 57 SrsEncoder();
@@ -59,7 +59,7 @@ public: @@ -59,7 +59,7 @@ public:
59 public: 59 public:
60 virtual int on_publish(SrsRequest* req); 60 virtual int on_publish(SrsRequest* req);
61 virtual void on_unpublish(); 61 virtual void on_unpublish();
62 -// interface ISrsThreadHandler. 62 +// interface ISrsReusableThreadHandler.
63 public: 63 public:
64 virtual int cycle(); 64 virtual int cycle();
65 virtual void on_thread_stop(); 65 virtual void on_thread_stop();
@@ -59,7 +59,7 @@ SrsForwarder::SrsForwarder(SrsSource* _source) @@ -59,7 +59,7 @@ SrsForwarder::SrsForwarder(SrsSource* _source)
59 kbps = new SrsKbps(); 59 kbps = new SrsKbps();
60 stream_id = 0; 60 stream_id = 0;
61 61
62 - pthread = new SrsThread("forward", this, SRS_FORWARDER_SLEEP_US, true); 62 + pthread = new SrsReusableThread("forward", this, SRS_FORWARDER_SLEEP_US);
63 queue = new SrsMessageQueue(); 63 queue = new SrsMessageQueue();
64 jitter = new SrsRtmpJitter(); 64 jitter = new SrsRtmpJitter();
65 65
@@ -407,7 +407,7 @@ int SrsForwarder::forward() @@ -407,7 +407,7 @@ int SrsForwarder::forward()
407 } 407 }
408 } 408 }
409 409
410 - while (pthread->can_loop()) { 410 + while (!pthread->interrupted()) {
411 pprint->elapse(); 411 pprint->elapse();
412 412
413 // read from client. 413 // read from client.
@@ -48,7 +48,7 @@ class SrsKbps; @@ -48,7 +48,7 @@ class SrsKbps;
48 * forward the stream to other servers. 48 * forward the stream to other servers.
49 */ 49 */
50 // TODO: FIXME: refine the error log, comments it. 50 // TODO: FIXME: refine the error log, comments it.
51 -class SrsForwarder : public ISrsThreadHandler 51 +class SrsForwarder : public ISrsReusableThreadHandler
52 { 52 {
53 private: 53 private:
54 // the ep to forward, server[:port]. 54 // the ep to forward, server[:port].
@@ -57,7 +57,7 @@ private: @@ -57,7 +57,7 @@ private:
57 int stream_id; 57 int stream_id;
58 private: 58 private:
59 st_netfd_t stfd; 59 st_netfd_t stfd;
60 - SrsThread* pthread; 60 + SrsReusableThread* pthread;
61 private: 61 private:
62 SrsSource* source; 62 SrsSource* source;
63 ISrsProtocolReaderWriter* io; 63 ISrsProtocolReaderWriter* io;
@@ -95,7 +95,7 @@ public: @@ -95,7 +95,7 @@ public:
95 * @param shared_video, directly ptr, copy it if need to save it. 95 * @param shared_video, directly ptr, copy it if need to save it.
96 */ 96 */
97 virtual int on_video(SrsSharedPtrMessage* shared_video); 97 virtual int on_video(SrsSharedPtrMessage* shared_video);
98 -// interface ISrsThreadHandler. 98 +// interface ISrsReusableThreadHandler.
99 public: 99 public:
100 virtual int cycle(); 100 virtual int cycle();
101 private: 101 private:
@@ -1157,12 +1157,11 @@ SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r) @@ -1157,12 +1157,11 @@ SrsStreamCache::SrsStreamCache(SrsSource* s, SrsRequest* r)
1157 req = r->copy(); 1157 req = r->copy();
1158 source = s; 1158 source = s;
1159 queue = new SrsMessageQueue(true); 1159 queue = new SrsMessageQueue(true);
1160 - pthread = new SrsThread("http-stream", this, 0, false); 1160 + pthread = new SrsEndlessThread("http-stream", this);
1161 } 1161 }
1162 1162
1163 SrsStreamCache::~SrsStreamCache() 1163 SrsStreamCache::~SrsStreamCache()
1164 { 1164 {
1165 - pthread->stop();  
1166 srs_freep(pthread); 1165 srs_freep(pthread);
1167 1166
1168 srs_freep(queue); 1167 srs_freep(queue);
@@ -386,20 +386,20 @@ protected: @@ -386,20 +386,20 @@ protected:
386 * for example, the audio stream cache to make android(weixin) happy. 386 * for example, the audio stream cache to make android(weixin) happy.
387 * we start a thread to shrink the queue. 387 * we start a thread to shrink the queue.
388 */ 388 */
389 -class SrsStreamCache : public ISrsThreadHandler 389 +class SrsStreamCache : public ISrsEndlessThreadHandler
390 { 390 {
391 private: 391 private:
392 SrsMessageQueue* queue; 392 SrsMessageQueue* queue;
393 SrsSource* source; 393 SrsSource* source;
394 SrsRequest* req; 394 SrsRequest* req;
395 - SrsThread* pthread; 395 + SrsEndlessThread* pthread;
396 public: 396 public:
397 SrsStreamCache(SrsSource* s, SrsRequest* r); 397 SrsStreamCache(SrsSource* s, SrsRequest* r);
398 virtual ~SrsStreamCache(); 398 virtual ~SrsStreamCache();
399 public: 399 public:
400 virtual int start(); 400 virtual int start();
401 virtual int dump_cache(SrsConsumer* consumer); 401 virtual int dump_cache(SrsConsumer* consumer);
402 -// interface ISrsThreadHandler. 402 +// interface ISrsEndlessThreadHandler.
403 public: 403 public:
404 virtual int cycle(); 404 virtual int cycle();
405 }; 405 };
@@ -669,7 +669,7 @@ public: @@ -669,7 +669,7 @@ public:
669 virtual int hls_update_m3u8(SrsRequest* r, std::string m3u8); 669 virtual int hls_update_m3u8(SrsRequest* r, std::string m3u8);
670 virtual int hls_update_ts(SrsRequest* r, std::string uri, std::string ts); 670 virtual int hls_update_ts(SrsRequest* r, std::string uri, std::string ts);
671 virtual void unmount_hls(SrsRequest* r); 671 virtual void unmount_hls(SrsRequest* r);
672 -// interface ISrsThreadHandler. 672 +// interface ISrsReloadHandler.
673 public: 673 public:
674 virtual int on_reload_vhost_http_updated(); 674 virtual int on_reload_vhost_http_updated();
675 virtual int on_reload_vhost_http_remux_updated(); 675 virtual int on_reload_vhost_http_remux_updated();
@@ -55,7 +55,7 @@ SrsIngester::SrsIngester() @@ -55,7 +55,7 @@ SrsIngester::SrsIngester()
55 { 55 {
56 _srs_config->subscribe(this); 56 _srs_config->subscribe(this);
57 57
58 - pthread = new SrsThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US, true); 58 + pthread = new SrsReusableThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US);
59 pprint = SrsPithyPrint::create_ingester(); 59 pprint = SrsPithyPrint::create_ingester();
60 } 60 }
61 61
@@ -59,12 +59,12 @@ public: @@ -59,12 +59,12 @@ public:
59 * encode with FFMPEG(optional), 59 * encode with FFMPEG(optional),
60 * push to SRS(or any RTMP server) over RTMP. 60 * push to SRS(or any RTMP server) over RTMP.
61 */ 61 */
62 -class SrsIngester : public ISrsThreadHandler, public ISrsReloadHandler 62 +class SrsIngester : public ISrsReusableThreadHandler, public ISrsReloadHandler
63 { 63 {
64 private: 64 private:
65 std::vector<SrsIngesterFFMPEG*> ingesters; 65 std::vector<SrsIngesterFFMPEG*> ingesters;
66 private: 66 private:
67 - SrsThread* pthread; 67 + SrsReusableThread* pthread;
68 SrsPithyPrint* pprint; 68 SrsPithyPrint* pprint;
69 public: 69 public:
70 SrsIngester(); 70 SrsIngester();
@@ -72,7 +72,7 @@ public: @@ -72,7 +72,7 @@ public:
72 public: 72 public:
73 virtual int start(); 73 virtual int start();
74 virtual void stop(); 74 virtual void stop();
75 -// interface ISrsThreadHandler. 75 +// interface ISrsReusableThreadHandler.
76 public: 76 public:
77 virtual int cycle(); 77 virtual int cycle();
78 virtual void on_thread_stop(); 78 virtual void on_thread_stop();
@@ -79,7 +79,7 @@ SrsUdpListener::SrsUdpListener(ISrsUdpHandler* h, string i, int p) @@ -79,7 +79,7 @@ SrsUdpListener::SrsUdpListener(ISrsUdpHandler* h, string i, int p)
79 nb_buf = SRS_UDP_MAX_PACKET_SIZE; 79 nb_buf = SRS_UDP_MAX_PACKET_SIZE;
80 buf = new char[nb_buf]; 80 buf = new char[nb_buf];
81 81
82 - pthread = new SrsThread("udp", this, 0, true); 82 + pthread = new SrsReusableThread("udp", this);
83 } 83 }
84 84
85 SrsUdpListener::~SrsUdpListener() 85 SrsUdpListener::~SrsUdpListener()
@@ -157,7 +157,7 @@ int SrsUdpListener::cycle() @@ -157,7 +157,7 @@ int SrsUdpListener::cycle()
157 { 157 {
158 int ret = ERROR_SUCCESS; 158 int ret = ERROR_SUCCESS;
159 159
160 - while (pthread->can_loop()) { 160 + while (!pthread->interrupted()) {
161 // TODO: FIXME: support ipv6, @see man 7 ipv6 161 // TODO: FIXME: support ipv6, @see man 7 ipv6
162 sockaddr_in from; 162 sockaddr_in from;
163 int nb_from = sizeof(sockaddr_in); 163 int nb_from = sizeof(sockaddr_in);
@@ -190,7 +190,7 @@ SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p) @@ -190,7 +190,7 @@ SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p)
190 _fd = -1; 190 _fd = -1;
191 _stfd = NULL; 191 _stfd = NULL;
192 192
193 - pthread = new SrsThread("tcp", this, 0, true); 193 + pthread = new SrsReusableThread("tcp", this);
194 } 194 }
195 195
196 SrsTcpListener::~SrsTcpListener() 196 SrsTcpListener::~SrsTcpListener()
@@ -82,12 +82,12 @@ public: @@ -82,12 +82,12 @@ public:
82 /** 82 /**
83 * bind udp port, start thread to recv packet and handler it. 83 * bind udp port, start thread to recv packet and handler it.
84 */ 84 */
85 -class SrsUdpListener : public ISrsThreadHandler 85 +class SrsUdpListener : public ISrsReusableThreadHandler
86 { 86 {
87 private: 87 private:
88 int _fd; 88 int _fd;
89 st_netfd_t _stfd; 89 st_netfd_t _stfd;
90 - SrsThread* pthread; 90 + SrsReusableThread* pthread;
91 private: 91 private:
92 char* buf; 92 char* buf;
93 int nb_buf; 93 int nb_buf;
@@ -103,7 +103,7 @@ public: @@ -103,7 +103,7 @@ public:
103 virtual st_netfd_t stfd(); 103 virtual st_netfd_t stfd();
104 public: 104 public:
105 virtual int listen(); 105 virtual int listen();
106 -// interface ISrsThreadHandler. 106 +// interface ISrsReusableThreadHandler.
107 public: 107 public:
108 virtual int cycle(); 108 virtual int cycle();
109 }; 109 };
@@ -111,12 +111,12 @@ public: @@ -111,12 +111,12 @@ public:
111 /** 111 /**
112 * bind and listen tcp port, use handler to process the client. 112 * bind and listen tcp port, use handler to process the client.
113 */ 113 */
114 -class SrsTcpListener : public ISrsThreadHandler 114 +class SrsTcpListener : public ISrsReusableThreadHandler
115 { 115 {
116 private: 116 private:
117 int _fd; 117 int _fd;
118 st_netfd_t _stfd; 118 st_netfd_t _stfd;
119 - SrsThread* pthread; 119 + SrsReusableThread* pthread;
120 private: 120 private:
121 ISrsTcpHandler* handler; 121 ISrsTcpHandler* handler;
122 std::string ip; 122 std::string ip;
@@ -128,7 +128,7 @@ public: @@ -128,7 +128,7 @@ public:
128 virtual int fd(); 128 virtual int fd();
129 public: 129 public:
130 virtual int listen(); 130 virtual int listen();
131 -// interface ISrsThreadHandler. 131 +// interface ISrsReusableThreadHandler.
132 public: 132 public:
133 virtual int cycle(); 133 virtual int cycle();
134 }; 134 };
@@ -82,7 +82,7 @@ public: @@ -82,7 +82,7 @@ public:
82 virtual void trace(const char* tag, int context_id, const char* fmt, ...); 82 virtual void trace(const char* tag, int context_id, const char* fmt, ...);
83 virtual void warn(const char* tag, int context_id, const char* fmt, ...); 83 virtual void warn(const char* tag, int context_id, const char* fmt, ...);
84 virtual void error(const char* tag, int context_id, const char* fmt, ...); 84 virtual void error(const char* tag, int context_id, const char* fmt, ...);
85 -// interface ISrsThreadHandler. 85 +// interface ISrsReloadHandler.
86 public: 86 public:
87 virtual int on_reload_log_tank(); 87 virtual int on_reload_log_tank();
88 virtual int on_reload_log_level(); 88 virtual int on_reload_log_level();
@@ -50,7 +50,7 @@ SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtm @@ -50,7 +50,7 @@ SrsRecvThread::SrsRecvThread(ISrsMessageHandler* msg_handler, SrsRtmpServer* rtm
50 timeout = timeout_ms; 50 timeout = timeout_ms;
51 handler = msg_handler; 51 handler = msg_handler;
52 rtmp = rtmp_sdk; 52 rtmp = rtmp_sdk;
53 - trd = new SrsThread("recv", this, 0, true); 53 + trd = new SrsReusableThread("recv", this);
54 } 54 }
55 55
56 SrsRecvThread::~SrsRecvThread() 56 SrsRecvThread::~SrsRecvThread()
@@ -76,7 +76,7 @@ int SrsRecvThread::cycle() @@ -76,7 +76,7 @@ int SrsRecvThread::cycle()
76 { 76 {
77 int ret = ERROR_SUCCESS; 77 int ret = ERROR_SUCCESS;
78 78
79 - while (trd->can_loop()) { 79 + while (!trd->interrupted()) {
80 if (!handler->can_handle()) { 80 if (!handler->can_handle()) {
81 st_usleep(timeout * 1000); 81 st_usleep(timeout * 1000);
82 continue; 82 continue;
@@ -96,7 +96,7 @@ int SrsRecvThread::cycle() @@ -96,7 +96,7 @@ int SrsRecvThread::cycle()
96 } 96 }
97 97
98 // we use no timeout to recv, should never got any error. 98 // we use no timeout to recv, should never got any error.
99 - trd->stop_loop(); 99 + trd->interrupt();
100 100
101 // notice the handler got a recv error. 101 // notice the handler got a recv error.
102 handler->on_recv_error(ret); 102 handler->on_recv_error(ret);
@@ -111,7 +111,7 @@ int SrsRecvThread::cycle() @@ -111,7 +111,7 @@ int SrsRecvThread::cycle()
111 111
112 void SrsRecvThread::stop_loop() 112 void SrsRecvThread::stop_loop()
113 { 113 {
114 - trd->stop_loop(); 114 + trd->interrupt();
115 } 115 }
116 116
117 void SrsRecvThread::on_thread_start() 117 void SrsRecvThread::on_thread_start()
@@ -79,10 +79,10 @@ public: @@ -79,10 +79,10 @@ public:
79 /** 79 /**
80 * the recv thread, use message handler to handle each received message. 80 * the recv thread, use message handler to handle each received message.
81 */ 81 */
82 -class SrsRecvThread : public ISrsThreadHandler 82 +class SrsRecvThread : public ISrsReusableThreadHandler
83 { 83 {
84 protected: 84 protected:
85 - SrsThread* trd; 85 + SrsReusableThread* trd;
86 ISrsMessageHandler* handler; 86 ISrsMessageHandler* handler;
87 SrsRtmpServer* rtmp; 87 SrsRtmpServer* rtmp;
88 int timeout; 88 int timeout;
@@ -192,7 +192,7 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) @@ -192,7 +192,7 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o)
192 stfd = fd; 192 stfd = fd;
193 skt = new SrsStSocket(fd); 193 skt = new SrsStSocket(fd);
194 rtsp = new SrsRtspStack(skt); 194 rtsp = new SrsRtspStack(skt);
195 - trd = new SrsThread("rtsp", this, 0, false); 195 + trd = new SrsOneCycleThread("rtsp", this);
196 196
197 req = NULL; 197 req = NULL;
198 io = NULL; 198 io = NULL;
@@ -210,7 +210,6 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o) @@ -210,7 +210,6 @@ SrsRtspConn::SrsRtspConn(SrsRtspCaster* c, st_netfd_t fd, std::string o)
210 SrsRtspConn::~SrsRtspConn() 210 SrsRtspConn::~SrsRtspConn()
211 { 211 {
212 srs_close_stfd(stfd); 212 srs_close_stfd(stfd);
213 - trd->stop();  
214 213
215 srs_freep(video_rtp); 214 srs_freep(video_rtp);
216 srs_freep(audio_rtp); 215 srs_freep(audio_rtp);
@@ -219,7 +218,9 @@ SrsRtspConn::~SrsRtspConn() @@ -219,7 +218,9 @@ SrsRtspConn::~SrsRtspConn()
219 srs_freep(skt); 218 srs_freep(skt);
220 srs_freep(rtsp); 219 srs_freep(rtsp);
221 220
222 - close(); 221 + srs_freep(client);
  222 + srs_freep(io);
  223 + srs_freep(req);
223 224
224 srs_freep(vjitter); 225 srs_freep(vjitter);
225 srs_freep(ajitter); 226 srs_freep(ajitter);
@@ -412,9 +413,6 @@ int SrsRtspConn::cycle() @@ -412,9 +413,6 @@ int SrsRtspConn::cycle()
412 srs_warn("client disconnect peer. ret=%d", ret); 413 srs_warn("client disconnect peer. ret=%d", ret);
413 } 414 }
414 415
415 - // terminate thread in the thread cycle itself.  
416 - trd->stop_loop();  
417 -  
418 return ERROR_SUCCESS; 416 return ERROR_SUCCESS;
419 } 417 }
420 418
@@ -763,14 +761,6 @@ int SrsRtspConn::connect_app(string ep_server, string ep_port) @@ -763,14 +761,6 @@ int SrsRtspConn::connect_app(string ep_server, string ep_port)
763 return ret; 761 return ret;
764 } 762 }
765 763
766 -void SrsRtspConn::close()  
767 -{  
768 - srs_freep(client);  
769 - srs_freep(io);  
770 - srs_freep(req);  
771 - srs_close_stfd(stfd);  
772 -}  
773 -  
774 SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c) 764 SrsRtspCaster::SrsRtspCaster(SrsConfDirective* c)
775 { 765 {
776 // TODO: FIXME: support reload. 766 // TODO: FIXME: support reload.
@@ -113,7 +113,7 @@ public: @@ -113,7 +113,7 @@ public:
113 /** 113 /**
114 * the rtsp connection serve the fd. 114 * the rtsp connection serve the fd.
115 */ 115 */
116 -class SrsRtspConn : public ISrsThreadHandler 116 +class SrsRtspConn : public ISrsOneCycleThreadHandler
117 { 117 {
118 private: 118 private:
119 std::string output_template; 119 std::string output_template;
@@ -136,7 +136,7 @@ private: @@ -136,7 +136,7 @@ private:
136 SrsStSocket* skt; 136 SrsStSocket* skt;
137 SrsRtspStack* rtsp; 137 SrsRtspStack* rtsp;
138 SrsRtspCaster* caster; 138 SrsRtspCaster* caster;
139 - SrsThread* trd; 139 + SrsOneCycleThread* trd;
140 private: 140 private:
141 SrsRequest* req; 141 SrsRequest* req;
142 SrsStSocket* io; 142 SrsStSocket* io;
@@ -163,7 +163,7 @@ private: @@ -163,7 +163,7 @@ private:
163 // internal methods 163 // internal methods
164 public: 164 public:
165 virtual int on_rtp_packet(SrsRtpPacket* pkt, int stream_id); 165 virtual int on_rtp_packet(SrsRtpPacket* pkt, int stream_id);
166 -// interface ISrsThreadHandler 166 +// interface ISrsOneCycleThreadHandler
167 public: 167 public:
168 virtual int cycle(); 168 virtual int cycle();
169 virtual void on_thread_stop(); 169 virtual void on_thread_stop();
@@ -182,8 +182,6 @@ private: @@ -182,8 +182,6 @@ private:
182 // @remark ignore when not connected, reconnect when disconnected. 182 // @remark ignore when not connected, reconnect when disconnected.
183 virtual int connect(); 183 virtual int connect();
184 virtual int connect_app(std::string ep_server, std::string ep_port); 184 virtual int connect_app(std::string ep_server, std::string ep_port);
185 - // close the connected io and rtmp to ready to be re-connect.  
186 - virtual void close();  
187 }; 185 };
188 186
189 /** 187 /**
@@ -367,13 +367,12 @@ SrsSignalManager::SrsSignalManager(SrsServer* server) @@ -367,13 +367,12 @@ SrsSignalManager::SrsSignalManager(SrsServer* server)
367 367
368 _server = server; 368 _server = server;
369 sig_pipe[0] = sig_pipe[1] = -1; 369 sig_pipe[0] = sig_pipe[1] = -1;
370 - pthread = new SrsThread("signal", this, 0, true); 370 + pthread = new SrsEndlessThread("signal", this);
371 signal_read_stfd = NULL; 371 signal_read_stfd = NULL;
372 } 372 }
373 373
374 SrsSignalManager::~SrsSignalManager() 374 SrsSignalManager::~SrsSignalManager()
375 { 375 {
376 - pthread->stop();  
377 srs_freep(pthread); 376 srs_freep(pthread);
378 377
379 srs_close_stfd(signal_read_stfd); 378 srs_close_stfd(signal_read_stfd);
@@ -179,7 +179,7 @@ public: @@ -179,7 +179,7 @@ public:
179 * convert signal to io, 179 * convert signal to io,
180 * @see: st-1.9/docs/notes.html 180 * @see: st-1.9/docs/notes.html
181 */ 181 */
182 -class SrsSignalManager : public ISrsThreadHandler 182 +class SrsSignalManager : public ISrsEndlessThreadHandler
183 { 183 {
184 private: 184 private:
185 /* Per-process pipe which is used as a signal queue. */ 185 /* Per-process pipe which is used as a signal queue. */
@@ -188,14 +188,14 @@ private: @@ -188,14 +188,14 @@ private:
188 st_netfd_t signal_read_stfd; 188 st_netfd_t signal_read_stfd;
189 private: 189 private:
190 SrsServer* _server; 190 SrsServer* _server;
191 - SrsThread* pthread; 191 + SrsEndlessThread* pthread;
192 public: 192 public:
193 SrsSignalManager(SrsServer* server); 193 SrsSignalManager(SrsServer* server);
194 virtual ~SrsSignalManager(); 194 virtual ~SrsSignalManager();
195 public: 195 public:
196 virtual int initialize(); 196 virtual int initialize();
197 virtual int start(); 197 virtual int start();
198 -// interface ISrsThreadHandler. 198 +// interface ISrsEndlessThreadHandler.
199 public: 199 public:
200 virtual int cycle(); 200 virtual int cycle();
201 private: 201 private:
@@ -26,206 +26,337 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -26,206 +26,337 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 #include <srs_kernel_error.hpp> 26 #include <srs_kernel_error.hpp>
27 #include <srs_kernel_log.hpp> 27 #include <srs_kernel_log.hpp>
28 28
29 -ISrsThreadHandler::ISrsThreadHandler() 29 +namespace internal {
  30 + ISrsThreadHandler::ISrsThreadHandler()
  31 + {
  32 + }
  33 +
  34 + ISrsThreadHandler::~ISrsThreadHandler()
  35 + {
  36 + }
  37 +
  38 + void ISrsThreadHandler::on_thread_start()
  39 + {
  40 + }
  41 +
  42 + int ISrsThreadHandler::on_before_cycle()
  43 + {
  44 + int ret = ERROR_SUCCESS;
  45 + return ret;
  46 + }
  47 +
  48 + int ISrsThreadHandler::on_end_cycle()
  49 + {
  50 + int ret = ERROR_SUCCESS;
  51 + return ret;
  52 + }
  53 +
  54 + void ISrsThreadHandler::on_thread_stop()
  55 + {
  56 + }
  57 +
  58 + SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable)
  59 + {
  60 + _name = name;
  61 + handler = thread_handler;
  62 + cycle_interval_us = interval_us;
  63 +
  64 + tid = NULL;
  65 + loop = false;
  66 + really_terminated = true;
  67 + _cid = -1;
  68 + _joinable = joinable;
  69 +
  70 + // in start(), the thread cycle method maybe stop and remove the thread itself,
  71 + // and the thread start() is waiting for the _cid, and segment fault then.
  72 + // @see https://github.com/simple-rtmp-server/srs/issues/110
  73 + // thread will set _cid, callback on_thread_start(), then wait for the can_run signal.
  74 + can_run = false;
  75 + }
  76 +
  77 + SrsThread::~SrsThread()
  78 + {
  79 + stop();
  80 + }
  81 +
  82 + int SrsThread::cid()
  83 + {
  84 + return _cid;
  85 + }
  86 +
  87 + int SrsThread::start()
  88 + {
  89 + int ret = ERROR_SUCCESS;
  90 +
  91 + if(tid) {
  92 + srs_info("thread %s already running.", _name);
  93 + return ret;
  94 + }
  95 +
  96 + if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){
  97 + ret = ERROR_ST_CREATE_CYCLE_THREAD;
  98 + srs_error("st_thread_create failed. ret=%d", ret);
  99 + return ret;
  100 + }
  101 +
  102 + // we set to loop to true for thread to run.
  103 + loop = true;
  104 +
  105 + // wait for cid to ready, for parent thread to get the cid.
  106 + while (_cid < 0 && loop) {
  107 + st_usleep(10 * 1000);
  108 + }
  109 +
  110 + // now, cycle thread can run.
  111 + can_run = true;
  112 +
  113 + return ret;
  114 + }
  115 +
  116 + void SrsThread::stop()
  117 + {
  118 + if (tid) {
  119 + loop = false;
  120 +
  121 + // the interrupt will cause the socket to read/write error,
  122 + // which will terminate the cycle thread.
  123 + st_thread_interrupt(tid);
  124 +
  125 + // when joinable, wait util quit.
  126 + if (_joinable) {
  127 + // wait the thread to exit.
  128 + int ret = st_thread_join(tid, NULL);
  129 + if (ret) {
  130 + srs_warn("core: ignore join thread failed.");
  131 + }
  132 +
  133 + // wait the thread actually terminated.
  134 + // sometimes the thread join return -1, for example,
  135 + // when thread use st_recvfrom, the thread join return -1.
  136 + // so here, we use a variable to ensure the thread stopped.
  137 + while (!really_terminated) {
  138 + st_usleep(10 * 1000);
  139 +
  140 + if (really_terminated) {
  141 + break;
  142 + }
  143 + srs_warn("core: wait thread to actually terminated");
  144 + }
  145 + }
  146 +
  147 + tid = NULL;
  148 + }
  149 + }
  150 +
  151 + bool SrsThread::can_loop()
  152 + {
  153 + return loop;
  154 + }
  155 +
  156 + void SrsThread::stop_loop()
  157 + {
  158 + loop = false;
  159 + }
  160 +
  161 + void SrsThread::thread_cycle()
  162 + {
  163 + int ret = ERROR_SUCCESS;
  164 +
  165 + _srs_context->generate_id();
  166 + srs_info("thread %s cycle start", _name);
  167 +
  168 + _cid = _srs_context->get_id();
  169 +
  170 + srs_assert(handler);
  171 + handler->on_thread_start();
  172 +
  173 + // thread is running now.
  174 + really_terminated = false;
  175 +
  176 + // wait for cid to ready, for parent thread to get the cid.
  177 + while (!can_run && loop) {
  178 + st_usleep(10 * 1000);
  179 + }
  180 +
  181 + while (loop) {
  182 + if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
  183 + srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret);
  184 + goto failed;
  185 + }
  186 + srs_info("thread %s on before cycle success");
  187 +
  188 + if ((ret = handler->cycle()) != ERROR_SUCCESS) {
  189 + if (!srs_is_client_gracefully_close(ret)) {
  190 + srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
  191 + }
  192 + goto failed;
  193 + }
  194 + srs_info("thread %s cycle success", _name);
  195 +
  196 + if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
  197 + srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret);
  198 + goto failed;
  199 + }
  200 + srs_info("thread %s on end cycle success", _name);
  201 +
  202 + failed:
  203 + if (!loop) {
  204 + break;
  205 + }
  206 +
  207 + // to improve performance, donot sleep when interval is zero.
  208 + // @see: https://github.com/simple-rtmp-server/srs/issues/237
  209 + if (cycle_interval_us != 0) {
  210 + st_usleep(cycle_interval_us);
  211 + }
  212 + }
  213 +
  214 + // readly terminated now.
  215 + really_terminated = true;
  216 +
  217 + handler->on_thread_stop();
  218 + srs_info("thread %s cycle finished", _name);
  219 + }
  220 +
  221 + void* SrsThread::thread_fun(void* arg)
  222 + {
  223 + SrsThread* obj = (SrsThread*)arg;
  224 + srs_assert(obj);
  225 +
  226 + obj->thread_cycle();
  227 +
  228 + st_thread_exit(NULL);
  229 +
  230 + return NULL;
  231 + }
  232 +}
  233 +
  234 +ISrsEndlessThreadHandler::ISrsEndlessThreadHandler()
30 { 235 {
31 } 236 }
32 237
33 -ISrsThreadHandler::~ISrsThreadHandler() 238 +ISrsEndlessThreadHandler::~ISrsEndlessThreadHandler()
34 { 239 {
35 } 240 }
36 241
37 -void ISrsThreadHandler::on_thread_start() 242 +SrsEndlessThread::SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h)
38 { 243 {
  244 + handler = h;
  245 + pthread = new internal::SrsThread(n, this, 0, false);
39 } 246 }
40 247
41 -int ISrsThreadHandler::on_before_cycle() 248 +SrsEndlessThread::~SrsEndlessThread()
42 { 249 {
43 - int ret = ERROR_SUCCESS;  
44 - return ret; 250 + pthread->stop();
  251 + srs_freep(pthread);
45 } 252 }
46 253
47 -int ISrsThreadHandler::on_end_cycle() 254 +int SrsEndlessThread::start()
48 { 255 {
49 - int ret = ERROR_SUCCESS;  
50 - return ret; 256 + return pthread->start();
51 } 257 }
52 258
53 -void ISrsThreadHandler::on_thread_stop() 259 +int SrsEndlessThread::cycle()
54 { 260 {
  261 + return handler->cycle();
55 } 262 }
56 263
57 -SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable) 264 +ISrsOneCycleThreadHandler::ISrsOneCycleThreadHandler()
58 { 265 {
59 - _name = name;  
60 - handler = thread_handler;  
61 - cycle_interval_us = interval_us;  
62 -  
63 - tid = NULL;  
64 - loop = false;  
65 - really_terminated = true;  
66 - _cid = -1;  
67 - _joinable = joinable;  
68 -  
69 - // in start(), the thread cycle method maybe stop and remove the thread itself,  
70 - // and the thread start() is waiting for the _cid, and segment fault then.  
71 - // @see https://github.com/simple-rtmp-server/srs/issues/110  
72 - // thread will set _cid, callback on_thread_start(), then wait for the can_run signal.  
73 - can_run = false;  
74 } 266 }
75 267
76 -SrsThread::~SrsThread() 268 +ISrsOneCycleThreadHandler::~ISrsOneCycleThreadHandler()
77 { 269 {
78 - stop();  
79 } 270 }
80 271
81 -int SrsThread::cid() 272 +void ISrsOneCycleThreadHandler::on_thread_stop()
82 { 273 {
83 - return _cid;  
84 } 274 }
85 275
86 -int SrsThread::start() 276 +SrsOneCycleThread::SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h)
87 { 277 {
88 - int ret = ERROR_SUCCESS;  
89 -  
90 - if(tid) {  
91 - srs_info("thread %s already running.", _name);  
92 - return ret;  
93 - }  
94 -  
95 - if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){  
96 - ret = ERROR_ST_CREATE_CYCLE_THREAD;  
97 - srs_error("st_thread_create failed. ret=%d", ret);  
98 - return ret;  
99 - }  
100 -  
101 - // we set to loop to true for thread to run.  
102 - loop = true;  
103 -  
104 - // wait for cid to ready, for parent thread to get the cid.  
105 - while (_cid < 0 && loop) {  
106 - st_usleep(10 * 1000);  
107 - }  
108 -  
109 - // now, cycle thread can run.  
110 - can_run = true;  
111 - 278 + handler = h;
  279 + pthread = new internal::SrsThread(n, this, 0, false);
  280 +}
  281 +
  282 +SrsOneCycleThread::~SrsOneCycleThread()
  283 +{
  284 + pthread->stop();
  285 + srs_freep(pthread);
  286 +}
  287 +
  288 +int SrsOneCycleThread::start()
  289 +{
  290 + return pthread->start();
  291 +}
  292 +
  293 +int SrsOneCycleThread::cycle()
  294 +{
  295 + int ret = handler->cycle();
  296 + pthread->stop_loop();
112 return ret; 297 return ret;
113 } 298 }
114 299
115 -void SrsThread::stop() 300 +void SrsOneCycleThread::on_thread_stop()
116 { 301 {
117 - if (tid) {  
118 - loop = false;  
119 -  
120 - // the interrupt will cause the socket to read/write error,  
121 - // which will terminate the cycle thread.  
122 - st_thread_interrupt(tid);  
123 -  
124 - // when joinable, wait util quit.  
125 - if (_joinable) {  
126 - // wait the thread to exit.  
127 - int ret = st_thread_join(tid, NULL);  
128 - if (ret) {  
129 - srs_warn("core: ignore join thread failed.");  
130 - } 302 + handler->on_thread_stop();
  303 +}
131 304
132 - // wait the thread actually terminated.  
133 - // sometimes the thread join return -1, for example,  
134 - // when thread use st_recvfrom, the thread join return -1.  
135 - // so here, we use a variable to ensure the thread stopped.  
136 - while (!really_terminated) {  
137 - st_usleep(10 * 1000); 305 +ISrsReusableThreadHandler::ISrsReusableThreadHandler()
  306 +{
  307 +}
138 308
139 - if (really_terminated) {  
140 - break;  
141 - }  
142 - srs_warn("core: wait thread to actually terminated");  
143 - }  
144 - }  
145 -  
146 - tid = NULL;  
147 - } 309 +ISrsReusableThreadHandler::~ISrsReusableThreadHandler()
  310 +{
148 } 311 }
149 312
150 -bool SrsThread::can_loop() 313 +void ISrsReusableThreadHandler::on_thread_stop()
151 { 314 {
152 - return loop;  
153 } 315 }
154 316
155 -void SrsThread::stop_loop() 317 +SrsReusableThread::SrsReusableThread(const char* n, ISrsReusableThreadHandler* h, int64_t interval_us)
156 { 318 {
157 - loop = false; 319 + handler = h;
  320 + pthread = new internal::SrsThread(n, this, interval_us, true);
158 } 321 }
159 322
160 -void SrsThread::thread_cycle() 323 +SrsReusableThread::~SrsReusableThread()
161 { 324 {
162 - int ret = ERROR_SUCCESS;  
163 -  
164 - _srs_context->generate_id();  
165 - srs_info("thread %s cycle start", _name);  
166 -  
167 - _cid = _srs_context->get_id();  
168 -  
169 - srs_assert(handler);  
170 - handler->on_thread_start(); 325 + pthread->stop();
  326 + srs_freep(pthread);
  327 +}
171 328
172 - // thread is running now.  
173 - really_terminated = false;  
174 -  
175 - // wait for cid to ready, for parent thread to get the cid.  
176 - while (!can_run && loop) {  
177 - st_usleep(10 * 1000);  
178 - }  
179 -  
180 - while (loop) {  
181 - if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {  
182 - srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret);  
183 - goto failed;  
184 - }  
185 - srs_info("thread %s on before cycle success");  
186 -  
187 - if ((ret = handler->cycle()) != ERROR_SUCCESS) {  
188 - if (!srs_is_client_gracefully_close(ret)) {  
189 - srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);  
190 - }  
191 - goto failed;  
192 - }  
193 - srs_info("thread %s cycle success", _name);  
194 -  
195 - if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {  
196 - srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret);  
197 - goto failed;  
198 - }  
199 - srs_info("thread %s on end cycle success", _name); 329 +int SrsReusableThread::start()
  330 +{
  331 + return pthread->start();
  332 +}
200 333
201 -failed:  
202 - if (!loop) {  
203 - break;  
204 - }  
205 -  
206 - // to improve performance, donot sleep when interval is zero.  
207 - // @see: https://github.com/simple-rtmp-server/srs/issues/237  
208 - if (cycle_interval_us != 0) {  
209 - st_usleep(cycle_interval_us);  
210 - }  
211 - }  
212 -  
213 - // readly terminated now.  
214 - really_terminated = true;  
215 -  
216 - handler->on_thread_stop();  
217 - srs_info("thread %s cycle finished", _name); 334 +void SrsReusableThread::stop()
  335 +{
  336 + pthread->stop();
218 } 337 }
219 338
220 -void* SrsThread::thread_fun(void* arg) 339 +int SrsReusableThread::cid()
221 { 340 {
222 - SrsThread* obj = (SrsThread*)arg;  
223 - srs_assert(obj);  
224 -  
225 - obj->thread_cycle(); 341 + return pthread->cid();
  342 +}
226 343
227 - st_thread_exit(NULL);  
228 -  
229 - return NULL; 344 +void SrsReusableThread::interrupt()
  345 +{
  346 + pthread->stop_loop();
230 } 347 }
231 348
  349 +bool SrsReusableThread::interrupted()
  350 +{
  351 + return !pthread->can_loop();
  352 +}
  353 +
  354 +int SrsReusableThread::cycle()
  355 +{
  356 + return handler->cycle();
  357 +}
  358 +
  359 +void SrsReusableThread::on_thread_stop()
  360 +{
  361 + handler->on_thread_stop();
  362 +}
@@ -31,189 +31,311 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. @@ -31,189 +31,311 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
31 31
32 #include <srs_app_st.hpp> 32 #include <srs_app_st.hpp>
33 33
  34 +// the internal classes, user should never use it.
  35 +// user should use the public classes at the bellow:
  36 +// @see SrsEndlessThread, SrsOneCycleThread, SrsReusableThread
  37 +namespace internal {
  38 + /**
  39 + * the handler for the thread, callback interface.
  40 + * the thread model defines as:
  41 + * handler->on_thread_start()
  42 + * while loop:
  43 + * handler->on_before_cycle()
  44 + * handler->cycle()
  45 + * handler->on_end_cycle()
  46 + * if !loop then break for user stop thread.
  47 + * sleep(CycleIntervalMilliseconds)
  48 + * handler->on_thread_stop()
  49 + * when stop, the thread will interrupt the st_thread,
  50 + * which will cause the socket to return error and
  51 + * terminate the cycle thread.
  52 + *
  53 + * @remark why should check can_loop() in cycle method?
  54 + * when thread interrupt, the socket maybe not got EINT,
  55 + * espectially on st_usleep(), so the cycle must check the loop,
  56 + * when handler->cycle() has loop itself, for example:
  57 + * while (true):
  58 + * if (read_from_socket(skt) < 0) break;
  59 + * if thread stop when read_from_socket, it's ok, the loop will break,
  60 + * but when thread stop interrupt the s_usleep(0), then the loop is
  61 + * death loop.
  62 + * in a word, the handler->cycle() must:
  63 + * while (pthread->can_loop()):
  64 + * if (read_from_socket(skt) < 0) break;
  65 + * check the loop, then it works.
  66 + *
  67 + * @remark why should use stop_loop() to terminate thread in itself?
  68 + * in the thread itself, that is the cycle method,
  69 + * if itself want to terminate the thread, should never use stop(),
  70 + * but use stop_loop() to set the loop to false and terminate normally.
  71 + *
  72 + * @remark when should set the interval_us, and when not?
  73 + * the cycle will invoke util cannot loop, eventhough the return code of cycle is error,
  74 + * so the interval_us used to sleep for each cycle.
  75 + */
  76 + class ISrsThreadHandler
  77 + {
  78 + public:
  79 + ISrsThreadHandler();
  80 + virtual ~ISrsThreadHandler();
  81 + public:
  82 + virtual void on_thread_start();
  83 + virtual int on_before_cycle();
  84 + virtual int cycle() = 0;
  85 + virtual int on_end_cycle();
  86 + virtual void on_thread_stop();
  87 + };
  88 +
  89 + /**
  90 + * provides servies from st_thread_t,
  91 + * for common thread usage.
  92 + */
  93 + class SrsThread
  94 + {
  95 + private:
  96 + st_thread_t tid;
  97 + int _cid;
  98 + bool loop;
  99 + bool can_run;
  100 + bool really_terminated;
  101 + bool _joinable;
  102 + const char* _name;
  103 + private:
  104 + ISrsThreadHandler* handler;
  105 + int64_t cycle_interval_us;
  106 + public:
  107 + /**
  108 + * initialize the thread.
  109 + * @param name, human readable name for st debug.
  110 + * @param thread_handler, the cycle handler for the thread.
  111 + * @param interval_us, the sleep interval when cycle finished.
  112 + * @param joinable, if joinable, other thread must stop the thread.
  113 + * @remark if joinable, thread never quit itself, or memory leak.
  114 + * @see: https://github.com/simple-rtmp-server/srs/issues/78
  115 + * @remark about st debug, see st-1.9/README, _st_iterate_threads_flag
  116 + */
  117 + /**
  118 + * TODO: FIXME: maybe all thread must be reap by others threads,
  119 + * @see: https://github.com/simple-rtmp-server/srs/issues/77
  120 + */
  121 + SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable);
  122 + virtual ~SrsThread();
  123 + public:
  124 + /**
  125 + * get the context id. @see: ISrsThreadContext.get_id().
  126 + * used for parent thread to get the id.
  127 + * @remark when start thread, parent thread will block and wait for this id ready.
  128 + */
  129 + virtual int cid();
  130 + /**
  131 + * start the thread, invoke the cycle of handler util
  132 + * user stop the thread.
  133 + * @remark ignore any error of cycle of handler.
  134 + * @remark user can start multiple times, ignore if already started.
  135 + * @remark wait for the cid is set by thread pfn.
  136 + */
  137 + virtual int start();
  138 + /**
  139 + * stop the thread, wait for the thread to terminate.
  140 + * @remark user can stop multiple times, ignore if already stopped.
  141 + */
  142 + virtual void stop();
  143 + public:
  144 + /**
  145 + * whether the thread should loop,
  146 + * used for handler->cycle() which has a loop method,
  147 + * to check this method, break if false.
  148 + */
  149 + virtual bool can_loop();
  150 + /**
  151 + * for the loop thread to stop the loop.
  152 + * other thread can directly use stop() to stop loop and wait for quit.
  153 + * this stop loop method only set loop to false.
  154 + */
  155 + virtual void stop_loop();
  156 + private:
  157 + virtual void thread_cycle();
  158 + static void* thread_fun(void* arg);
  159 + };
  160 +}
  161 +
34 /** 162 /**
35 - * the handler for the thread, callback interface.  
36 - * the thread model defines as:  
37 - * handler->on_thread_start()  
38 - * while loop:  
39 - * handler->on_before_cycle()  
40 - * handler->cycle()  
41 - * handler->on_end_cycle()  
42 - * if !loop then break for user stop thread.  
43 - * sleep(CycleIntervalMilliseconds)  
44 - * handler->on_thread_stop()  
45 - * when stop, the thread will interrupt the st_thread,  
46 - * which will cause the socket to return error and  
47 - * terminate the cycle thread.  
48 - *  
49 - * Usage 1: loop thread never quit. 163 + * the endless thread is a loop thread never quit.
50 * user can create thread always running util server terminate. 164 * user can create thread always running util server terminate.
51 * the step to create a thread never stop: 165 * the step to create a thread never stop:
52 - * 1. create SrsThread field, with joinable false. 166 + * 1. create SrsEndlessThread field.
53 * for example: 167 * for example:
54 - * class SrsStreamCache : public ISrsThreadHandler {  
55 - * public: SrsStreamCache() { pthread = new SrsThread("http-stream", this, SRS_AUTO_STREAM_SLEEP_US, false); } 168 + * class SrsStreamCache : public ISrsEndlessThreadHandler {
  169 + * public: SrsStreamCache() { pthread = new SrsEndlessThread("http-stream", this); }
56 * public: virtual int cycle() { 170 * public: virtual int cycle() {
57 - * // check status, start ffmpeg when stopped. 171 + * // do some work never end.
58 * } 172 * }
59 * } 173 * }
60 - *  
61 - * Usage 2: stop by other thread.  
62 - * user can create thread and stop then start again and again,  
63 - * generally must provides a start and stop method, @see SrsIngester.  
64 - * the step to create a thread stop by other thread:  
65 - * 1. create SrsThread field, with joinable true.  
66 - * 2. must use stop to stop and join the thread.  
67 - * for example:  
68 - * class SrsIngester : public ISrsThreadHandler {  
69 - * public: SrsIngester() { pthread = new SrsThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US, true); }  
70 - * public: virtual int start() { return pthread->start(); }  
71 - * public: virtual void stop() { pthread->stop(); }  
72 - * public: virtual int cycle() {  
73 - * // check status, start ffmpeg when stopped.  
74 - * }  
75 - * };  
76 - *  
77 - * Usage 3: stop by thread itself. 174 + * @remark user must use block method in cycle method, for example, sleep or socket io.
  175 + */
  176 +class ISrsEndlessThreadHandler
  177 +{
  178 +public:
  179 + ISrsEndlessThreadHandler();
  180 + virtual ~ISrsEndlessThreadHandler();
  181 +public:
  182 + /**
  183 + * the cycle method for the common thread.
  184 + * @remark user must use block method in cycle method, for example, sleep or socket io.
  185 + */
  186 + virtual int cycle() = 0;
  187 +};
  188 +class SrsEndlessThread : public internal::ISrsThreadHandler
  189 +{
  190 +private:
  191 + internal::SrsThread* pthread;
  192 + ISrsEndlessThreadHandler* handler;
  193 +public:
  194 + SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h);
  195 + virtual ~SrsEndlessThread();
  196 +public:
  197 + /**
  198 + * for the endless thread, never quit.
  199 + */
  200 + virtual int start();
  201 +// interface internal::ISrsThreadHandler
  202 +public:
  203 + virtual int cycle();
  204 +};
  205 +
  206 +/**
  207 + * the one cycle thread is a thread do the cycle only one time,
  208 + * that is, the thread will quit when return from the cycle.
78 * user can create thread which stop itself, 209 * user can create thread which stop itself,
79 * generally only need to provides a start method, 210 * generally only need to provides a start method,
80 * the object will destroy itself then terminate the thread, @see SrsConnection 211 * the object will destroy itself then terminate the thread, @see SrsConnection
81 - * 1. create SrsThread field, with joinable false.  
82 - * 2. owner stop thread loop, destroy itself when thread stop. 212 + * 1. create SrsThread field
  213 + * 2. the thread quit when return from cycle.
83 * for example: 214 * for example:
84 - * class SrsConnection : public ISrsThreadHandler {  
85 - * public: SrsConnection() { pthread = new SrsThread("conn", this, 0, false); } 215 + * class SrsConnection : public ISrsOneCycleThreadHandler {
  216 + * public: SrsConnection() { pthread = new SrsOneCycleThread("conn", this); }
86 * public: virtual int start() { return pthread->start(); } 217 * public: virtual int start() { return pthread->start(); }
87 * public: virtual int cycle() { 218 * public: virtual int cycle() {
88 * // serve client. 219 * // serve client.
89 * // set loop to stop to quit, stop thread itself. 220 * // set loop to stop to quit, stop thread itself.
90 * pthread->stop_loop(); 221 * pthread->stop_loop();
91 * } 222 * }
92 - * public: virtual int on_thread_stop() { 223 + * public: virtual void on_thread_stop() {
93 * // remove the connection in thread itself. 224 * // remove the connection in thread itself.
94 * server->remove(this); 225 * server->remove(this);
95 * } 226 * }
96 * }; 227 * };
97 - *  
98 - * Usage 4: loop in the cycle method.  
99 - * user can use loop code in the cycle method, @see SrsForwarder  
100 - * 1. create SrsThread field, with or without joinable is ok.  
101 - * 2. loop code in cycle method, check the can_loop() for thread to quit. 228 + */
  229 +class ISrsOneCycleThreadHandler
  230 +{
  231 +public:
  232 + ISrsOneCycleThreadHandler();
  233 + virtual ~ISrsOneCycleThreadHandler();
  234 +public:
  235 + /**
  236 + * the cycle method for the one cycle thread.
  237 + */
  238 + virtual int cycle() = 0;
  239 + /**
  240 + * when thread stop, the handler can do cleanup.
  241 + * @remark this method is optional, handler can ignore it.
  242 + */
  243 + virtual void on_thread_stop();
  244 +};
  245 +class SrsOneCycleThread : public internal::ISrsThreadHandler
  246 +{
  247 +private:
  248 + internal::SrsThread* pthread;
  249 + ISrsOneCycleThreadHandler* handler;
  250 +public:
  251 + SrsOneCycleThread(const char* n, ISrsOneCycleThreadHandler* h);
  252 + virtual ~SrsOneCycleThread();
  253 +public:
  254 + /**
  255 + * for the one cycle thread, quit when cycle return.
  256 + */
  257 + virtual int start();
  258 +// interface internal::ISrsThreadHandler
  259 +public:
  260 + virtual int cycle();
  261 + virtual void on_thread_stop();
  262 +};
  263 +
  264 +/**
  265 + * the reuse thread is a thread stop and start by other thread.
  266 + * user can create thread and stop then start again and again,
  267 + * generally must provides a start and stop method, @see SrsIngester.
  268 + * the step to create a thread stop by other thread:
  269 + * 1. create SrsThread field, with joinable true.
  270 + * 2. must use stop to stop and join the thread.
102 * for example: 271 * for example:
103 - * class SrsForwarder : public ISrsThreadHandler { 272 + * class SrsIngester : public ISrsThreadHandler {
  273 + * public: SrsIngester() { pthread = new SrsThread("ingest", this, SRS_AUTO_INGESTER_SLEEP_US, true); }
  274 + * public: virtual int start() { return pthread->start(); }
  275 + * public: virtual void stop() { pthread->stop(); }
104 * public: virtual int cycle() { 276 * public: virtual int cycle() {
105 - * while (pthread->can_loop()) {  
106 - * // read msgs from queue and forward to server.  
107 - * } 277 + * // check status, start ffmpeg when stopped.
108 * } 278 * }
109 * }; 279 * };
110 - *  
111 - * @remark why should check can_loop() in cycle method?  
112 - * when thread interrupt, the socket maybe not got EINT,  
113 - * espectially on st_usleep(), so the cycle must check the loop,  
114 - * when handler->cycle() has loop itself, for example:  
115 - * while (true):  
116 - * if (read_from_socket(skt) < 0) break;  
117 - * if thread stop when read_from_socket, it's ok, the loop will break,  
118 - * but when thread stop interrupt the s_usleep(0), then the loop is  
119 - * death loop.  
120 - * in a word, the handler->cycle() must:  
121 - * while (pthread->can_loop()):  
122 - * if (read_from_socket(skt) < 0) break;  
123 - * check the loop, then it works.  
124 - *  
125 - * @remark why should use stop_loop() to terminate thread in itself?  
126 - * in the thread itself, that is the cycle method,  
127 - * if itself want to terminate the thread, should never use stop(),  
128 - * but use stop_loop() to set the loop to false and terminate normally.  
129 - *  
130 - * @remark when should set the interval_us, and when not?  
131 - * the cycle will invoke util cannot loop, eventhough the return code of cycle is error,  
132 - * so the interval_us used to sleep for each cycle.  
133 */ 280 */
134 -class ISrsThreadHandler 281 +class ISrsReusableThreadHandler
135 { 282 {
136 public: 283 public:
137 - ISrsThreadHandler();  
138 - virtual ~ISrsThreadHandler(); 284 + ISrsReusableThreadHandler();
  285 + virtual ~ISrsReusableThreadHandler();
139 public: 286 public:
140 - virtual void on_thread_start();  
141 - virtual int on_before_cycle(); 287 + /**
  288 + * the cycle method for the one cycle thread.
  289 + * @remark when the cycle has its inner loop, it must check whether
  290 + * the thread is interrupted.
  291 + */
142 virtual int cycle() = 0; 292 virtual int cycle() = 0;
143 - virtual int on_end_cycle(); 293 + /**
  294 + * when thread stop, the handler can do cleanup.
  295 + * @remark this method is optional, handler can ignore it.
  296 + */
144 virtual void on_thread_stop(); 297 virtual void on_thread_stop();
145 }; 298 };
146 -  
147 -/**  
148 -* provides servies from st_thread_t,  
149 -* for common thread usage.  
150 -*/  
151 -class SrsThread 299 +class SrsReusableThread : public internal::ISrsThreadHandler
152 { 300 {
153 private: 301 private:
154 - st_thread_t tid;  
155 - int _cid;  
156 - bool loop;  
157 - bool can_run;  
158 - bool really_terminated;  
159 - bool _joinable;  
160 - const char* _name;  
161 -private:  
162 - ISrsThreadHandler* handler;  
163 - int64_t cycle_interval_us; 302 + internal::SrsThread* pthread;
  303 + ISrsReusableThreadHandler* handler;
164 public: 304 public:
165 - /**  
166 - * initialize the thread.  
167 - * @param name, human readable name for st debug.  
168 - * @param thread_handler, the cycle handler for the thread.  
169 - * @param interval_us, the sleep interval when cycle finished.  
170 - * @param joinable, if joinable, other thread must stop the thread.  
171 - * @remark if joinable, thread never quit itself, or memory leak.  
172 - * @see: https://github.com/simple-rtmp-server/srs/issues/78  
173 - * @remark about st debug, see st-1.9/README, _st_iterate_threads_flag  
174 - */  
175 - /**  
176 - * TODO: FIXME: maybe all thread must be reap by others threads,  
177 - * @see: https://github.com/simple-rtmp-server/srs/issues/77  
178 - */  
179 - SrsThread(const char* name, ISrsThreadHandler* thread_handler, int64_t interval_us, bool joinable);  
180 - virtual ~SrsThread(); 305 + SrsReusableThread(const char* n, ISrsReusableThreadHandler* h, int64_t interval_us = 0);
  306 + virtual ~SrsReusableThread();
181 public: 307 public:
182 /** 308 /**
183 - * get the context id. @see: ISrsThreadContext.get_id().  
184 - * used for parent thread to get the id.  
185 - * @remark when start thread, parent thread will block and wait for this id ready.  
186 - */  
187 - virtual int cid();  
188 - /**  
189 - * start the thread, invoke the cycle of handler util  
190 - * user stop the thread.  
191 - * @remark ignore any error of cycle of handler.  
192 - * @remark user can start multiple times, ignore if already started.  
193 - * @remark wait for the cid is set by thread pfn.  
194 - */ 309 + * for the reusable thread, start and stop by user.
  310 + */
195 virtual int start(); 311 virtual int start();
196 /** 312 /**
197 - * stop the thread, wait for the thread to terminate.  
198 - * @remark user can stop multiple times, ignore if already stopped.  
199 - */ 313 + * stop the thread, wait for the thread to terminate.
  314 + * @remark user can stop multiple times, ignore if already stopped.
  315 + */
200 virtual void stop(); 316 virtual void stop();
201 public: 317 public:
202 /** 318 /**
203 - * whether the thread should loop,  
204 - * used for handler->cycle() which has a loop method,  
205 - * to check this method, break if false.  
206 - */  
207 - virtual bool can_loop(); 319 + * get the context id. @see: ISrsThreadContext.get_id().
  320 + * used for parent thread to get the id.
  321 + * @remark when start thread, parent thread will block and wait for this id ready.
  322 + */
  323 + virtual int cid();
208 /** 324 /**
209 - * for the loop thread to stop the loop.  
210 - * other thread can directly use stop() to stop loop and wait for quit.  
211 - * this stop loop method only set loop to false.  
212 - */  
213 - virtual void stop_loop();  
214 -private:  
215 - virtual void thread_cycle();  
216 - static void* thread_fun(void* arg); 325 + * interrupt the thread to stop loop.
  326 + * we only set the loop flag to false, not really interrupt the thread.
  327 + */
  328 + virtual void interrupt();
  329 + /**
  330 + * whether the thread is interrupted,
  331 + * for the cycle has its loop, the inner loop should quit when thread
  332 + * is interrupted.
  333 + */
  334 + virtual bool interrupted();
  335 +// interface internal::ISrsThreadHandler
  336 +public:
  337 + virtual int cycle();
  338 + virtual void on_thread_stop();
217 }; 339 };
218 340
219 #endif 341 #endif