Просмотр исходного кода

ScriptDebuggerRemote use threads

Fabio Alessandrelli 5 лет назад
Родитель
Сommit
d0009636df

+ 209 - 0
core/script_debugger_peer.cpp

@@ -0,0 +1,209 @@
+/*************************************************************************/
+/*  script_debugger_peer.cpp                                             */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#include "script_debugger_peer.h"
+
+#include "core/io/packet_peer.h"
+#include "core/io/stream_peer_tcp.h"
+#include "core/os/mutex.h"
+#include "core/os/os.h"
+#include "core/os/thread.h"
+
+class ScriptDebuggerPeerTCP : public ScriptDebuggerPeer {
+private:
+	enum {
+		QUEUE_MAX = 2048,
+		POLL_USEC_MAX = 100,
+	};
+
+	Ref<StreamPeerTCP> tcp_client = Ref<StreamPeerTCP>(memnew(StreamPeerTCP));
+	Ref<PacketPeerStream> packet_peer = Ref<PacketPeerStream>(memnew(PacketPeerStream));
+	Mutex mutex;
+	Thread *thread = NULL;
+	List<Array> in_queue;
+	List<Array> out_queue;
+	bool connected = false;
+	bool running = false;
+
+	static void _thread_func(void *p_ud);
+
+	void _poll();
+
+public:
+	void poll();
+	Error connect_to_host(const String &p_host, uint16_t p_port);
+
+	bool is_peer_connected() {
+		return connected;
+	}
+
+	bool has_message() {
+		return in_queue.size() > 0;
+	}
+
+	Array get_message() {
+		MutexLock lock(mutex);
+		ERR_FAIL_COND_V(!has_message(), Array());
+		Array out = in_queue[0];
+		in_queue.pop_front();
+		return out;
+	}
+
+	Error put_message(const Array &p_arr) {
+		MutexLock lock(mutex);
+		if (out_queue.size() >= 2048) // XXX Should we keep track of size instead?
+			return ERR_OUT_OF_MEMORY;
+
+		out_queue.push_back(p_arr);
+		return OK;
+	}
+
+	void close() {
+		if (thread) {
+			running = false;
+			Thread::wait_to_finish(thread);
+			memdelete(thread);
+			thread = NULL;
+		}
+		MutexLock lock(mutex);
+		tcp_client->disconnect_from_host();
+		packet_peer->set_stream_peer(Ref<StreamPeer>());
+	}
+
+	ScriptDebuggerPeerTCP() {
+		packet_peer->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
+	}
+
+	~ScriptDebuggerPeerTCP() {
+		close();
+	}
+};
+
+Error ScriptDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_port) {
+
+	IP_Address ip;
+	if (p_host.is_valid_ip_address())
+		ip = p_host;
+	else
+		ip = IP::get_singleton()->resolve_hostname(p_host);
+
+	int port = p_port;
+
+	const int tries = 6;
+	int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
+
+	tcp_client->connect_to_host(ip, port);
+
+	for (int i = 0; i < tries; i++) {
+
+		if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
+			print_verbose("Remote Debugger: Connected!");
+			break;
+		} else {
+
+			const int ms = waits[i];
+			OS::get_singleton()->delay_usec(ms * 1000);
+			print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
+		};
+	};
+
+	if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
+
+		ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
+		return FAILED;
+	};
+	packet_peer->set_stream_peer(tcp_client);
+	connected = true;
+#ifndef NO_THREADS
+	running = true;
+	thread = Thread::create(_thread_func, this);
+#endif
+	return OK;
+}
+
+void ScriptDebuggerPeerTCP::_thread_func(void *p_ud) {
+	ScriptDebuggerPeerTCP *peer = (ScriptDebuggerPeerTCP *)p_ud;
+	while (peer->running && peer->is_peer_connected()) {
+		peer->_poll();
+		if (!peer->is_peer_connected())
+			break;
+		OS::get_singleton()->delay_usec(100);
+	}
+}
+
+void ScriptDebuggerPeerTCP::poll() {
+#ifdef NO_THREADS
+	_poll();
+#endif
+}
+
+void ScriptDebuggerPeerTCP::_poll() {
+	MutexLock lock(mutex);
+	// Poll in
+	uint64_t ticks = OS::get_singleton()->get_ticks_usec();
+	while (connected && packet_peer->get_available_packet_count() > 0 && in_queue.size() < QUEUE_MAX && OS::get_singleton()->get_ticks_usec() - ticks < POLL_USEC_MAX) {
+		Variant var;
+		const Error err = packet_peer->get_var(var);
+		connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+		if (err != OK) {
+			ERR_PRINT("Error reading variant from peer");
+			break;
+		}
+		ERR_CONTINUE_MSG(var.get_type() != Variant::ARRAY, "Malformed packet received, not an Array.");
+		in_queue.push_back(var);
+	}
+	// Poll out
+	ticks = OS::get_singleton()->get_ticks_usec();
+	while (connected && out_queue.size() > 0 && OS::get_singleton()->get_ticks_usec() - ticks < POLL_USEC_MAX) {
+		Array arr = out_queue[0];
+		out_queue.pop_front();
+		const Error err = packet_peer->put_var(arr);
+		connected = tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+		if (err != OK) {
+			ERR_PRINT("Error writing variant to peer");
+			break;
+		}
+	}
+}
+
+Ref<ScriptDebuggerPeer> ScriptDebuggerPeer::create_from_uri(const String p_uri) {
+	String debug_host = p_uri;
+	uint16_t debug_port = 6007;
+	if (debug_host.find(":") != -1) {
+		int sep_pos = debug_host.find_last(":");
+		debug_port = debug_host.substr(sep_pos + 1, debug_host.length()).to_int();
+		debug_host = debug_host.substr(0, sep_pos);
+	}
+	Ref<ScriptDebuggerPeerTCP> peer = Ref<ScriptDebuggerPeer>(memnew(ScriptDebuggerPeerTCP));
+	Error err = peer->connect_to_host(debug_host, debug_port);
+	if (err != OK)
+		return Ref<ScriptDebuggerPeer>();
+	return peer;
+}

+ 48 - 0
core/script_debugger_peer.h

@@ -0,0 +1,48 @@
+/*************************************************************************/
+/*  script_debugger_peer.h                                               */
+/*************************************************************************/
+/*                       This file is part of:                           */
+/*                           GODOT ENGINE                                */
+/*                      https://godotengine.org                          */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur.                 */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md).   */
+/*                                                                       */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the       */
+/* "Software"), to deal in the Software without restriction, including   */
+/* without limitation the rights to use, copy, modify, merge, publish,   */
+/* distribute, sublicense, and/or sell copies of the Software, and to    */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions:                                             */
+/*                                                                       */
+/* The above copyright notice and this permission notice shall be        */
+/* included in all copies or substantial portions of the Software.       */
+/*                                                                       */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,       */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF    */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY  */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,  */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE     */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                */
+/*************************************************************************/
+
+#ifndef SCRIPT_DEBUGGER_PEER_H
+#define SCRIPT_DEBUGGER_PEER_H
+
+#include "core/reference.h"
+#include "core/ustring.h"
+
+class ScriptDebuggerPeer : public Reference {
+public:
+	static Ref<ScriptDebuggerPeer> create_from_uri(const String p_uri);
+	virtual bool is_peer_connected() = 0;
+	virtual bool has_message() = 0;
+	virtual Error put_message(const Array &p_arr) = 0;
+	virtual Array get_message() = 0;
+	virtual void close() = 0;
+	virtual void poll() = 0;
+};
+
+#endif

+ 18 - 68
core/script_debugger_remote.cpp

@@ -303,11 +303,11 @@ void ScriptDebuggerRemote::_put_msg(String p_message, Array p_data) {
 	Array msg;
 	msg.push_back(p_message);
 	msg.push_back(p_data);
-	packet_peer_stream->put_var(msg);
+	peer->put_message(msg);
 }
 
 bool ScriptDebuggerRemote::is_peer_connected() {
-	return tcp_client->is_connected_to_host() && tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED;
+	return peer->is_peer_connected();
 }
 
 void ScriptDebuggerRemote::_send_video_memory() {
@@ -319,48 +319,6 @@ void ScriptDebuggerRemote::_send_video_memory() {
 	_put_msg("message:video_mem", usage.serialize());
 }
 
-Error ScriptDebuggerRemote::connect_to_host(const String &p_host, uint16_t p_port) {
-
-	IP_Address ip;
-	if (p_host.is_valid_ip_address())
-		ip = p_host;
-	else
-		ip = IP::get_singleton()->resolve_hostname(p_host);
-
-	int port = p_port;
-
-	const int tries = 6;
-	int waits[tries] = { 1, 10, 100, 1000, 1000, 1000 };
-
-	tcp_client->connect_to_host(ip, port);
-
-	for (int i = 0; i < tries; i++) {
-
-		if (tcp_client->get_status() == StreamPeerTCP::STATUS_CONNECTED) {
-			print_verbose("Remote Debugger: Connected!");
-			break;
-		} else {
-
-			const int ms = waits[i];
-			OS::get_singleton()->delay_usec(ms * 1000);
-			print_verbose("Remote Debugger: Connection failed with status: '" + String::num(tcp_client->get_status()) + "', retrying in " + String::num(ms) + " msec.");
-		};
-	};
-
-	if (tcp_client->get_status() != StreamPeerTCP::STATUS_CONNECTED) {
-
-		ERR_PRINT("Remote Debugger: Unable to connect. Status: " + String::num(tcp_client->get_status()) + ".");
-		return FAILED;
-	};
-
-	packet_peer_stream->set_stream_peer(tcp_client);
-	Array msg;
-	msg.push_back(OS::get_singleton()->get_process_id());
-	send_message("set_pid", msg);
-
-	return OK;
-}
-
 void ScriptDebuggerRemote::_parse_message(const String p_command, const Array &p_data, ScriptLanguage *p_script) {
 
 	if (p_command == "request_video_mem") {
@@ -539,18 +497,13 @@ void ScriptDebuggerRemote::debug(ScriptLanguage *p_script, bool p_can_continue,
 	uint64_t loop_time_sec = 0;
 	while (true) {
 		loop_begin_usec = OS::get_singleton()->get_ticks_usec();
+		peer->poll();
 
 		_get_output();
 
-		if (packet_peer_stream->get_available_packet_count() > 0) {
-
-			Variant var;
-			Error err = packet_peer_stream->get_var(var);
+		if (peer->has_message()) {
 
-			ERR_CONTINUE(err != OK);
-			ERR_CONTINUE(var.get_type() != Variant::ARRAY);
-
-			Array cmd = var;
+			Array cmd = peer->get_message();
 
 			ERR_CONTINUE(cmd.size() != 2);
 			ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
@@ -700,19 +653,13 @@ void ScriptDebuggerRemote::_poll_events() {
 	//this si called from ::idle_poll, happens only when running the game,
 	//does not get called while on debug break
 
-	while (packet_peer_stream->get_available_packet_count() > 0) {
-
-		_get_output();
+	while (peer->has_message()) {
 
+		peer->poll();
 		//send over output_strings
+		_get_output();
 
-		Variant var;
-		Error err = packet_peer_stream->get_var(var);
-
-		ERR_CONTINUE(err != OK);
-		ERR_CONTINUE(var.get_type() != Variant::ARRAY);
-
-		Array cmd = var;
+		Array cmd = peer->get_message();
 
 		ERR_CONTINUE(cmd.size() < 2);
 		ERR_CONTINUE(cmd[0].get_type() != Variant::STRING);
@@ -1089,18 +1036,23 @@ void ScriptDebuggerRemote::set_skip_breakpoints(bool p_skip_breakpoints) {
 	skip_breakpoints = p_skip_breakpoints;
 }
 
+ScriptDebuggerRemote *ScriptDebuggerRemote::create_for_uri(const String &p_uri) {
+	Ref<ScriptDebuggerPeer> peer = ScriptDebuggerPeer::create_from_uri(p_uri);
+	if (peer.is_valid())
+		return memnew(ScriptDebuggerRemote(peer));
+	return NULL;
+}
+
 ScriptDebuggerRemote::ResourceUsageFunc ScriptDebuggerRemote::resource_usage_func = NULL;
 ScriptDebuggerRemote::ParseMessageFunc ScriptDebuggerRemote::scene_tree_parse_func = NULL;
 
-ScriptDebuggerRemote::ScriptDebuggerRemote() :
+ScriptDebuggerRemote::ScriptDebuggerRemote(Ref<ScriptDebuggerPeer> p_peer) :
 		profiling(false),
 		visual_profiling(false),
 		network_profiling(false),
 		max_frame_functions(16),
 		skip_profile_frame(false),
 		reload_all_scripts(false),
-		tcp_client(Ref<StreamPeerTCP>(memnew(StreamPeerTCP))),
-		packet_peer_stream(Ref<PacketPeerStream>(memnew(PacketPeerStream))),
 		last_perf_time(0),
 		last_net_prof_time(0),
 		last_net_bandwidth_time(0),
@@ -1120,9 +1072,7 @@ ScriptDebuggerRemote::ScriptDebuggerRemote() :
 		locking(false),
 		poll_every(0) {
 
-	packet_peer_stream->set_stream_peer(tcp_client);
-	packet_peer_stream->set_output_buffer_max_size((1024 * 1024 * 8) - 4); // 8 MiB should be way more than enough, minus 4 bytes for separator.
-
+	peer = p_peer;
 	phl.printfunc = _print_handler;
 	phl.userdata = this;
 	add_print_handler(&phl);

+ 5 - 6
core/script_debugger_remote.h

@@ -31,10 +31,9 @@
 #ifndef SCRIPT_DEBUGGER_REMOTE_H
 #define SCRIPT_DEBUGGER_REMOTE_H
 
-#include "core/io/packet_peer.h"
-#include "core/io/stream_peer_tcp.h"
 #include "core/list.h"
 #include "core/os/os.h"
+#include "core/script_debugger_peer.h"
 #include "core/script_language.h"
 
 class ScriptDebuggerRemote : public ScriptDebugger {
@@ -222,8 +221,7 @@ protected:
 	bool skip_profile_frame;
 	bool reload_all_scripts;
 
-	Ref<StreamPeerTCP> tcp_client;
-	Ref<PacketPeerStream> packet_peer_stream;
+	Ref<ScriptDebuggerPeer> peer;
 
 	uint64_t last_perf_time;
 	uint64_t last_net_prof_time;
@@ -286,7 +284,8 @@ public:
 	static ResourceUsageFunc resource_usage_func;
 	static ParseMessageFunc scene_tree_parse_func; // Could be made into list, extensible...
 
-	Error connect_to_host(const String &p_host, uint16_t p_port);
+	static ScriptDebuggerRemote *create_for_uri(const String &p_uri);
+
 	bool is_peer_connected();
 	virtual void debug(ScriptLanguage *p_script, bool p_can_continue = true, bool p_is_error_breakpoint = false);
 	virtual void idle_poll();
@@ -309,7 +308,7 @@ public:
 
 	virtual void set_skip_breakpoints(bool p_skip_breakpoints);
 
-	ScriptDebuggerRemote();
+	ScriptDebuggerRemote(Ref<ScriptDebuggerPeer> p_peer);
 	~ScriptDebuggerRemote();
 };
 

+ 3 - 14
main/main.cpp

@@ -897,20 +897,9 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 
 	if (debug_mode == "remote") {
 
-		ScriptDebuggerRemote *sdr = memnew(ScriptDebuggerRemote);
-		uint16_t debug_port = 6007;
-		if (debug_host.find(":") != -1) {
-			int sep_pos = debug_host.find_last(":");
-			debug_port = debug_host.substr(sep_pos + 1, debug_host.length()).to_int();
-			debug_host = debug_host.substr(0, sep_pos);
-		}
-		Error derr = sdr->connect_to_host(debug_host, debug_port);
-
-		sdr->set_skip_breakpoints(skip_breakpoints);
-
-		if (derr != OK) {
-			memdelete(sdr);
-		} else {
+		ScriptDebuggerRemote *sdr = ScriptDebuggerRemote::create_for_uri(debug_host);
+		if (sdr) {
+			sdr->set_skip_breakpoints(skip_breakpoints);
 			script_debugger = sdr;
 		}
 	} else if (debug_mode == "local") {