From d0009636df6544dd26ab7c568a0244af6a20634a Mon Sep 17 00:00:00 2001 From: Fabio Alessandrelli Date: Tue, 25 Feb 2020 22:48:15 +0100 Subject: ScriptDebuggerRemote use threads --- core/script_debugger_peer.cpp | 209 ++++++++++++++++++++++++++++++++++++++++ core/script_debugger_peer.h | 48 +++++++++ core/script_debugger_remote.cpp | 86 ++++------------- core/script_debugger_remote.h | 11 +-- 4 files changed, 280 insertions(+), 74 deletions(-) create mode 100644 core/script_debugger_peer.cpp create mode 100644 core/script_debugger_peer.h (limited to 'core') diff --git a/core/script_debugger_peer.cpp b/core/script_debugger_peer.cpp new file mode 100644 index 0000000000..a4e1de6b66 --- /dev/null +++ b/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 tcp_client = Ref(memnew(StreamPeerTCP)); + Ref packet_peer = Ref(memnew(PacketPeerStream)); + Mutex mutex; + Thread *thread = NULL; + List in_queue; + List 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()); + } + + 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::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 peer = Ref(memnew(ScriptDebuggerPeerTCP)); + Error err = peer->connect_to_host(debug_host, debug_port); + if (err != OK) + return Ref(); + return peer; +} diff --git a/core/script_debugger_peer.h b/core/script_debugger_peer.h new file mode 100644 index 0000000000..194ef0b900 --- /dev/null +++ b/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 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 diff --git a/core/script_debugger_remote.cpp b/core/script_debugger_remote.cpp index 67375da6e2..3a3b83367e 100644 --- a/core/script_debugger_remote.cpp +++ b/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 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 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(memnew(StreamPeerTCP))), - packet_peer_stream(Ref(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); diff --git a/core/script_debugger_remote.h b/core/script_debugger_remote.h index b7a309b2f9..2a30f6d7c0 100644 --- a/core/script_debugger_remote.h +++ b/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 tcp_client; - Ref packet_peer_stream; + Ref 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 p_peer); ~ScriptDebuggerRemote(); }; -- cgit v1.2.3