Modernize Thread
- Based on C++11's `thread` and `thread_local` - No more need to allocate-deallocate or check for null - No pointer anymore, just a member variable - Platform-specific implementations no longer needed (except for the few cases of non-portable functions) - Simpler for `NO_THREADS` - Thread ids are now the same across platforms (main is 1; others follow)
This commit is contained in:
parent
6ddfc8e718
commit
99fe462452
|
@ -1990,24 +1990,13 @@ Error _Thread::start(Object *p_instance, const StringName &p_method, const Varia
|
||||||
|
|
||||||
Thread::Settings s;
|
Thread::Settings s;
|
||||||
s.priority = (Thread::Priority)p_priority;
|
s.priority = (Thread::Priority)p_priority;
|
||||||
thread = Thread::create(_start_func, ud, s);
|
thread.start(_start_func, ud, s);
|
||||||
if (!thread) {
|
|
||||||
active = false;
|
|
||||||
target_method = StringName();
|
|
||||||
target_instance = nullptr;
|
|
||||||
userdata = Variant();
|
|
||||||
return ERR_CANT_CREATE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
String _Thread::get_id() const {
|
String _Thread::get_id() const {
|
||||||
if (!thread) {
|
return itos(thread.get_id());
|
||||||
return String();
|
|
||||||
}
|
|
||||||
|
|
||||||
return itos(thread->get_id());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool _Thread::is_active() const {
|
bool _Thread::is_active() const {
|
||||||
|
@ -2015,18 +2004,13 @@ bool _Thread::is_active() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
Variant _Thread::wait_to_finish() {
|
Variant _Thread::wait_to_finish() {
|
||||||
ERR_FAIL_COND_V_MSG(!thread, Variant(), "Thread must exist to wait for its completion.");
|
|
||||||
ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion.");
|
ERR_FAIL_COND_V_MSG(!active, Variant(), "Thread must be active to wait for its completion.");
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
Variant r = ret;
|
Variant r = ret;
|
||||||
active = false;
|
active = false;
|
||||||
target_method = StringName();
|
target_method = StringName();
|
||||||
target_instance = nullptr;
|
target_instance = nullptr;
|
||||||
userdata = Variant();
|
userdata = Variant();
|
||||||
if (thread) {
|
|
||||||
memdelete(thread);
|
|
||||||
}
|
|
||||||
thread = nullptr;
|
|
||||||
|
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
@ -2042,10 +2026,6 @@ void _Thread::_bind_methods() {
|
||||||
BIND_ENUM_CONSTANT(PRIORITY_HIGH);
|
BIND_ENUM_CONSTANT(PRIORITY_HIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
_Thread::~_Thread() {
|
|
||||||
ERR_FAIL_COND_MSG(active, "Reference to a Thread object was lost while the thread is still running...");
|
|
||||||
}
|
|
||||||
|
|
||||||
////// _ClassDB //////
|
////// _ClassDB //////
|
||||||
|
|
||||||
PackedStringArray _ClassDB::get_class_list() const {
|
PackedStringArray _ClassDB::get_class_list() const {
|
||||||
|
|
|
@ -553,7 +553,7 @@ protected:
|
||||||
volatile bool active = false;
|
volatile bool active = false;
|
||||||
Object *target_instance = nullptr;
|
Object *target_instance = nullptr;
|
||||||
StringName target_method;
|
StringName target_method;
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
static void _bind_methods();
|
static void _bind_methods();
|
||||||
static void _start_func(void *ud);
|
static void _start_func(void *ud);
|
||||||
|
|
||||||
|
@ -569,9 +569,6 @@ public:
|
||||||
String get_id() const;
|
String get_id() const;
|
||||||
bool is_active() const;
|
bool is_active() const;
|
||||||
Variant wait_to_finish();
|
Variant wait_to_finish();
|
||||||
|
|
||||||
_Thread() {}
|
|
||||||
~_Thread();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
VARIANT_ENUM_CAST(_Thread::Priority);
|
VARIANT_ENUM_CAST(_Thread::Priority);
|
||||||
|
|
|
@ -65,12 +65,8 @@ int RemoteDebuggerPeerTCP::get_max_message_size() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void RemoteDebuggerPeerTCP::close() {
|
void RemoteDebuggerPeerTCP::close() {
|
||||||
if (thread) {
|
running = false;
|
||||||
running = false;
|
thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(thread);
|
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
|
||||||
tcp_client->disconnect_from_host();
|
tcp_client->disconnect_from_host();
|
||||||
out_buf.resize(0);
|
out_buf.resize(0);
|
||||||
in_buf.resize(0);
|
in_buf.resize(0);
|
||||||
|
@ -85,7 +81,7 @@ RemoteDebuggerPeerTCP::RemoteDebuggerPeerTCP(Ref<StreamPeerTCP> p_tcp) {
|
||||||
connected = true;
|
connected = true;
|
||||||
#ifndef NO_THREADS
|
#ifndef NO_THREADS
|
||||||
running = true;
|
running = true;
|
||||||
thread = Thread::create(_thread_func, this);
|
thread.start(_thread_func, this);
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
tcp_client.instance();
|
tcp_client.instance();
|
||||||
|
@ -188,7 +184,7 @@ Error RemoteDebuggerPeerTCP::connect_to_host(const String &p_host, uint16_t p_po
|
||||||
connected = true;
|
connected = true;
|
||||||
#ifndef NO_THREADS
|
#ifndef NO_THREADS
|
||||||
running = true;
|
running = true;
|
||||||
thread = Thread::create(_thread_func, this);
|
thread.start(_thread_func, this);
|
||||||
#endif
|
#endif
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,7 @@ class RemoteDebuggerPeerTCP : public RemoteDebuggerPeer {
|
||||||
private:
|
private:
|
||||||
Ref<StreamPeerTCP> tcp_client;
|
Ref<StreamPeerTCP> tcp_client;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
List<Array> in_queue;
|
List<Array> in_queue;
|
||||||
List<Array> out_queue;
|
List<Array> out_queue;
|
||||||
int out_left = 0;
|
int out_left = 0;
|
||||||
|
|
|
@ -201,7 +201,7 @@ Error FileAccessNetworkClient::connect(const String &p_host, int p_port, const S
|
||||||
return ERR_INVALID_PARAMETER;
|
return ERR_INVALID_PARAMETER;
|
||||||
}
|
}
|
||||||
|
|
||||||
thread = Thread::create(_thread_func, this);
|
thread.start(_thread_func, this);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -214,12 +214,9 @@ FileAccessNetworkClient::FileAccessNetworkClient() {
|
||||||
}
|
}
|
||||||
|
|
||||||
FileAccessNetworkClient::~FileAccessNetworkClient() {
|
FileAccessNetworkClient::~FileAccessNetworkClient() {
|
||||||
if (thread) {
|
quit = true;
|
||||||
quit = true;
|
sem.post();
|
||||||
sem.post();
|
thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(thread);
|
|
||||||
memdelete(thread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) {
|
void FileAccessNetwork::_set_block(int p_offset, const Vector<uint8_t> &p_block) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ class FileAccessNetworkClient {
|
||||||
List<BlockRequest> block_requests;
|
List<BlockRequest> block_requests;
|
||||||
|
|
||||||
Semaphore sem;
|
Semaphore sem;
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Mutex blockrequest_mutex;
|
Mutex blockrequest_mutex;
|
||||||
|
|
|
@ -71,7 +71,7 @@ struct _IP_ResolverPrivate {
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Semaphore sem;
|
Semaphore sem;
|
||||||
|
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
//Semaphore* semaphore;
|
//Semaphore* semaphore;
|
||||||
bool thread_abort;
|
bool thread_abort;
|
||||||
|
|
||||||
|
@ -141,7 +141,7 @@ IP::ResolverID IP::resolve_hostname_queue_item(const String &p_hostname, IP::Typ
|
||||||
} else {
|
} else {
|
||||||
resolver->queue[id].response = IP_Address();
|
resolver->queue[id].response = IP_Address();
|
||||||
resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING;
|
resolver->queue[id].status = IP::RESOLVER_STATUS_WAITING;
|
||||||
if (resolver->thread) {
|
if (resolver->thread.is_started()) {
|
||||||
resolver->sem.post();
|
resolver->sem.post();
|
||||||
} else {
|
} else {
|
||||||
resolver->resolve_queues();
|
resolver->resolve_queues();
|
||||||
|
@ -285,26 +285,14 @@ IP::IP() {
|
||||||
singleton = this;
|
singleton = this;
|
||||||
resolver = memnew(_IP_ResolverPrivate);
|
resolver = memnew(_IP_ResolverPrivate);
|
||||||
|
|
||||||
#ifndef NO_THREADS
|
|
||||||
|
|
||||||
resolver->thread_abort = false;
|
resolver->thread_abort = false;
|
||||||
|
resolver->thread.start(_IP_ResolverPrivate::_thread_function, resolver);
|
||||||
resolver->thread = Thread::create(_IP_ResolverPrivate::_thread_function, resolver);
|
|
||||||
#else
|
|
||||||
resolver->thread = nullptr;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
IP::~IP() {
|
IP::~IP() {
|
||||||
#ifndef NO_THREADS
|
resolver->thread_abort = true;
|
||||||
if (resolver->thread) {
|
resolver->sem.post();
|
||||||
resolver->thread_abort = true;
|
resolver->thread.wait_to_finish();
|
||||||
resolver->sem.post();
|
|
||||||
Thread::wait_to_finish(resolver->thread);
|
|
||||||
memdelete(resolver->thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
memdelete(resolver);
|
memdelete(resolver);
|
||||||
}
|
}
|
||||||
|
|
|
@ -362,7 +362,8 @@ Error ResourceLoader::load_threaded_request(const String &p_path, const String &
|
||||||
|
|
||||||
print_lt("REQUEST: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count));
|
print_lt("REQUEST: load count: " + itos(thread_loading_count) + " / wait count: " + itos(thread_waiting_count) + " / suspended count: " + itos(thread_suspended_count) + " / active: " + itos(thread_loading_count - thread_suspended_count));
|
||||||
|
|
||||||
load_task.thread = Thread::create(_thread_load_function, &thread_load_tasks[local_path]);
|
load_task.thread = memnew(Thread);
|
||||||
|
load_task.thread->start(_thread_load_function, &thread_load_tasks[local_path]);
|
||||||
load_task.loader_id = load_task.thread->get_id();
|
load_task.loader_id = load_task.thread->get_id();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -489,7 +490,7 @@ RES ResourceLoader::load_threaded_get(const String &p_path, Error *r_error) {
|
||||||
|
|
||||||
if (load_task.requests == 0) {
|
if (load_task.requests == 0) {
|
||||||
if (load_task.thread) { //thread may not have been used
|
if (load_task.thread) { //thread may not have been used
|
||||||
Thread::wait_to_finish(load_task.thread);
|
load_task.thread->wait_to_finish();
|
||||||
memdelete(load_task.thread);
|
memdelete(load_task.thread);
|
||||||
}
|
}
|
||||||
thread_load_tasks.erase(local_path);
|
thread_load_tasks.erase(local_path);
|
||||||
|
|
|
@ -30,30 +30,70 @@
|
||||||
|
|
||||||
#include "thread.h"
|
#include "thread.h"
|
||||||
|
|
||||||
Thread *(*Thread::create_func)(ThreadCreateCallback, void *, const Settings &) = nullptr;
|
#include "core/object/script_language.h"
|
||||||
Thread::ID (*Thread::get_thread_id_func)() = nullptr;
|
|
||||||
void (*Thread::wait_to_finish_func)(Thread *) = nullptr;
|
#if !defined(NO_THREADS)
|
||||||
|
|
||||||
Error (*Thread::set_name_func)(const String &) = nullptr;
|
Error (*Thread::set_name_func)(const String &) = nullptr;
|
||||||
|
void (*Thread::set_priority_func)(Thread::Priority) = nullptr;
|
||||||
|
void (*Thread::init_func)() = nullptr;
|
||||||
|
void (*Thread::term_func)() = nullptr;
|
||||||
|
|
||||||
Thread::ID Thread::_main_thread_id = 0;
|
Thread::ID Thread::main_thread_id = 1;
|
||||||
|
Thread::ID Thread::last_thread_id = 1;
|
||||||
|
thread_local Thread::ID Thread::caller_id = 1;
|
||||||
|
|
||||||
Thread::ID Thread::get_caller_id() {
|
void Thread::_set_platform_funcs(
|
||||||
if (get_thread_id_func) {
|
Error (*p_set_name_func)(const String &),
|
||||||
return get_thread_id_func();
|
void (*p_set_priority_func)(Thread::Priority),
|
||||||
}
|
void (*p_init_func)(),
|
||||||
return 0;
|
void (*p_term_func)()) {
|
||||||
|
Thread::set_name_func = p_set_name_func;
|
||||||
|
Thread::set_priority_func = p_set_priority_func;
|
||||||
|
Thread::init_func = p_init_func;
|
||||||
|
Thread::term_func = p_term_func;
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread *Thread::create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings) {
|
void Thread::callback(Thread *p_self, const Settings &p_settings, Callback p_callback, void *p_userdata) {
|
||||||
if (create_func) {
|
Thread::caller_id = p_self->id;
|
||||||
return create_func(p_callback, p_user, p_settings);
|
if (set_priority_func) {
|
||||||
|
set_priority_func(p_settings.priority);
|
||||||
|
}
|
||||||
|
if (init_func) {
|
||||||
|
init_func();
|
||||||
|
}
|
||||||
|
ScriptServer::thread_enter(); //scripts may need to attach a stack
|
||||||
|
p_callback(p_userdata);
|
||||||
|
ScriptServer::thread_exit();
|
||||||
|
if (term_func) {
|
||||||
|
term_func();
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Thread::wait_to_finish(Thread *p_thread) {
|
void Thread::start(Thread::Callback p_callback, void *p_user, const Settings &p_settings) {
|
||||||
if (wait_to_finish_func) {
|
if (id != 0) {
|
||||||
wait_to_finish_func(p_thread);
|
#ifdef DEBUG_ENABLED
|
||||||
|
WARN_PRINT("A Thread object has been re-started without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
|
||||||
|
#endif
|
||||||
|
thread.detach();
|
||||||
|
std::thread empty_thread;
|
||||||
|
thread.swap(empty_thread);
|
||||||
|
}
|
||||||
|
id = atomic_increment(&last_thread_id);
|
||||||
|
std::thread new_thread(&Thread::callback, this, p_settings, p_callback, p_user);
|
||||||
|
thread.swap(new_thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Thread::is_started() const {
|
||||||
|
return id != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Thread::wait_to_finish() {
|
||||||
|
if (id != 0) {
|
||||||
|
thread.join();
|
||||||
|
std::thread empty_thread;
|
||||||
|
thread.swap(empty_thread);
|
||||||
|
id = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,3 +104,14 @@ Error Thread::set_name(const String &p_name) {
|
||||||
|
|
||||||
return ERR_UNAVAILABLE;
|
return ERR_UNAVAILABLE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Thread::~Thread() {
|
||||||
|
if (id != 0) {
|
||||||
|
#ifdef DEBUG_ENABLED
|
||||||
|
WARN_PRINT("A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.");
|
||||||
|
#endif
|
||||||
|
thread.detach();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
|
@ -31,13 +31,20 @@
|
||||||
#ifndef THREAD_H
|
#ifndef THREAD_H
|
||||||
#define THREAD_H
|
#define THREAD_H
|
||||||
|
|
||||||
#include "core/string/ustring.h"
|
|
||||||
#include "core/typedefs.h"
|
#include "core/typedefs.h"
|
||||||
|
|
||||||
typedef void (*ThreadCreateCallback)(void *p_userdata);
|
#if !defined(NO_THREADS)
|
||||||
|
#include <thread>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
class String;
|
||||||
|
|
||||||
class Thread {
|
class Thread {
|
||||||
public:
|
public:
|
||||||
|
typedef void (*Callback)(void *p_userdata);
|
||||||
|
|
||||||
|
typedef uint64_t ID;
|
||||||
|
|
||||||
enum Priority {
|
enum Priority {
|
||||||
PRIORITY_LOW,
|
PRIORITY_LOW,
|
||||||
PRIORITY_NORMAL,
|
PRIORITY_NORMAL,
|
||||||
|
@ -49,30 +56,60 @@ public:
|
||||||
Settings() { priority = PRIORITY_NORMAL; }
|
Settings() { priority = PRIORITY_NORMAL; }
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef uint64_t ID;
|
private:
|
||||||
|
#if !defined(NO_THREADS)
|
||||||
protected:
|
|
||||||
static Thread *(*create_func)(ThreadCreateCallback p_callback, void *, const Settings &);
|
|
||||||
static ID (*get_thread_id_func)();
|
|
||||||
static void (*wait_to_finish_func)(Thread *);
|
|
||||||
static Error (*set_name_func)(const String &);
|
|
||||||
|
|
||||||
friend class Main;
|
friend class Main;
|
||||||
|
|
||||||
static ID _main_thread_id;
|
static ID main_thread_id;
|
||||||
|
static ID last_thread_id;
|
||||||
|
|
||||||
Thread() {}
|
ID id = 0;
|
||||||
|
static thread_local ID caller_id;
|
||||||
|
std::thread thread;
|
||||||
|
|
||||||
|
static void callback(Thread *p_self, const Settings &p_settings, Thread::Callback p_callback, void *p_userdata);
|
||||||
|
|
||||||
|
static Error (*set_name_func)(const String &);
|
||||||
|
static void (*set_priority_func)(Thread::Priority);
|
||||||
|
static void (*init_func)();
|
||||||
|
static void (*term_func)();
|
||||||
|
#endif
|
||||||
|
|
||||||
public:
|
public:
|
||||||
virtual ID get_id() const = 0;
|
static void _set_platform_funcs(
|
||||||
|
Error (*p_set_name_func)(const String &),
|
||||||
|
void (*p_set_priority_func)(Thread::Priority),
|
||||||
|
void (*p_init_func)() = nullptr,
|
||||||
|
void (*p_term_func)() = nullptr);
|
||||||
|
|
||||||
|
#if !defined(NO_THREADS)
|
||||||
|
_FORCE_INLINE_ ID get_id() const { return id; }
|
||||||
|
// get the ID of the caller thread
|
||||||
|
_FORCE_INLINE_ static ID get_caller_id() { return caller_id; }
|
||||||
|
// get the ID of the main thread
|
||||||
|
_FORCE_INLINE_ static ID get_main_id() { return main_thread_id; }
|
||||||
|
|
||||||
static Error set_name(const String &p_name);
|
static Error set_name(const String &p_name);
|
||||||
_FORCE_INLINE_ static ID get_main_id() { return _main_thread_id; } ///< get the ID of the main thread
|
|
||||||
static ID get_caller_id(); ///< get the ID of the caller function ID
|
|
||||||
static void wait_to_finish(Thread *p_thread); ///< waits until thread is finished, and deallocates it.
|
|
||||||
static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings()); ///< Static function to create a thread, will call p_callback
|
|
||||||
|
|
||||||
virtual ~Thread() {}
|
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings());
|
||||||
|
bool is_started() const;
|
||||||
|
///< waits until thread is finished, and deallocates it.
|
||||||
|
void wait_to_finish();
|
||||||
|
|
||||||
|
~Thread();
|
||||||
|
#else
|
||||||
|
_FORCE_INLINE_ ID get_id() const { return 0; }
|
||||||
|
// get the ID of the caller thread
|
||||||
|
_FORCE_INLINE_ static ID get_caller_id() { return 0; }
|
||||||
|
// get the ID of the main thread
|
||||||
|
_FORCE_INLINE_ static ID get_main_id() { return 0; }
|
||||||
|
|
||||||
|
static Error set_name(const String &p_name) { return ERR_UNAVAILABLE; }
|
||||||
|
|
||||||
|
void start(Thread::Callback p_callback, void *p_user, const Settings &p_settings = Settings()) {}
|
||||||
|
bool is_started() const { return false; }
|
||||||
|
void wait_to_finish() {}
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // THREAD_H
|
#endif // THREAD_H
|
||||||
|
|
|
@ -1,41 +0,0 @@
|
||||||
/*************************************************************************/
|
|
||||||
/* thread_dummy.cpp */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* Copyright (c) 2014-2021 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 "thread_dummy.h"
|
|
||||||
|
|
||||||
#include "core/os/memory.h"
|
|
||||||
|
|
||||||
Thread *ThreadDummy::create(ThreadCreateCallback p_callback, void *p_user, const Thread::Settings &p_settings) {
|
|
||||||
return memnew(ThreadDummy);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadDummy::make_default() {
|
|
||||||
Thread::create_func = &ThreadDummy::create;
|
|
||||||
}
|
|
|
@ -1,47 +0,0 @@
|
||||||
/*************************************************************************/
|
|
||||||
/* thread_dummy.h */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* Copyright (c) 2014-2021 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 THREAD_DUMMY_H
|
|
||||||
#define THREAD_DUMMY_H
|
|
||||||
|
|
||||||
#include "core/os/rw_lock.h"
|
|
||||||
#include "core/os/semaphore.h"
|
|
||||||
#include "core/os/thread.h"
|
|
||||||
|
|
||||||
class ThreadDummy : public Thread {
|
|
||||||
static Thread *create(ThreadCreateCallback p_callback, void *p_user, const Settings &p_settings = Settings());
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ID get_id() const { return 0; };
|
|
||||||
|
|
||||||
static void make_default();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // THREAD_DUMMY_H
|
|
|
@ -74,18 +74,17 @@ void thread_process_array(uint32_t p_elements, C *p_instance, M p_method, U p_us
|
||||||
data.elements = p_elements;
|
data.elements = p_elements;
|
||||||
data.process(data.index); //process first, let threads increment for next
|
data.process(data.index); //process first, let threads increment for next
|
||||||
|
|
||||||
Vector<Thread *> threads;
|
int thread_count = OS::get_singleton()->get_processor_count();
|
||||||
|
Thread *threads = memnew_arr(Thread, thread_count);
|
||||||
|
|
||||||
threads.resize(OS::get_singleton()->get_processor_count());
|
for (int i = 0; i < thread_count; i++) {
|
||||||
|
threads[i].start(process_array_thread<ThreadArrayProcessData<C, U>>, &data);
|
||||||
for (int i = 0; i < threads.size(); i++) {
|
|
||||||
threads.write[i] = Thread::create(process_array_thread<ThreadArrayProcessData<C, U>>, &data);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < threads.size(); i++) {
|
for (int i = 0; i < thread_count; i++) {
|
||||||
Thread::wait_to_finish(threads[i]);
|
threads[i].wait_to_finish();
|
||||||
memdelete(threads[i]);
|
|
||||||
}
|
}
|
||||||
|
memdelete_arr(threads);
|
||||||
}
|
}
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
|
@ -32,14 +32,15 @@
|
||||||
|
|
||||||
#include "core/os/os.h"
|
#include "core/os/os.h"
|
||||||
|
|
||||||
void ThreadWorkPool::_thread_function(ThreadData *p_thread) {
|
void ThreadWorkPool::_thread_function(void *p_user) {
|
||||||
|
ThreadData *thread = static_cast<ThreadData *>(p_user);
|
||||||
while (true) {
|
while (true) {
|
||||||
p_thread->start.wait();
|
thread->start.wait();
|
||||||
if (p_thread->exit.load()) {
|
if (thread->exit.load()) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
p_thread->work->work();
|
thread->work->work();
|
||||||
p_thread->completed.post();
|
thread->completed.post();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -54,7 +55,7 @@ void ThreadWorkPool::init(int p_thread_count) {
|
||||||
|
|
||||||
for (uint32_t i = 0; i < thread_count; i++) {
|
for (uint32_t i = 0; i < thread_count; i++) {
|
||||||
threads[i].exit.store(false);
|
threads[i].exit.store(false);
|
||||||
threads[i].thread = memnew(std::thread(ThreadWorkPool::_thread_function, &threads[i]));
|
threads[i].thread.start(&ThreadWorkPool::_thread_function, &threads[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,8 +69,7 @@ void ThreadWorkPool::finish() {
|
||||||
threads[i].start.post();
|
threads[i].start.post();
|
||||||
}
|
}
|
||||||
for (uint32_t i = 0; i < thread_count; i++) {
|
for (uint32_t i = 0; i < thread_count; i++) {
|
||||||
threads[i].thread->join();
|
threads[i].thread.wait_to_finish();
|
||||||
memdelete(threads[i].thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memdelete_arr(threads);
|
memdelete_arr(threads);
|
||||||
|
|
|
@ -33,9 +33,9 @@
|
||||||
|
|
||||||
#include "core/os/memory.h"
|
#include "core/os/memory.h"
|
||||||
#include "core/os/semaphore.h"
|
#include "core/os/semaphore.h"
|
||||||
|
#include "core/os/thread.h"
|
||||||
|
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
class ThreadWorkPool {
|
class ThreadWorkPool {
|
||||||
std::atomic<uint32_t> index;
|
std::atomic<uint32_t> index;
|
||||||
|
@ -64,7 +64,7 @@ class ThreadWorkPool {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ThreadData {
|
struct ThreadData {
|
||||||
std::thread *thread;
|
Thread thread;
|
||||||
Semaphore start;
|
Semaphore start;
|
||||||
Semaphore completed;
|
Semaphore completed;
|
||||||
std::atomic<bool> exit;
|
std::atomic<bool> exit;
|
||||||
|
@ -75,7 +75,7 @@ class ThreadWorkPool {
|
||||||
uint32_t thread_count = 0;
|
uint32_t thread_count = 0;
|
||||||
BaseWork *current_work = nullptr;
|
BaseWork *current_work = nullptr;
|
||||||
|
|
||||||
static void _thread_function(ThreadData *p_thread);
|
static void _thread_function(void *p_user);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
template <class C, class M, class U>
|
template <class C, class M, class U>
|
||||||
|
|
|
@ -153,7 +153,7 @@ Error AudioDriverALSA::init() {
|
||||||
|
|
||||||
Error err = init_device();
|
Error err = init_device();
|
||||||
if (err == OK) {
|
if (err == OK) {
|
||||||
thread = Thread::create(AudioDriverALSA::thread_func, this);
|
thread.start(AudioDriverALSA::thread_func, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -291,16 +291,10 @@ void AudioDriverALSA::set_device(String device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverALSA::lock() {
|
void AudioDriverALSA::lock() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverALSA::unlock() {
|
void AudioDriverALSA::unlock() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -312,13 +306,8 @@ void AudioDriverALSA::finish_device() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverALSA::finish() {
|
void AudioDriverALSA::finish() {
|
||||||
if (thread) {
|
exit_thread = true;
|
||||||
exit_thread = true;
|
thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(thread);
|
|
||||||
|
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish_device();
|
finish_device();
|
||||||
}
|
}
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#include <alsa/asoundlib.h>
|
#include <alsa/asoundlib.h>
|
||||||
|
|
||||||
class AudioDriverALSA : public AudioDriver {
|
class AudioDriverALSA : public AudioDriver {
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
snd_pcm_t *pcm_handle = nullptr;
|
snd_pcm_t *pcm_handle = nullptr;
|
||||||
|
|
|
@ -150,19 +150,14 @@ Error MIDIDriverALSAMidi::open() {
|
||||||
snd_device_name_free_hint(hints);
|
snd_device_name_free_hint(hints);
|
||||||
|
|
||||||
exit_thread = false;
|
exit_thread = false;
|
||||||
thread = Thread::create(MIDIDriverALSAMidi::thread_func, this);
|
thread.start(MIDIDriverALSAMidi::thread_func, this);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MIDIDriverALSAMidi::close() {
|
void MIDIDriverALSAMidi::close() {
|
||||||
if (thread) {
|
exit_thread = true;
|
||||||
exit_thread = true;
|
thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(thread);
|
|
||||||
|
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < connected_inputs.size(); i++) {
|
for (int i = 0; i < connected_inputs.size(); i++) {
|
||||||
snd_rawmidi_t *midi_in = connected_inputs[i];
|
snd_rawmidi_t *midi_in = connected_inputs[i];
|
||||||
|
@ -198,8 +193,6 @@ PackedStringArray MIDIDriverALSAMidi::get_connected_inputs() {
|
||||||
}
|
}
|
||||||
|
|
||||||
MIDIDriverALSAMidi::MIDIDriverALSAMidi() {
|
MIDIDriverALSAMidi::MIDIDriverALSAMidi() {
|
||||||
thread = nullptr;
|
|
||||||
|
|
||||||
exit_thread = false;
|
exit_thread = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
class MIDIDriverALSAMidi : public MIDIDriver {
|
class MIDIDriverALSAMidi : public MIDIDriver {
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
Vector<snd_rawmidi_t *> connected_inputs;
|
Vector<snd_rawmidi_t *> connected_inputs;
|
||||||
|
|
|
@ -287,7 +287,7 @@ Error AudioDriverPulseAudio::init() {
|
||||||
|
|
||||||
Error err = init_device();
|
Error err = init_device();
|
||||||
if (err == OK) {
|
if (err == OK) {
|
||||||
thread = Thread::create(AudioDriverPulseAudio::thread_func, this);
|
thread.start(AudioDriverPulseAudio::thread_func, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -581,16 +581,10 @@ void AudioDriverPulseAudio::set_device(String device) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverPulseAudio::lock() {
|
void AudioDriverPulseAudio::lock() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverPulseAudio::unlock() {
|
void AudioDriverPulseAudio::unlock() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -603,12 +597,12 @@ void AudioDriverPulseAudio::finish_device() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverPulseAudio::finish() {
|
void AudioDriverPulseAudio::finish() {
|
||||||
if (!thread) {
|
if (!thread.is_started()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
exit_thread = true;
|
exit_thread = true;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
|
|
||||||
finish_device();
|
finish_device();
|
||||||
|
|
||||||
|
@ -622,10 +616,6 @@ void AudioDriverPulseAudio::finish() {
|
||||||
pa_mainloop_free(pa_ml);
|
pa_mainloop_free(pa_ml);
|
||||||
pa_ml = nullptr;
|
pa_ml = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
memdelete(thread);
|
|
||||||
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Error AudioDriverPulseAudio::capture_init_device() {
|
Error AudioDriverPulseAudio::capture_init_device() {
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
#include <pulse/pulseaudio.h>
|
#include <pulse/pulseaudio.h>
|
||||||
|
|
||||||
class AudioDriverPulseAudio : public AudioDriver {
|
class AudioDriverPulseAudio : public AudioDriver {
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
pa_mainloop *pa_ml = nullptr;
|
pa_mainloop *pa_ml = nullptr;
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
#include "core/config/project_settings.h"
|
#include "core/config/project_settings.h"
|
||||||
#include "core/debugger/engine_debugger.h"
|
#include "core/debugger/engine_debugger.h"
|
||||||
#include "core/debugger/script_debugger.h"
|
#include "core/debugger/script_debugger.h"
|
||||||
#include "core/os/thread_dummy.h"
|
|
||||||
#include "drivers/unix/dir_access_unix.h"
|
#include "drivers/unix/dir_access_unix.h"
|
||||||
#include "drivers/unix/file_access_unix.h"
|
#include "drivers/unix/file_access_unix.h"
|
||||||
#include "drivers/unix/net_socket_posix.h"
|
#include "drivers/unix/net_socket_posix.h"
|
||||||
|
@ -63,6 +62,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/// Clock Setup function (used by get_ticks_usec)
|
/// Clock Setup function (used by get_ticks_usec)
|
||||||
|
@ -116,11 +116,10 @@ int OS_Unix::unix_initialize_audio(int p_audio_driver) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void OS_Unix::initialize_core() {
|
void OS_Unix::initialize_core() {
|
||||||
#ifdef NO_THREADS
|
#if !defined(NO_THREADS)
|
||||||
ThreadDummy::make_default();
|
init_thread_posix();
|
||||||
#else
|
|
||||||
ThreadPosix::make_default();
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
|
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_RESOURCES);
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
|
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_USERDATA);
|
||||||
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
|
FileAccess::make_default<FileAccessUnix>(FileAccess::ACCESS_FILESYSTEM);
|
||||||
|
|
|
@ -28,88 +28,14 @@
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
#include "thread_posix.h"
|
|
||||||
|
|
||||||
#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS)
|
#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS)
|
||||||
|
|
||||||
#include "core/object/script_language.h"
|
#include "thread_posix.h"
|
||||||
#include "core/os/memory.h"
|
|
||||||
#include "core/templates/safe_refcount.h"
|
|
||||||
|
|
||||||
#ifdef PTHREAD_BSD_SET_NAME
|
#include "core/os/thread.h"
|
||||||
#include <pthread_np.h>
|
#include "core/string/ustring.h"
|
||||||
#endif
|
|
||||||
|
|
||||||
static void _thread_id_key_destr_callback(void *p_value) {
|
static Error set_name(const String &p_name) {
|
||||||
memdelete(static_cast<Thread::ID *>(p_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_key_t _create_thread_id_key() {
|
|
||||||
pthread_key_t key;
|
|
||||||
pthread_key_create(&key, &_thread_id_key_destr_callback);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_key_t ThreadPosix::thread_id_key = _create_thread_id_key();
|
|
||||||
Thread::ID ThreadPosix::next_thread_id = 0;
|
|
||||||
|
|
||||||
Thread::ID ThreadPosix::get_id() const {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *ThreadPosix::create_thread_posix() {
|
|
||||||
return memnew(ThreadPosix);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *ThreadPosix::thread_callback(void *userdata) {
|
|
||||||
ThreadPosix *t = reinterpret_cast<ThreadPosix *>(userdata);
|
|
||||||
t->id = atomic_increment(&next_thread_id);
|
|
||||||
pthread_setspecific(thread_id_key, (void *)memnew(ID(t->id)));
|
|
||||||
|
|
||||||
ScriptServer::thread_enter(); //scripts may need to attach a stack
|
|
||||||
|
|
||||||
t->callback(t->user);
|
|
||||||
|
|
||||||
ScriptServer::thread_exit();
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *ThreadPosix::create_func_posix(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
|
|
||||||
ThreadPosix *tr = memnew(ThreadPosix);
|
|
||||||
tr->callback = p_callback;
|
|
||||||
tr->user = p_user;
|
|
||||||
pthread_attr_init(&tr->pthread_attr);
|
|
||||||
pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE);
|
|
||||||
pthread_attr_setstacksize(&tr->pthread_attr, 256 * 1024);
|
|
||||||
|
|
||||||
pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr);
|
|
||||||
|
|
||||||
return tr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread::ID ThreadPosix::get_thread_id_func_posix() {
|
|
||||||
void *value = pthread_getspecific(thread_id_key);
|
|
||||||
|
|
||||||
if (value) {
|
|
||||||
return *static_cast<ID *>(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
ID new_id = atomic_increment(&next_thread_id);
|
|
||||||
pthread_setspecific(thread_id_key, (void *)memnew(ID(new_id)));
|
|
||||||
return new_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadPosix::wait_to_finish_func_posix(Thread *p_thread) {
|
|
||||||
ThreadPosix *tp = static_cast<ThreadPosix *>(p_thread);
|
|
||||||
ERR_FAIL_COND(!tp);
|
|
||||||
ERR_FAIL_COND(tp->pthread == 0);
|
|
||||||
|
|
||||||
pthread_join(tp->pthread, nullptr);
|
|
||||||
tp->pthread = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Error ThreadPosix::set_name_func_posix(const String &p_name) {
|
|
||||||
#ifdef PTHREAD_NO_RENAME
|
#ifdef PTHREAD_NO_RENAME
|
||||||
return ERR_UNAVAILABLE;
|
return ERR_UNAVAILABLE;
|
||||||
|
|
||||||
|
@ -137,20 +63,10 @@ Error ThreadPosix::set_name_func_posix(const String &p_name) {
|
||||||
return err == 0 ? OK : ERR_INVALID_PARAMETER;
|
return err == 0 ? OK : ERR_INVALID_PARAMETER;
|
||||||
|
|
||||||
#endif // PTHREAD_NO_RENAME
|
#endif // PTHREAD_NO_RENAME
|
||||||
};
|
|
||||||
|
|
||||||
void ThreadPosix::make_default() {
|
|
||||||
create_func = create_func_posix;
|
|
||||||
get_thread_id_func = get_thread_id_func_posix;
|
|
||||||
wait_to_finish_func = wait_to_finish_func_posix;
|
|
||||||
set_name_func = set_name_func_posix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadPosix::ThreadPosix() {
|
void init_thread_posix() {
|
||||||
pthread = 0;
|
Thread::_set_platform_funcs(&set_name, nullptr);
|
||||||
}
|
|
||||||
|
|
||||||
ThreadPosix::~ThreadPosix() {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -31,42 +31,8 @@
|
||||||
#ifndef THREAD_POSIX_H
|
#ifndef THREAD_POSIX_H
|
||||||
#define THREAD_POSIX_H
|
#define THREAD_POSIX_H
|
||||||
|
|
||||||
#if (defined(UNIX_ENABLED) || defined(PTHREAD_ENABLED)) && !defined(NO_THREADS)
|
#if !defined(NO_THREADS)
|
||||||
|
void init_thread_posix();
|
||||||
#include "core/os/thread.h"
|
|
||||||
#include <pthread.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
class ThreadPosix : public Thread {
|
|
||||||
static pthread_key_t thread_id_key;
|
|
||||||
static ID next_thread_id;
|
|
||||||
|
|
||||||
pthread_t pthread;
|
|
||||||
pthread_attr_t pthread_attr;
|
|
||||||
ThreadCreateCallback callback;
|
|
||||||
void *user;
|
|
||||||
ID id;
|
|
||||||
|
|
||||||
static Thread *create_thread_posix();
|
|
||||||
|
|
||||||
static void *thread_callback(void *userdata);
|
|
||||||
|
|
||||||
static Thread *create_func_posix(ThreadCreateCallback p_callback, void *, const Settings &);
|
|
||||||
static ID get_thread_id_func_posix();
|
|
||||||
static void wait_to_finish_func_posix(Thread *p_thread);
|
|
||||||
|
|
||||||
static Error set_name_func_posix(const String &p_name);
|
|
||||||
|
|
||||||
ThreadPosix();
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ID get_id() const;
|
|
||||||
|
|
||||||
static void make_default();
|
|
||||||
|
|
||||||
~ThreadPosix();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -397,7 +397,7 @@ Error AudioDriverWASAPI::init() {
|
||||||
exit_thread = false;
|
exit_thread = false;
|
||||||
thread_exited = false;
|
thread_exited = false;
|
||||||
|
|
||||||
thread = Thread::create(thread_func, this);
|
thread.start(thread_func, this);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -767,13 +767,8 @@ void AudioDriverWASAPI::unlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverWASAPI::finish() {
|
void AudioDriverWASAPI::finish() {
|
||||||
if (thread) {
|
exit_thread = true;
|
||||||
exit_thread = true;
|
thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(thread);
|
|
||||||
|
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
finish_capture_device();
|
finish_capture_device();
|
||||||
finish_render_device();
|
finish_render_device();
|
||||||
|
|
|
@ -64,7 +64,7 @@ class AudioDriverWASAPI : public AudioDriver {
|
||||||
AudioDeviceWASAPI audio_output;
|
AudioDeviceWASAPI audio_output;
|
||||||
|
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
|
|
||||||
Vector<int32_t> samples_in;
|
Vector<int32_t> samples_in;
|
||||||
|
|
||||||
|
|
|
@ -1,88 +0,0 @@
|
||||||
/*************************************************************************/
|
|
||||||
/* thread_windows.cpp */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* Copyright (c) 2014-2021 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 "thread_windows.h"
|
|
||||||
|
|
||||||
#if defined(WINDOWS_ENABLED) && !defined(UWP_ENABLED)
|
|
||||||
|
|
||||||
#include "core/os/memory.h"
|
|
||||||
|
|
||||||
Thread::ID ThreadWindows::get_id() const {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *ThreadWindows::create_thread_windows() {
|
|
||||||
return memnew(ThreadWindows);
|
|
||||||
}
|
|
||||||
|
|
||||||
DWORD ThreadWindows::thread_callback(LPVOID userdata) {
|
|
||||||
ThreadWindows *t = reinterpret_cast<ThreadWindows *>(userdata);
|
|
||||||
|
|
||||||
ScriptServer::thread_enter(); //scripts may need to attach a stack
|
|
||||||
|
|
||||||
t->id = (ID)GetCurrentThreadId(); // must implement
|
|
||||||
t->callback(t->user);
|
|
||||||
SetEvent(t->handle);
|
|
||||||
|
|
||||||
ScriptServer::thread_exit();
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *ThreadWindows::create_func_windows(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
|
|
||||||
ThreadWindows *tr = memnew(ThreadWindows);
|
|
||||||
tr->callback = p_callback;
|
|
||||||
tr->user = p_user;
|
|
||||||
tr->handle = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
|
||||||
|
|
||||||
QueueUserWorkItem(thread_callback, tr, WT_EXECUTELONGFUNCTION);
|
|
||||||
|
|
||||||
return tr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread::ID ThreadWindows::get_thread_id_func_windows() {
|
|
||||||
return (ID)GetCurrentThreadId(); //must implement
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadWindows::wait_to_finish_func_windows(Thread *p_thread) {
|
|
||||||
ThreadWindows *tp = static_cast<ThreadWindows *>(p_thread);
|
|
||||||
ERR_FAIL_COND(!tp);
|
|
||||||
WaitForSingleObject(tp->handle, INFINITE);
|
|
||||||
CloseHandle(tp->handle);
|
|
||||||
//`memdelete(tp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadWindows::make_default() {
|
|
||||||
create_func = create_func_windows;
|
|
||||||
get_thread_id_func = get_thread_id_func_windows;
|
|
||||||
wait_to_finish_func = wait_to_finish_func_windows;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,67 +0,0 @@
|
||||||
/*************************************************************************/
|
|
||||||
/* thread_windows.h */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* Copyright (c) 2014-2021 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 THREAD_WINDOWS_H
|
|
||||||
#define THREAD_WINDOWS_H
|
|
||||||
|
|
||||||
#ifdef WINDOWS_ENABLED
|
|
||||||
|
|
||||||
#include "core/object/script_language.h"
|
|
||||||
#include "core/os/thread.h"
|
|
||||||
|
|
||||||
#include <windows.h>
|
|
||||||
|
|
||||||
class ThreadWindows : public Thread {
|
|
||||||
ThreadCreateCallback callback;
|
|
||||||
void *user;
|
|
||||||
ID id;
|
|
||||||
HANDLE handle = nullptr;
|
|
||||||
|
|
||||||
static Thread *create_thread_windows();
|
|
||||||
|
|
||||||
static DWORD WINAPI thread_callback(LPVOID userdata);
|
|
||||||
|
|
||||||
static Thread *create_func_windows(ThreadCreateCallback p_callback, void *, const Settings &);
|
|
||||||
static ID get_thread_id_func_windows();
|
|
||||||
static void wait_to_finish_func_windows(Thread *p_thread);
|
|
||||||
|
|
||||||
ThreadWindows() {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ID get_id() const;
|
|
||||||
|
|
||||||
static void make_default();
|
|
||||||
|
|
||||||
~ThreadWindows() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -78,7 +78,7 @@ Error AudioDriverXAudio2::init() {
|
||||||
hr = xaudio->CreateSourceVoice(&source_voice, &wave_format, 0, XAUDIO2_MAX_FREQ_RATIO, &voice_callback);
|
hr = xaudio->CreateSourceVoice(&source_voice, &wave_format, 0, XAUDIO2_MAX_FREQ_RATIO, &voice_callback);
|
||||||
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_UNAVAILABLE, "Error creating XAudio2 source voice. Error code: " + itos(hr) + ".");
|
ERR_FAIL_COND_V_MSG(hr != S_OK, ERR_UNAVAILABLE, "Error creating XAudio2 source voice. Error code: " + itos(hr) + ".");
|
||||||
|
|
||||||
thread = Thread::create(AudioDriverXAudio2::thread_func, this);
|
thread.start(AudioDriverXAudio2::thread_func, this);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
@ -146,23 +146,16 @@ float AudioDriverXAudio2::get_latency() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverXAudio2::lock() {
|
void AudioDriverXAudio2::lock() {
|
||||||
if (!thread)
|
|
||||||
return;
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverXAudio2::unlock() {
|
void AudioDriverXAudio2::unlock() {
|
||||||
if (!thread)
|
|
||||||
return;
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverXAudio2::finish() {
|
void AudioDriverXAudio2::finish() {
|
||||||
if (!thread)
|
|
||||||
return;
|
|
||||||
|
|
||||||
exit_thread = true;
|
exit_thread = true;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
|
|
||||||
if (source_voice) {
|
if (source_voice) {
|
||||||
source_voice->Stop(0);
|
source_voice->Stop(0);
|
||||||
|
@ -179,9 +172,6 @@ void AudioDriverXAudio2::finish() {
|
||||||
}
|
}
|
||||||
|
|
||||||
mastering_voice->DestroyVoice();
|
mastering_voice->DestroyVoice();
|
||||||
|
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
AudioDriverXAudio2::AudioDriverXAudio2() {
|
AudioDriverXAudio2::AudioDriverXAudio2() {
|
||||||
|
|
|
@ -62,7 +62,7 @@ class AudioDriverXAudio2 : public AudioDriver {
|
||||||
void STDMETHODCALLTYPE OnVoiceError(void *pBufferContext, HRESULT Error) {}
|
void STDMETHODCALLTYPE OnVoiceError(void *pBufferContext, HRESULT Error) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
int32_t *samples_in = nullptr;
|
int32_t *samples_in = nullptr;
|
||||||
|
|
|
@ -197,7 +197,8 @@ Ref<AudioStreamPreview> AudioStreamPreviewGenerator::generate_preview(const Ref<
|
||||||
preview->preview->length = len_s;
|
preview->preview->length = len_s;
|
||||||
|
|
||||||
if (preview->playback.is_valid()) {
|
if (preview->playback.is_valid()) {
|
||||||
preview->thread = Thread::create(_preview_thread, preview);
|
preview->thread = memnew(Thread);
|
||||||
|
preview->thread->start(_preview_thread, preview);
|
||||||
}
|
}
|
||||||
|
|
||||||
return preview->preview;
|
return preview->preview;
|
||||||
|
@ -218,7 +219,8 @@ void AudioStreamPreviewGenerator::_notification(int p_what) {
|
||||||
for (Map<ObjectID, Preview>::Element *E = previews.front(); E; E = E->next()) {
|
for (Map<ObjectID, Preview>::Element *E = previews.front(); E; E = E->next()) {
|
||||||
if (!E->get().generating) {
|
if (!E->get().generating) {
|
||||||
if (E->get().thread) {
|
if (E->get().thread) {
|
||||||
Thread::wait_to_finish(E->get().thread);
|
E->get().thread->wait_to_finish();
|
||||||
|
memdelete(E->get().thread);
|
||||||
E->get().thread = nullptr;
|
E->get().thread = nullptr;
|
||||||
}
|
}
|
||||||
if (!ObjectDB::get_instance(E->key())) { //no longer in use, get rid of preview
|
if (!ObjectDB::get_instance(E->key())) { //no longer in use, get rid of preview
|
||||||
|
|
|
@ -595,7 +595,7 @@ void EditorFileSystem::scan() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (scanning || scanning_changes || thread) {
|
if (scanning || scanning_changes || thread.is_started()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -619,13 +619,13 @@ void EditorFileSystem::scan() {
|
||||||
_queue_update_script_classes();
|
_queue_update_script_classes();
|
||||||
first_scan = false;
|
first_scan = false;
|
||||||
} else {
|
} else {
|
||||||
ERR_FAIL_COND(thread);
|
ERR_FAIL_COND(thread.is_started());
|
||||||
set_process(true);
|
set_process(true);
|
||||||
Thread::Settings s;
|
Thread::Settings s;
|
||||||
scanning = true;
|
scanning = true;
|
||||||
scan_total = 0;
|
scan_total = 0;
|
||||||
s.priority = Thread::PRIORITY_LOW;
|
s.priority = Thread::PRIORITY_LOW;
|
||||||
thread = Thread::create(_thread_func, this, s);
|
thread.start(_thread_func, this, s);
|
||||||
//tree->hide();
|
//tree->hide();
|
||||||
//progress->show();
|
//progress->show();
|
||||||
}
|
}
|
||||||
|
@ -1046,7 +1046,7 @@ void EditorFileSystem::_thread_func_sources(void *_userdata) {
|
||||||
|
|
||||||
void EditorFileSystem::scan_changes() {
|
void EditorFileSystem::scan_changes() {
|
||||||
if (first_scan || // Prevent a premature changes scan from inhibiting the first full scan
|
if (first_scan || // Prevent a premature changes scan from inhibiting the first full scan
|
||||||
scanning || scanning_changes || thread) {
|
scanning || scanning_changes || thread.is_started()) {
|
||||||
scan_changes_pending = true;
|
scan_changes_pending = true;
|
||||||
set_process(true);
|
set_process(true);
|
||||||
return;
|
return;
|
||||||
|
@ -1076,12 +1076,12 @@ void EditorFileSystem::scan_changes() {
|
||||||
scanning_changes_done = true;
|
scanning_changes_done = true;
|
||||||
emit_signal("sources_changed", sources_changed.size() > 0);
|
emit_signal("sources_changed", sources_changed.size() > 0);
|
||||||
} else {
|
} else {
|
||||||
ERR_FAIL_COND(thread_sources);
|
ERR_FAIL_COND(thread_sources.is_started());
|
||||||
set_process(true);
|
set_process(true);
|
||||||
scan_total = 0;
|
scan_total = 0;
|
||||||
Thread::Settings s;
|
Thread::Settings s;
|
||||||
s.priority = Thread::PRIORITY_LOW;
|
s.priority = Thread::PRIORITY_LOW;
|
||||||
thread_sources = Thread::create(_thread_func_sources, this, s);
|
thread_sources.start(_thread_func_sources, this, s);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1092,17 +1092,14 @@ void EditorFileSystem::_notification(int p_what) {
|
||||||
|
|
||||||
} break;
|
} break;
|
||||||
case NOTIFICATION_EXIT_TREE: {
|
case NOTIFICATION_EXIT_TREE: {
|
||||||
Thread *active_thread = thread ? thread : thread_sources;
|
Thread &active_thread = thread.is_started() ? thread : thread_sources;
|
||||||
if (use_threads && active_thread) {
|
if (use_threads && active_thread.is_started()) {
|
||||||
//abort thread if in progress
|
//abort thread if in progress
|
||||||
abort_scan = true;
|
abort_scan = true;
|
||||||
while (scanning) {
|
while (scanning) {
|
||||||
OS::get_singleton()->delay_usec(1000);
|
OS::get_singleton()->delay_usec(1000);
|
||||||
}
|
}
|
||||||
Thread::wait_to_finish(active_thread);
|
active_thread.wait_to_finish();
|
||||||
memdelete(active_thread);
|
|
||||||
thread = nullptr;
|
|
||||||
thread_sources = nullptr;
|
|
||||||
WARN_PRINT("Scan thread aborted...");
|
WARN_PRINT("Scan thread aborted...");
|
||||||
set_process(false);
|
set_process(false);
|
||||||
}
|
}
|
||||||
|
@ -1125,9 +1122,7 @@ void EditorFileSystem::_notification(int p_what) {
|
||||||
|
|
||||||
set_process(false);
|
set_process(false);
|
||||||
|
|
||||||
Thread::wait_to_finish(thread_sources);
|
thread_sources.wait_to_finish();
|
||||||
memdelete(thread_sources);
|
|
||||||
thread_sources = nullptr;
|
|
||||||
if (_update_scan_actions()) {
|
if (_update_scan_actions()) {
|
||||||
emit_signal("filesystem_changed");
|
emit_signal("filesystem_changed");
|
||||||
}
|
}
|
||||||
|
@ -1135,7 +1130,7 @@ void EditorFileSystem::_notification(int p_what) {
|
||||||
_queue_update_script_classes();
|
_queue_update_script_classes();
|
||||||
first_scan = false;
|
first_scan = false;
|
||||||
}
|
}
|
||||||
} else if (!scanning && thread) {
|
} else if (!scanning && thread.is_started()) {
|
||||||
set_process(false);
|
set_process(false);
|
||||||
|
|
||||||
if (filesystem) {
|
if (filesystem) {
|
||||||
|
@ -1143,9 +1138,7 @@ void EditorFileSystem::_notification(int p_what) {
|
||||||
}
|
}
|
||||||
filesystem = new_filesystem;
|
filesystem = new_filesystem;
|
||||||
new_filesystem = nullptr;
|
new_filesystem = nullptr;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
_update_scan_actions();
|
_update_scan_actions();
|
||||||
emit_signal("filesystem_changed");
|
emit_signal("filesystem_changed");
|
||||||
emit_signal("sources_changed", sources_changed.size() > 0);
|
emit_signal("sources_changed", sources_changed.size() > 0);
|
||||||
|
@ -2080,11 +2073,9 @@ EditorFileSystem::EditorFileSystem() {
|
||||||
filesystem = memnew(EditorFileSystemDirectory); //like, empty
|
filesystem = memnew(EditorFileSystemDirectory); //like, empty
|
||||||
filesystem->parent = nullptr;
|
filesystem->parent = nullptr;
|
||||||
|
|
||||||
thread = nullptr;
|
|
||||||
scanning = false;
|
scanning = false;
|
||||||
importing = false;
|
importing = false;
|
||||||
use_threads = true;
|
use_threads = true;
|
||||||
thread_sources = nullptr;
|
|
||||||
new_filesystem = nullptr;
|
new_filesystem = nullptr;
|
||||||
|
|
||||||
abort_scan = false;
|
abort_scan = false;
|
||||||
|
|
|
@ -127,7 +127,7 @@ class EditorFileSystem : public Node {
|
||||||
};
|
};
|
||||||
|
|
||||||
bool use_threads;
|
bool use_threads;
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
static void _thread_func(void *_userdata);
|
static void _thread_func(void *_userdata);
|
||||||
|
|
||||||
EditorFileSystemDirectory *new_filesystem;
|
EditorFileSystemDirectory *new_filesystem;
|
||||||
|
@ -189,7 +189,7 @@ class EditorFileSystem : public Node {
|
||||||
|
|
||||||
void _scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess *da, const ScanProgress &p_progress);
|
void _scan_new_dir(EditorFileSystemDirectory *p_dir, DirAccess *da, const ScanProgress &p_progress);
|
||||||
|
|
||||||
Thread *thread_sources;
|
Thread thread_sources;
|
||||||
bool scanning_changes;
|
bool scanning_changes;
|
||||||
bool scanning_changes_done;
|
bool scanning_changes_done;
|
||||||
|
|
||||||
|
|
|
@ -5485,9 +5485,7 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p
|
||||||
|
|
||||||
int prev_len = 0;
|
int prev_len = 0;
|
||||||
|
|
||||||
eta.execute_output_thread = Thread::create(_execute_thread, &eta);
|
eta.execute_output_thread.start(_execute_thread, &eta);
|
||||||
|
|
||||||
ERR_FAIL_COND_V(!eta.execute_output_thread, 0);
|
|
||||||
|
|
||||||
while (!eta.done) {
|
while (!eta.done) {
|
||||||
{
|
{
|
||||||
|
@ -5502,8 +5500,7 @@ int EditorNode::execute_and_show_output(const String &p_title, const String &p_p
|
||||||
OS::get_singleton()->delay_usec(1000);
|
OS::get_singleton()->delay_usec(1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
Thread::wait_to_finish(eta.execute_output_thread);
|
eta.execute_output_thread.wait_to_finish();
|
||||||
memdelete(eta.execute_output_thread);
|
|
||||||
execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode));
|
execute_outputs->add_text("\nExit Code: " + itos(eta.exitcode));
|
||||||
|
|
||||||
if (p_close_on_errors && eta.exitcode != 0) {
|
if (p_close_on_errors && eta.exitcode != 0) {
|
||||||
|
|
|
@ -108,7 +108,7 @@ public:
|
||||||
String path;
|
String path;
|
||||||
List<String> args;
|
List<String> args;
|
||||||
String output;
|
String output;
|
||||||
Thread *execute_output_thread = nullptr;
|
Thread execute_output_thread;
|
||||||
Mutex execute_output_mutex;
|
Mutex execute_output_mutex;
|
||||||
int exitcode = 0;
|
int exitcode = 0;
|
||||||
volatile bool done = false;
|
volatile bool done = false;
|
||||||
|
|
|
@ -424,26 +424,23 @@ void EditorResourcePreview::check_for_invalidation(const String &p_path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorResourcePreview::start() {
|
void EditorResourcePreview::start() {
|
||||||
ERR_FAIL_COND_MSG(thread, "Thread already started.");
|
ERR_FAIL_COND_MSG(thread.is_started(), "Thread already started.");
|
||||||
thread = Thread::create(_thread_func, this);
|
thread.start(_thread_func, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditorResourcePreview::stop() {
|
void EditorResourcePreview::stop() {
|
||||||
if (thread) {
|
if (thread.is_started()) {
|
||||||
exit = true;
|
exit = true;
|
||||||
preview_sem.post();
|
preview_sem.post();
|
||||||
while (!exited) {
|
while (!exited) {
|
||||||
OS::get_singleton()->delay_usec(10000);
|
OS::get_singleton()->delay_usec(10000);
|
||||||
RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
|
RenderingServer::get_singleton()->sync(); //sync pending stuff, as thread may be blocked on visual server
|
||||||
}
|
}
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorResourcePreview::EditorResourcePreview() {
|
EditorResourcePreview::EditorResourcePreview() {
|
||||||
thread = nullptr;
|
|
||||||
singleton = this;
|
singleton = this;
|
||||||
order = 0;
|
order = 0;
|
||||||
exit = false;
|
exit = false;
|
||||||
|
|
|
@ -70,7 +70,7 @@ class EditorResourcePreview : public Node {
|
||||||
|
|
||||||
Mutex preview_mutex;
|
Mutex preview_mutex;
|
||||||
Semaphore preview_sem;
|
Semaphore preview_sem;
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
volatile bool exit;
|
volatile bool exit;
|
||||||
volatile bool exited;
|
volatile bool exited;
|
||||||
|
|
||||||
|
|
|
@ -43,7 +43,7 @@ void EditorFileServer::_close_client(ClientData *cd) {
|
||||||
cd->connection->disconnect_from_host();
|
cd->connection->disconnect_from_host();
|
||||||
{
|
{
|
||||||
MutexLock lock(cd->efs->wait_mutex);
|
MutexLock lock(cd->efs->wait_mutex);
|
||||||
cd->efs->to_wait.insert(cd->thread);
|
cd->efs->to_wait.insert(&cd->thread);
|
||||||
}
|
}
|
||||||
while (cd->files.size()) {
|
while (cd->files.size()) {
|
||||||
memdelete(cd->files.front()->get());
|
memdelete(cd->files.front()->get());
|
||||||
|
@ -278,7 +278,7 @@ void EditorFileServer::_thread_start(void *s) {
|
||||||
cd->connection = self->server->take_connection();
|
cd->connection = self->server->take_connection();
|
||||||
cd->efs = self;
|
cd->efs = self;
|
||||||
cd->quit = false;
|
cd->quit = false;
|
||||||
cd->thread = Thread::create(_subthread_start, cd);
|
cd->thread.start(_subthread_start, cd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -287,8 +287,7 @@ void EditorFileServer::_thread_start(void *s) {
|
||||||
Thread *w = self->to_wait.front()->get();
|
Thread *w = self->to_wait.front()->get();
|
||||||
self->to_wait.erase(w);
|
self->to_wait.erase(w);
|
||||||
self->wait_mutex.unlock();
|
self->wait_mutex.unlock();
|
||||||
Thread::wait_to_finish(w);
|
w->wait_to_finish();
|
||||||
memdelete(w);
|
|
||||||
self->wait_mutex.lock();
|
self->wait_mutex.lock();
|
||||||
}
|
}
|
||||||
self->wait_mutex.unlock();
|
self->wait_mutex.unlock();
|
||||||
|
@ -317,7 +316,7 @@ EditorFileServer::EditorFileServer() {
|
||||||
quit = false;
|
quit = false;
|
||||||
active = false;
|
active = false;
|
||||||
cmd = CMD_NONE;
|
cmd = CMD_NONE;
|
||||||
thread = Thread::create(_thread_start, this);
|
thread.start(_thread_start, this);
|
||||||
|
|
||||||
EDITOR_DEF("filesystem/file_server/port", 6010);
|
EDITOR_DEF("filesystem/file_server/port", 6010);
|
||||||
EDITOR_DEF("filesystem/file_server/password", "");
|
EDITOR_DEF("filesystem/file_server/password", "");
|
||||||
|
@ -325,6 +324,5 @@ EditorFileServer::EditorFileServer() {
|
||||||
|
|
||||||
EditorFileServer::~EditorFileServer() {
|
EditorFileServer::~EditorFileServer() {
|
||||||
quit = true;
|
quit = true;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,7 @@ class EditorFileServer : public Object {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ClientData {
|
struct ClientData {
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
Ref<StreamPeerTCP> connection;
|
Ref<StreamPeerTCP> connection;
|
||||||
Map<int, FileAccess *> files;
|
Map<int, FileAccess *> files;
|
||||||
EditorFileServer *efs = nullptr;
|
EditorFileServer *efs = nullptr;
|
||||||
|
@ -61,7 +61,7 @@ class EditorFileServer : public Object {
|
||||||
static void _subthread_start(void *s);
|
static void _subthread_start(void *s);
|
||||||
|
|
||||||
Mutex wait_mutex;
|
Mutex wait_mutex;
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
static void _thread_start(void *);
|
static void _thread_start(void *);
|
||||||
bool quit;
|
bool quit;
|
||||||
Command cmd;
|
Command cmd;
|
||||||
|
|
|
@ -512,8 +512,6 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
|
||||||
|
|
||||||
MAIN_PRINT("Main: Initialize Globals");
|
MAIN_PRINT("Main: Initialize Globals");
|
||||||
|
|
||||||
Thread::_main_thread_id = Thread::get_caller_id();
|
|
||||||
|
|
||||||
globals = memnew(ProjectSettings);
|
globals = memnew(ProjectSettings);
|
||||||
input_map = memnew(InputMap);
|
input_map = memnew(InputMap);
|
||||||
|
|
||||||
|
@ -1465,9 +1463,11 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
|
||||||
// Print engine name and version
|
// Print engine name and version
|
||||||
print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
|
print_line(String(VERSION_NAME) + " v" + get_full_version_string() + " - " + String(VERSION_WEBSITE));
|
||||||
|
|
||||||
|
#if !defined(NO_THREADS)
|
||||||
if (p_main_tid_override) {
|
if (p_main_tid_override) {
|
||||||
Thread::_main_thread_id = p_main_tid_override;
|
Thread::main_thread_id = p_main_tid_override;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Determine text driver */
|
/* Determine text driver */
|
||||||
|
|
||||||
|
|
|
@ -267,12 +267,13 @@ void image_compress_cvtt(Image *p_image, float p_lossy_quality, Image::UsedChann
|
||||||
job_queue.num_tasks = static_cast<uint32_t>(tasks.size());
|
job_queue.num_tasks = static_cast<uint32_t>(tasks.size());
|
||||||
|
|
||||||
for (int i = 0; i < num_job_threads; i++) {
|
for (int i = 0; i < num_job_threads; i++) {
|
||||||
threads_wb[i] = Thread::create(_digest_job_queue, &job_queue);
|
threads_wb[i] = memnew(Thread);
|
||||||
|
threads_wb[i]->start(_digest_job_queue, &job_queue);
|
||||||
}
|
}
|
||||||
_digest_job_queue(&job_queue);
|
_digest_job_queue(&job_queue);
|
||||||
|
|
||||||
for (int i = 0; i < num_job_threads; i++) {
|
for (int i = 0; i < num_job_threads; i++) {
|
||||||
Thread::wait_to_finish(threads_wb[i]);
|
threads_wb[i]->wait_to_finish();
|
||||||
memdelete(threads_wb[i]);
|
memdelete(threads_wb[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -48,7 +48,7 @@ extern "C" {
|
||||||
|
|
||||||
JNIEnv *GDAPI godot_android_get_env() {
|
JNIEnv *GDAPI godot_android_get_env() {
|
||||||
#ifdef __ANDROID__
|
#ifdef __ANDROID__
|
||||||
return ThreadAndroid::get_env();
|
return get_jni_env();
|
||||||
#else
|
#else
|
||||||
return nullptr;
|
return nullptr;
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -36,7 +36,6 @@
|
||||||
#include "editor/editor_node.h"
|
#include "editor/editor_node.h"
|
||||||
|
|
||||||
GDScriptLanguageServer::GDScriptLanguageServer() {
|
GDScriptLanguageServer::GDScriptLanguageServer() {
|
||||||
thread = nullptr;
|
|
||||||
thread_running = false;
|
thread_running = false;
|
||||||
started = false;
|
started = false;
|
||||||
|
|
||||||
|
@ -87,9 +86,8 @@ void GDScriptLanguageServer::start() {
|
||||||
if (protocol.start(port, IP_Address("127.0.0.1")) == OK) {
|
if (protocol.start(port, IP_Address("127.0.0.1")) == OK) {
|
||||||
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
|
EditorNode::get_log()->add_message("--- GDScript language server started ---", EditorLog::MSG_TYPE_EDITOR);
|
||||||
if (use_thread) {
|
if (use_thread) {
|
||||||
ERR_FAIL_COND(thread != nullptr);
|
|
||||||
thread_running = true;
|
thread_running = true;
|
||||||
thread = Thread::create(GDScriptLanguageServer::thread_main, this);
|
thread.start(GDScriptLanguageServer::thread_main, this);
|
||||||
}
|
}
|
||||||
set_process_internal(!use_thread);
|
set_process_internal(!use_thread);
|
||||||
started = true;
|
started = true;
|
||||||
|
@ -98,11 +96,9 @@ void GDScriptLanguageServer::start() {
|
||||||
|
|
||||||
void GDScriptLanguageServer::stop() {
|
void GDScriptLanguageServer::stop() {
|
||||||
if (use_thread) {
|
if (use_thread) {
|
||||||
ERR_FAIL_COND(nullptr == thread);
|
ERR_FAIL_COND(!thread.is_started());
|
||||||
thread_running = false;
|
thread_running = false;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
}
|
||||||
protocol.stop();
|
protocol.stop();
|
||||||
started = false;
|
started = false;
|
||||||
|
|
|
@ -40,7 +40,7 @@ class GDScriptLanguageServer : public EditorPlugin {
|
||||||
|
|
||||||
GDScriptLanguageProtocol protocol;
|
GDScriptLanguageProtocol protocol;
|
||||||
|
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
bool thread_running;
|
bool thread_running;
|
||||||
bool started;
|
bool started;
|
||||||
bool use_thread;
|
bool use_thread;
|
||||||
|
|
|
@ -109,7 +109,7 @@ bool jni_exception_check(JNIEnv *p_env) {
|
||||||
String app_native_lib_dir_cache;
|
String app_native_lib_dir_cache;
|
||||||
|
|
||||||
String determine_app_native_lib_dir() {
|
String determine_app_native_lib_dir() {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
|
ScopedLocalRef<jclass> activityThreadClass(env, env->FindClass("android/app/ActivityThread"));
|
||||||
jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
|
jmethodID currentActivityThread = env->GetStaticMethodID(activityThreadClass, "currentActivityThread", "()Landroid/app/ActivityThread;");
|
||||||
|
@ -253,7 +253,7 @@ int32_t get_build_version_sdk_int() {
|
||||||
// android.os.Build.VERSION.SDK_INT
|
// android.os.Build.VERSION.SDK_INT
|
||||||
|
|
||||||
if (build_version_sdk_int == 0) {
|
if (build_version_sdk_int == 0) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
jclass versionClass = env->FindClass("android/os/Build$VERSION");
|
jclass versionClass = env->FindClass("android/os/Build$VERSION");
|
||||||
ERR_FAIL_NULL_V(versionClass, 0);
|
ERR_FAIL_NULL_V(versionClass, 0);
|
||||||
|
@ -281,7 +281,7 @@ MonoBoolean _gd_mono_init_cert_store() {
|
||||||
// return false;
|
// return false;
|
||||||
// }
|
// }
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
|
ScopedLocalRef<jclass> keyStoreClass(env, env->FindClass("java/security/KeyStore"));
|
||||||
|
|
||||||
|
@ -322,7 +322,7 @@ MonoArray *_gd_mono_android_cert_store_lookup(MonoString *p_alias) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
|
ScopedLocalRef<jstring> js_alias(env, env->NewStringUTF(alias_utf8));
|
||||||
mono_free(alias_utf8);
|
mono_free(alias_utf8);
|
||||||
|
@ -380,7 +380,7 @@ void cleanup() {
|
||||||
if (godot_dl_handle)
|
if (godot_dl_handle)
|
||||||
gd_mono_android_dlclose(godot_dl_handle, nullptr);
|
gd_mono_android_dlclose(godot_dl_handle, nullptr);
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
if (certStore) {
|
if (certStore) {
|
||||||
env->DeleteGlobalRef(certStore);
|
env->DeleteGlobalRef(certStore);
|
||||||
|
@ -437,7 +437,7 @@ GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_up_state(const char
|
||||||
|
|
||||||
*r_is_up = 0;
|
*r_is_up = 0;
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
|
jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
|
||||||
ERR_FAIL_NULL_V(networkInterfaceClass, 0);
|
ERR_FAIL_NULL_V(networkInterfaceClass, 0);
|
||||||
|
@ -469,7 +469,7 @@ GD_PINVOKE_EXPORT mono_bool _monodroid_get_network_interface_supports_multicast(
|
||||||
|
|
||||||
*r_supports_multicast = 0;
|
*r_supports_multicast = 0;
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
|
jclass networkInterfaceClass = env->FindClass("java/net/NetworkInterface");
|
||||||
ERR_FAIL_NULL_V(networkInterfaceClass, 0);
|
ERR_FAIL_NULL_V(networkInterfaceClass, 0);
|
||||||
|
@ -507,7 +507,7 @@ static void interop_get_active_network_dns_servers(char **r_dns_servers, int *dn
|
||||||
CRASH_COND(get_build_version_sdk_int() < 23);
|
CRASH_COND(get_build_version_sdk_int() < 23);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
|
GodotJavaWrapper *godot_java = ((OS_Android *)OS::get_singleton())->get_godot_java();
|
||||||
jobject activity = godot_java->get_activity();
|
jobject activity = godot_java->get_activity();
|
||||||
|
@ -648,7 +648,7 @@ GD_PINVOKE_EXPORT const char *_monodroid_timezone_get_default_id() {
|
||||||
//
|
//
|
||||||
// TimeZone.getDefault().getID()
|
// TimeZone.getDefault().getID()
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
|
ScopedLocalRef<jclass> timeZoneClass(env, env->FindClass("java/util/TimeZone"));
|
||||||
ERR_FAIL_NULL_V(timeZoneClass, nullptr);
|
ERR_FAIL_NULL_V(timeZoneClass, nullptr);
|
||||||
|
|
|
@ -34,7 +34,6 @@
|
||||||
|
|
||||||
NoiseTexture::NoiseTexture() {
|
NoiseTexture::NoiseTexture() {
|
||||||
update_queued = false;
|
update_queued = false;
|
||||||
noise_thread = nullptr;
|
|
||||||
regen_queued = false;
|
regen_queued = false;
|
||||||
first_time = true;
|
first_time = true;
|
||||||
|
|
||||||
|
@ -52,10 +51,7 @@ NoiseTexture::~NoiseTexture() {
|
||||||
if (texture.is_valid()) {
|
if (texture.is_valid()) {
|
||||||
RS::get_singleton()->free(texture);
|
RS::get_singleton()->free(texture);
|
||||||
}
|
}
|
||||||
if (noise_thread) {
|
noise_thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(noise_thread);
|
|
||||||
memdelete(noise_thread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NoiseTexture::_bind_methods() {
|
void NoiseTexture::_bind_methods() {
|
||||||
|
@ -109,11 +105,9 @@ void NoiseTexture::_set_texture_data(const Ref<Image> &p_image) {
|
||||||
|
|
||||||
void NoiseTexture::_thread_done(const Ref<Image> &p_image) {
|
void NoiseTexture::_thread_done(const Ref<Image> &p_image) {
|
||||||
_set_texture_data(p_image);
|
_set_texture_data(p_image);
|
||||||
Thread::wait_to_finish(noise_thread);
|
noise_thread.wait_to_finish();
|
||||||
memdelete(noise_thread);
|
|
||||||
noise_thread = nullptr;
|
|
||||||
if (regen_queued) {
|
if (regen_queued) {
|
||||||
noise_thread = Thread::create(_thread_function, this);
|
noise_thread.start(_thread_function, this);
|
||||||
regen_queued = false;
|
regen_queued = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -165,8 +159,8 @@ void NoiseTexture::_update_texture() {
|
||||||
use_thread = false;
|
use_thread = false;
|
||||||
#endif
|
#endif
|
||||||
if (use_thread) {
|
if (use_thread) {
|
||||||
if (!noise_thread) {
|
if (!noise_thread.is_started()) {
|
||||||
noise_thread = Thread::create(_thread_function, this);
|
noise_thread.start(_thread_function, this);
|
||||||
regen_queued = false;
|
regen_queued = false;
|
||||||
} else {
|
} else {
|
||||||
regen_queued = true;
|
regen_queued = true;
|
||||||
|
|
|
@ -45,7 +45,7 @@ class NoiseTexture : public Texture2D {
|
||||||
private:
|
private:
|
||||||
Ref<Image> data;
|
Ref<Image> data;
|
||||||
|
|
||||||
Thread *noise_thread;
|
Thread noise_thread;
|
||||||
|
|
||||||
bool first_time;
|
bool first_time;
|
||||||
bool update_queued;
|
bool update_queued;
|
||||||
|
|
|
@ -140,9 +140,7 @@ void VideoStreamPlaybackTheora::clear() {
|
||||||
#ifdef THEORA_USE_THREAD_STREAMING
|
#ifdef THEORA_USE_THREAD_STREAMING
|
||||||
thread_exit = true;
|
thread_exit = true;
|
||||||
thread_sem->post(); //just in case
|
thread_sem->post(); //just in case
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
ring_buffer.clear();
|
ring_buffer.clear();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -181,7 +179,7 @@ void VideoStreamPlaybackTheora::set_file(const String &p_file) {
|
||||||
int read = file->get_buffer(read_buffer.ptr(), to_read);
|
int read = file->get_buffer(read_buffer.ptr(), to_read);
|
||||||
ring_buffer.write(read_buffer.ptr(), read);
|
ring_buffer.write(read_buffer.ptr(), read);
|
||||||
|
|
||||||
thread = Thread::create(_streaming_thread, this);
|
thread.start(_streaming_thread, this);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -669,7 +667,6 @@ VideoStreamPlaybackTheora::VideoStreamPlaybackTheora() {
|
||||||
ring_buffer.resize(rb_power);
|
ring_buffer.resize(rb_power);
|
||||||
read_buffer.resize(RB_SIZE_KB * 1024);
|
read_buffer.resize(RB_SIZE_KB * 1024);
|
||||||
thread_sem = Semaphore::create();
|
thread_sem = Semaphore::create();
|
||||||
thread = nullptr;
|
|
||||||
thread_exit = false;
|
thread_exit = false;
|
||||||
thread_eof = false;
|
thread_eof = false;
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ class VideoStreamPlaybackTheora : public VideoStreamPlayback {
|
||||||
Vector<uint8_t> read_buffer;
|
Vector<uint8_t> read_buffer;
|
||||||
bool thread_eof;
|
bool thread_eof;
|
||||||
Semaphore *thread_sem;
|
Semaphore *thread_sem;
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
volatile bool thread_exit;
|
volatile bool thread_exit;
|
||||||
|
|
||||||
static void _streaming_thread(void *ud);
|
static void _streaming_thread(void *ud);
|
||||||
|
|
|
@ -83,7 +83,7 @@ public:
|
||||||
v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
|
v = (jvalue *)alloca(sizeof(jvalue) * p_argcount);
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
int res = env->PushLocalFrame(16);
|
int res = env->PushLocalFrame(16);
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ Error AudioDriverAndroid::init() {
|
||||||
|
|
||||||
// __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
|
// __android_log_print(ANDROID_LOG_VERBOSE, "SDL", "SDL audio: opening device");
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
int mix_rate = GLOBAL_GET("audio/mix_rate");
|
int mix_rate = GLOBAL_GET("audio/mix_rate");
|
||||||
|
|
||||||
int latency = GLOBAL_GET("audio/output_latency");
|
int latency = GLOBAL_GET("audio/output_latency");
|
||||||
|
@ -98,7 +98,7 @@ void AudioDriverAndroid::start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverAndroid::setup(jobject p_io) {
|
void AudioDriverAndroid::setup(jobject p_io) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
io = p_io;
|
io = p_io;
|
||||||
|
|
||||||
jclass c = env->GetObjectClass(io);
|
jclass c = env->GetObjectClass(io);
|
||||||
|
@ -162,7 +162,7 @@ void AudioDriverAndroid::unlock() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverAndroid::finish() {
|
void AudioDriverAndroid::finish() {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(io, _quit);
|
env->CallVoidMethod(io, _quit);
|
||||||
|
|
||||||
if (audioBuffer) {
|
if (audioBuffer) {
|
||||||
|
@ -175,7 +175,7 @@ void AudioDriverAndroid::finish() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverAndroid::set_pause(bool p_pause) {
|
void AudioDriverAndroid::set_pause(bool p_pause) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(io, _pause, p_pause);
|
env->CallVoidMethod(io, _pause, p_pause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ DirAccess *DirAccessJAndroid::create_fs() {
|
||||||
|
|
||||||
Error DirAccessJAndroid::list_dir_begin() {
|
Error DirAccessJAndroid::list_dir_begin() {
|
||||||
list_dir_end();
|
list_dir_end();
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
jstring js = env->NewStringUTF(current_dir.utf8().get_data());
|
jstring js = env->NewStringUTF(current_dir.utf8().get_data());
|
||||||
int res = env->CallIntMethod(io, _dir_open, js);
|
int res = env->CallIntMethod(io, _dir_open, js);
|
||||||
|
@ -62,7 +62,7 @@ Error DirAccessJAndroid::list_dir_begin() {
|
||||||
String DirAccessJAndroid::get_next() {
|
String DirAccessJAndroid::get_next() {
|
||||||
ERR_FAIL_COND_V(id == 0, "");
|
ERR_FAIL_COND_V(id == 0, "");
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring str = (jstring)env->CallObjectMethod(io, _dir_next, id);
|
jstring str = (jstring)env->CallObjectMethod(io, _dir_next, id);
|
||||||
if (!str)
|
if (!str)
|
||||||
return "";
|
return "";
|
||||||
|
@ -73,7 +73,7 @@ String DirAccessJAndroid::get_next() {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirAccessJAndroid::current_is_dir() const {
|
bool DirAccessJAndroid::current_is_dir() const {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
return env->CallBooleanMethod(io, _dir_is_dir, id);
|
return env->CallBooleanMethod(io, _dir_is_dir, id);
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ void DirAccessJAndroid::list_dir_end() {
|
||||||
if (id == 0)
|
if (id == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(io, _dir_close, id);
|
env->CallVoidMethod(io, _dir_close, id);
|
||||||
id = 0;
|
id = 0;
|
||||||
}
|
}
|
||||||
|
@ -100,7 +100,7 @@ String DirAccessJAndroid::get_drive(int p_drive) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Error DirAccessJAndroid::change_dir(String p_dir) {
|
Error DirAccessJAndroid::change_dir(String p_dir) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == ""))
|
if (p_dir == "" || p_dir == "." || (p_dir == ".." && current_dir == ""))
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -154,7 +154,7 @@ bool DirAccessJAndroid::file_exists(String p_file) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DirAccessJAndroid::dir_exists(String p_dir) {
|
bool DirAccessJAndroid::dir_exists(String p_dir) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
String sd;
|
String sd;
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ size_t DirAccessJAndroid::get_space_left() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void DirAccessJAndroid::setup(jobject p_io) {
|
void DirAccessJAndroid::setup(jobject p_io) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
io = p_io;
|
io = p_io;
|
||||||
|
|
||||||
jclass c = env->GetObjectClass(io);
|
jclass c = env->GetObjectClass(io);
|
||||||
|
|
|
@ -269,7 +269,7 @@ class EditorExportPlatformAndroid : public EditorExportPlatform {
|
||||||
Vector<Device> devices;
|
Vector<Device> devices;
|
||||||
volatile bool devices_changed;
|
volatile bool devices_changed;
|
||||||
Mutex device_lock;
|
Mutex device_lock;
|
||||||
Thread *check_for_changes_thread;
|
Thread check_for_changes_thread;
|
||||||
volatile bool quit_request;
|
volatile bool quit_request;
|
||||||
|
|
||||||
static void _check_for_changes_poll_thread(void *ud) {
|
static void _check_for_changes_poll_thread(void *ud) {
|
||||||
|
@ -2848,13 +2848,12 @@ public:
|
||||||
devices_changed = true;
|
devices_changed = true;
|
||||||
plugins_changed = true;
|
plugins_changed = true;
|
||||||
quit_request = false;
|
quit_request = false;
|
||||||
check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
|
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
~EditorExportPlatformAndroid() {
|
~EditorExportPlatformAndroid() {
|
||||||
quit_request = true;
|
quit_request = true;
|
||||||
Thread::wait_to_finish(check_for_changes_thread);
|
check_for_changes_thread.wait_to_finish();
|
||||||
memdelete(check_for_changes_thread);
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
|
||||||
if (!M)
|
if (!M)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
MethodInfo *method = nullptr;
|
MethodInfo *method = nullptr;
|
||||||
for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
|
for (List<MethodInfo>::Element *E = M->get().front(); E; E = E->next()) {
|
||||||
|
@ -964,7 +964,7 @@ Ref<JavaClass> JavaClassWrapper::wrap(const String &p_class) {
|
||||||
if (class_cache.has(p_class))
|
if (class_cache.has(p_class))
|
||||||
return class_cache[p_class];
|
return class_cache[p_class];
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
jclass bclass = env->FindClass(p_class.utf8().get_data());
|
jclass bclass = env->FindClass(p_class.utf8().get_data());
|
||||||
ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
|
ERR_FAIL_COND_V(!bclass, Ref<JavaClass>());
|
||||||
|
@ -1148,7 +1148,7 @@ JavaClassWrapper *JavaClassWrapper::singleton = nullptr;
|
||||||
JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
|
JavaClassWrapper::JavaClassWrapper(jobject p_activity) {
|
||||||
singleton = this;
|
singleton = this;
|
||||||
|
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
jclass activityClass = env->FindClass("android/app/Activity");
|
jclass activityClass = env->FindClass("android/app/Activity");
|
||||||
jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
jmethodID getClassLoader = env->GetMethodID(activityClass, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
|
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
|
||||||
// we can't cache it.
|
// we can't cache it.
|
||||||
// For GodotIO we call all access methods from our thread and we thus get a valid JNIEnv
|
// For GodotIO we call all access methods from our thread and we thus get a valid JNIEnv
|
||||||
// from ThreadAndroid.
|
// from get_jni_env().
|
||||||
|
|
||||||
GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance) {
|
GodotIOJavaWrapper::GodotIOJavaWrapper(JNIEnv *p_env, jobject p_godot_io_instance) {
|
||||||
godot_io_instance = p_env->NewGlobalRef(p_godot_io_instance);
|
godot_io_instance = p_env->NewGlobalRef(p_godot_io_instance);
|
||||||
|
@ -72,7 +72,7 @@ jobject GodotIOJavaWrapper::get_instance() {
|
||||||
|
|
||||||
Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
|
Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
|
||||||
if (_open_URI) {
|
if (_open_URI) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
|
jstring jStr = env->NewStringUTF(p_uri.utf8().get_data());
|
||||||
return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
|
return env->CallIntMethod(godot_io_instance, _open_URI, jStr) ? ERR_CANT_OPEN : OK;
|
||||||
} else {
|
} else {
|
||||||
|
@ -82,7 +82,7 @@ Error GodotIOJavaWrapper::open_uri(const String &p_uri) {
|
||||||
|
|
||||||
String GodotIOJavaWrapper::get_user_data_dir() {
|
String GodotIOJavaWrapper::get_user_data_dir() {
|
||||||
if (_get_data_dir) {
|
if (_get_data_dir) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
|
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_data_dir);
|
||||||
return jstring_to_string(s, env);
|
return jstring_to_string(s, env);
|
||||||
} else {
|
} else {
|
||||||
|
@ -92,7 +92,7 @@ String GodotIOJavaWrapper::get_user_data_dir() {
|
||||||
|
|
||||||
String GodotIOJavaWrapper::get_locale() {
|
String GodotIOJavaWrapper::get_locale() {
|
||||||
if (_get_locale) {
|
if (_get_locale) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
|
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_locale);
|
||||||
return jstring_to_string(s, env);
|
return jstring_to_string(s, env);
|
||||||
} else {
|
} else {
|
||||||
|
@ -102,7 +102,7 @@ String GodotIOJavaWrapper::get_locale() {
|
||||||
|
|
||||||
String GodotIOJavaWrapper::get_model() {
|
String GodotIOJavaWrapper::get_model() {
|
||||||
if (_get_model) {
|
if (_get_model) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
|
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_model);
|
||||||
return jstring_to_string(s, env);
|
return jstring_to_string(s, env);
|
||||||
} else {
|
} else {
|
||||||
|
@ -112,7 +112,7 @@ String GodotIOJavaWrapper::get_model() {
|
||||||
|
|
||||||
int GodotIOJavaWrapper::get_screen_dpi() {
|
int GodotIOJavaWrapper::get_screen_dpi() {
|
||||||
if (_get_screen_DPI) {
|
if (_get_screen_DPI) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
|
return env->CallIntMethod(godot_io_instance, _get_screen_DPI);
|
||||||
} else {
|
} else {
|
||||||
return 160;
|
return 160;
|
||||||
|
@ -121,7 +121,7 @@ int GodotIOJavaWrapper::get_screen_dpi() {
|
||||||
|
|
||||||
void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
|
void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
|
||||||
if (_screen_get_usable_rect) {
|
if (_screen_get_usable_rect) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _screen_get_usable_rect);
|
jintArray returnArray = (jintArray)env->CallObjectMethod(godot_io_instance, _screen_get_usable_rect);
|
||||||
ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
|
ERR_FAIL_COND(env->GetArrayLength(returnArray) != 4);
|
||||||
jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
|
jint *arrayBody = env->GetIntArrayElements(returnArray, JNI_FALSE);
|
||||||
|
@ -134,7 +134,7 @@ void GodotIOJavaWrapper::screen_get_usable_rect(int (&p_rect_xywh)[4]) {
|
||||||
|
|
||||||
String GodotIOJavaWrapper::get_unique_id() {
|
String GodotIOJavaWrapper::get_unique_id() {
|
||||||
if (_get_unique_id) {
|
if (_get_unique_id) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
|
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_unique_id);
|
||||||
return jstring_to_string(s, env);
|
return jstring_to_string(s, env);
|
||||||
} else {
|
} else {
|
||||||
|
@ -148,7 +148,7 @@ bool GodotIOJavaWrapper::has_vk() {
|
||||||
|
|
||||||
void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
|
void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int p_max_input_length, int p_cursor_start, int p_cursor_end) {
|
||||||
if (_show_keyboard) {
|
if (_show_keyboard) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
|
jstring jStr = env->NewStringUTF(p_existing.utf8().get_data());
|
||||||
env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
|
env->CallVoidMethod(godot_io_instance, _show_keyboard, jStr, p_multiline, p_max_input_length, p_cursor_start, p_cursor_end);
|
||||||
}
|
}
|
||||||
|
@ -156,21 +156,21 @@ void GodotIOJavaWrapper::show_vk(const String &p_existing, bool p_multiline, int
|
||||||
|
|
||||||
void GodotIOJavaWrapper::hide_vk() {
|
void GodotIOJavaWrapper::hide_vk() {
|
||||||
if (_hide_keyboard) {
|
if (_hide_keyboard) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(godot_io_instance, _hide_keyboard);
|
env->CallVoidMethod(godot_io_instance, _hide_keyboard);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
|
void GodotIOJavaWrapper::set_screen_orientation(int p_orient) {
|
||||||
if (_set_screen_orientation) {
|
if (_set_screen_orientation) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
|
env->CallVoidMethod(godot_io_instance, _set_screen_orientation, p_orient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int GodotIOJavaWrapper::get_screen_orientation() {
|
int GodotIOJavaWrapper::get_screen_orientation() {
|
||||||
if (_get_screen_orientation) {
|
if (_get_screen_orientation) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
|
return env->CallIntMethod(godot_io_instance, _get_screen_orientation);
|
||||||
} else {
|
} else {
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -179,7 +179,7 @@ int GodotIOJavaWrapper::get_screen_orientation() {
|
||||||
|
|
||||||
String GodotIOJavaWrapper::get_system_dir(int p_dir) {
|
String GodotIOJavaWrapper::get_system_dir(int p_dir) {
|
||||||
if (_get_system_dir) {
|
if (_get_system_dir) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
|
jstring s = (jstring)env->CallObjectMethod(godot_io_instance, _get_system_dir, p_dir);
|
||||||
return jstring_to_string(s, env);
|
return jstring_to_string(s, env);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -87,7 +87,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_initialize(JNIEnv *en
|
||||||
godot_java = new GodotJavaWrapper(env, activity, godot_instance);
|
godot_java = new GodotJavaWrapper(env, activity, godot_instance);
|
||||||
godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env));
|
godot_io_java = new GodotIOJavaWrapper(env, godot_java->get_member_object("io", "Lorg/godotengine/godot/GodotIO;", env));
|
||||||
|
|
||||||
ThreadAndroid::make_default(jvm);
|
init_thread_jandroid(jvm, env);
|
||||||
|
|
||||||
jobject amgr = env->NewGlobalRef(p_asset_manager);
|
jobject amgr = env->NewGlobalRef(p_asset_manager);
|
||||||
|
|
||||||
|
@ -119,7 +119,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_ondestroy(JNIEnv *env
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_setup(JNIEnv *env, jclass clazz, jobjectArray p_cmdline) {
|
||||||
ThreadAndroid::setup_thread();
|
setup_android_thread();
|
||||||
|
|
||||||
const char **cmdline = nullptr;
|
const char **cmdline = nullptr;
|
||||||
jstring *j_cmdline = nullptr;
|
jstring *j_cmdline = nullptr;
|
||||||
|
@ -206,7 +206,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_step(JNIEnv *env, jcl
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (step == 0) {
|
if (step == 0) {
|
||||||
// Since Godot is initialized on the UI thread, _main_thread_id was set to that thread's id,
|
// Since Godot is initialized on the UI thread, main_thread_id was set to that thread's id,
|
||||||
// but for Godot purposes, the main thread is the one running the game loop
|
// but for Godot purposes, the main thread is the one running the game loop
|
||||||
Main::setup2(Thread::get_caller_id());
|
Main::setup2(Thread::get_caller_id());
|
||||||
++step;
|
++step;
|
||||||
|
@ -382,7 +382,7 @@ JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_focusout(JNIEnv *env,
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) {
|
JNIEXPORT void JNICALL Java_org_godotengine_godot_GodotLib_audio(JNIEnv *env, jclass clazz) {
|
||||||
ThreadAndroid::setup_thread();
|
setup_android_thread();
|
||||||
AudioDriverAndroid::thread_func(env);
|
AudioDriverAndroid::thread_func(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
#include "thread_jandroid.h"
|
#include "thread_jandroid.h"
|
||||||
|
|
||||||
GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
|
GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
_godot_view = env->NewGlobalRef(godot_view);
|
_godot_view = env->NewGlobalRef(godot_view);
|
||||||
|
|
||||||
|
@ -47,20 +47,20 @@ GodotJavaViewWrapper::GodotJavaViewWrapper(jobject godot_view) {
|
||||||
|
|
||||||
void GodotJavaViewWrapper::request_pointer_capture() {
|
void GodotJavaViewWrapper::request_pointer_capture() {
|
||||||
if (_request_pointer_capture != 0) {
|
if (_request_pointer_capture != 0) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(_godot_view, _request_pointer_capture);
|
env->CallVoidMethod(_godot_view, _request_pointer_capture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GodotJavaViewWrapper::release_pointer_capture() {
|
void GodotJavaViewWrapper::release_pointer_capture() {
|
||||||
if (_request_pointer_capture != 0) {
|
if (_request_pointer_capture != 0) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(_godot_view, _release_pointer_capture);
|
env->CallVoidMethod(_godot_view, _release_pointer_capture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GodotJavaViewWrapper::~GodotJavaViewWrapper() {
|
GodotJavaViewWrapper::~GodotJavaViewWrapper() {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->DeleteGlobalRef(_godot_view);
|
env->DeleteGlobalRef(_godot_view);
|
||||||
env->DeleteGlobalRef(_cls);
|
env->DeleteGlobalRef(_cls);
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@
|
||||||
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
|
// JNIEnv is only valid within the thread it belongs to, in a multi threading environment
|
||||||
// we can't cache it.
|
// we can't cache it.
|
||||||
// For Godot we call most access methods from our thread and we thus get a valid JNIEnv
|
// For Godot we call most access methods from our thread and we thus get a valid JNIEnv
|
||||||
// from ThreadAndroid. For one or two we expect to pass the environment
|
// from get_jni_env(). For one or two we expect to pass the environment
|
||||||
|
|
||||||
// TODO we could probably create a base class for this...
|
// TODO we could probably create a base class for this...
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ jobject GodotJavaWrapper::get_activity() {
|
||||||
jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
|
jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_class, JNIEnv *p_env) {
|
||||||
if (godot_class) {
|
if (godot_class) {
|
||||||
if (p_env == nullptr)
|
if (p_env == nullptr)
|
||||||
p_env = ThreadAndroid::get_env();
|
p_env = get_jni_env();
|
||||||
|
|
||||||
jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
|
jfieldID fid = p_env->GetStaticFieldID(godot_class, p_name, p_class);
|
||||||
return p_env->GetStaticObjectField(godot_class, fid);
|
return p_env->GetStaticObjectField(godot_class, fid);
|
||||||
|
@ -102,7 +102,7 @@ jobject GodotJavaWrapper::get_member_object(const char *p_name, const char *p_cl
|
||||||
|
|
||||||
jobject GodotJavaWrapper::get_class_loader() {
|
jobject GodotJavaWrapper::get_class_loader() {
|
||||||
if (_get_class_loader) {
|
if (_get_class_loader) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
return env->CallObjectMethod(activity, _get_class_loader);
|
return env->CallObjectMethod(activity, _get_class_loader);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -113,7 +113,7 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
|
||||||
if (_godot_view != nullptr) {
|
if (_godot_view != nullptr) {
|
||||||
return _godot_view;
|
return _godot_view;
|
||||||
}
|
}
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
|
jmethodID godot_view_getter = env->GetMethodID(godot_class, "getRenderView", "()Lorg/godotengine/godot/GodotRenderView;");
|
||||||
_godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
|
_godot_view = new GodotJavaViewWrapper(env->CallObjectMethod(godot_instance, godot_view_getter));
|
||||||
return _godot_view;
|
return _godot_view;
|
||||||
|
@ -122,7 +122,7 @@ GodotJavaViewWrapper *GodotJavaWrapper::get_godot_view() {
|
||||||
void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
|
void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
|
||||||
if (_on_video_init)
|
if (_on_video_init)
|
||||||
if (p_env == nullptr)
|
if (p_env == nullptr)
|
||||||
p_env = ThreadAndroid::get_env();
|
p_env = get_jni_env();
|
||||||
|
|
||||||
p_env->CallVoidMethod(godot_instance, _on_video_init);
|
p_env->CallVoidMethod(godot_instance, _on_video_init);
|
||||||
}
|
}
|
||||||
|
@ -130,7 +130,7 @@ void GodotJavaWrapper::on_video_init(JNIEnv *p_env) {
|
||||||
void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
|
void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
|
||||||
if (_on_godot_main_loop_started) {
|
if (_on_godot_main_loop_started) {
|
||||||
if (p_env == nullptr) {
|
if (p_env == nullptr) {
|
||||||
p_env = ThreadAndroid::get_env();
|
p_env = get_jni_env();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
|
p_env->CallVoidMethod(godot_instance, _on_godot_main_loop_started);
|
||||||
|
@ -139,7 +139,7 @@ void GodotJavaWrapper::on_godot_main_loop_started(JNIEnv *p_env) {
|
||||||
void GodotJavaWrapper::restart(JNIEnv *p_env) {
|
void GodotJavaWrapper::restart(JNIEnv *p_env) {
|
||||||
if (_restart)
|
if (_restart)
|
||||||
if (p_env == nullptr)
|
if (p_env == nullptr)
|
||||||
p_env = ThreadAndroid::get_env();
|
p_env = get_jni_env();
|
||||||
|
|
||||||
p_env->CallVoidMethod(godot_instance, _restart);
|
p_env->CallVoidMethod(godot_instance, _restart);
|
||||||
}
|
}
|
||||||
|
@ -147,21 +147,21 @@ void GodotJavaWrapper::restart(JNIEnv *p_env) {
|
||||||
void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
|
void GodotJavaWrapper::force_quit(JNIEnv *p_env) {
|
||||||
if (_finish)
|
if (_finish)
|
||||||
if (p_env == nullptr)
|
if (p_env == nullptr)
|
||||||
p_env = ThreadAndroid::get_env();
|
p_env = get_jni_env();
|
||||||
|
|
||||||
p_env->CallVoidMethod(godot_instance, _finish);
|
p_env->CallVoidMethod(godot_instance, _finish);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
|
void GodotJavaWrapper::set_keep_screen_on(bool p_enabled) {
|
||||||
if (_set_keep_screen_on) {
|
if (_set_keep_screen_on) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
|
env->CallVoidMethod(godot_instance, _set_keep_screen_on, p_enabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
|
void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
|
||||||
if (_alert) {
|
if (_alert) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
|
jstring jStrMessage = env->NewStringUTF(p_message.utf8().get_data());
|
||||||
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
|
jstring jStrTitle = env->NewStringUTF(p_title.utf8().get_data());
|
||||||
env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
|
env->CallVoidMethod(godot_instance, _alert, jStrMessage, jStrTitle);
|
||||||
|
@ -169,7 +169,7 @@ void GodotJavaWrapper::alert(const String &p_message, const String &p_title) {
|
||||||
}
|
}
|
||||||
|
|
||||||
int GodotJavaWrapper::get_gles_version_code() {
|
int GodotJavaWrapper::get_gles_version_code() {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
if (_get_GLES_version_code) {
|
if (_get_GLES_version_code) {
|
||||||
return env->CallIntMethod(godot_instance, _get_GLES_version_code);
|
return env->CallIntMethod(godot_instance, _get_GLES_version_code);
|
||||||
}
|
}
|
||||||
|
@ -183,7 +183,7 @@ bool GodotJavaWrapper::has_get_clipboard() {
|
||||||
|
|
||||||
String GodotJavaWrapper::get_clipboard() {
|
String GodotJavaWrapper::get_clipboard() {
|
||||||
if (_get_clipboard) {
|
if (_get_clipboard) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
|
jstring s = (jstring)env->CallObjectMethod(godot_instance, _get_clipboard);
|
||||||
return jstring_to_string(s, env);
|
return jstring_to_string(s, env);
|
||||||
} else {
|
} else {
|
||||||
|
@ -193,7 +193,7 @@ String GodotJavaWrapper::get_clipboard() {
|
||||||
|
|
||||||
String GodotJavaWrapper::get_input_fallback_mapping() {
|
String GodotJavaWrapper::get_input_fallback_mapping() {
|
||||||
if (_get_input_fallback_mapping) {
|
if (_get_input_fallback_mapping) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
|
jstring fallback_mapping = (jstring)env->CallObjectMethod(godot_instance, _get_input_fallback_mapping);
|
||||||
return jstring_to_string(fallback_mapping, env);
|
return jstring_to_string(fallback_mapping, env);
|
||||||
} else {
|
} else {
|
||||||
|
@ -207,7 +207,7 @@ bool GodotJavaWrapper::has_set_clipboard() {
|
||||||
|
|
||||||
void GodotJavaWrapper::set_clipboard(const String &p_text) {
|
void GodotJavaWrapper::set_clipboard(const String &p_text) {
|
||||||
if (_set_clipboard) {
|
if (_set_clipboard) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
|
jstring jStr = env->NewStringUTF(p_text.utf8().get_data());
|
||||||
env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
|
env->CallVoidMethod(godot_instance, _set_clipboard, jStr);
|
||||||
}
|
}
|
||||||
|
@ -215,7 +215,7 @@ void GodotJavaWrapper::set_clipboard(const String &p_text) {
|
||||||
|
|
||||||
bool GodotJavaWrapper::request_permission(const String &p_name) {
|
bool GodotJavaWrapper::request_permission(const String &p_name) {
|
||||||
if (_request_permission) {
|
if (_request_permission) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
|
jstring jStrName = env->NewStringUTF(p_name.utf8().get_data());
|
||||||
return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
|
return env->CallBooleanMethod(godot_instance, _request_permission, jStrName);
|
||||||
} else {
|
} else {
|
||||||
|
@ -225,7 +225,7 @@ bool GodotJavaWrapper::request_permission(const String &p_name) {
|
||||||
|
|
||||||
bool GodotJavaWrapper::request_permissions() {
|
bool GodotJavaWrapper::request_permissions() {
|
||||||
if (_request_permissions) {
|
if (_request_permissions) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
return env->CallBooleanMethod(godot_instance, _request_permissions);
|
return env->CallBooleanMethod(godot_instance, _request_permissions);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -235,7 +235,7 @@ bool GodotJavaWrapper::request_permissions() {
|
||||||
Vector<String> GodotJavaWrapper::get_granted_permissions() const {
|
Vector<String> GodotJavaWrapper::get_granted_permissions() const {
|
||||||
Vector<String> permissions_list;
|
Vector<String> permissions_list;
|
||||||
if (_get_granted_permissions) {
|
if (_get_granted_permissions) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
|
jobject permissions_object = env->CallObjectMethod(godot_instance, _get_granted_permissions);
|
||||||
jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
|
jobjectArray *arr = reinterpret_cast<jobjectArray *>(&permissions_object);
|
||||||
|
|
||||||
|
@ -253,14 +253,14 @@ Vector<String> GodotJavaWrapper::get_granted_permissions() const {
|
||||||
|
|
||||||
void GodotJavaWrapper::init_input_devices() {
|
void GodotJavaWrapper::init_input_devices() {
|
||||||
if (_init_input_devices) {
|
if (_init_input_devices) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(godot_instance, _init_input_devices);
|
env->CallVoidMethod(godot_instance, _init_input_devices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
jobject GodotJavaWrapper::get_surface() {
|
jobject GodotJavaWrapper::get_surface() {
|
||||||
if (_get_surface) {
|
if (_get_surface) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
return env->CallObjectMethod(godot_instance, _get_surface);
|
return env->CallObjectMethod(godot_instance, _get_surface);
|
||||||
} else {
|
} else {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -269,7 +269,7 @@ jobject GodotJavaWrapper::get_surface() {
|
||||||
|
|
||||||
bool GodotJavaWrapper::is_activity_resumed() {
|
bool GodotJavaWrapper::is_activity_resumed() {
|
||||||
if (_is_activity_resumed) {
|
if (_is_activity_resumed) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
|
return env->CallBooleanMethod(godot_instance, _is_activity_resumed);
|
||||||
} else {
|
} else {
|
||||||
return false;
|
return false;
|
||||||
|
@ -278,7 +278,7 @@ bool GodotJavaWrapper::is_activity_resumed() {
|
||||||
|
|
||||||
void GodotJavaWrapper::vibrate(int p_duration_ms) {
|
void GodotJavaWrapper::vibrate(int p_duration_ms) {
|
||||||
if (_vibrate) {
|
if (_vibrate) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
|
env->CallVoidMethod(godot_instance, _vibrate, p_duration_ms);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,7 +38,7 @@ jmethodID NetSocketAndroid::_multicast_lock_acquire = 0;
|
||||||
jmethodID NetSocketAndroid::_multicast_lock_release = 0;
|
jmethodID NetSocketAndroid::_multicast_lock_release = 0;
|
||||||
|
|
||||||
void NetSocketAndroid::setup(jobject p_net_utils) {
|
void NetSocketAndroid::setup(jobject p_net_utils) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
|
|
||||||
net_utils = env->NewGlobalRef(p_net_utils);
|
net_utils = env->NewGlobalRef(p_net_utils);
|
||||||
|
|
||||||
|
@ -51,14 +51,14 @@ void NetSocketAndroid::setup(jobject p_net_utils) {
|
||||||
|
|
||||||
void NetSocketAndroid::multicast_lock_acquire() {
|
void NetSocketAndroid::multicast_lock_acquire() {
|
||||||
if (_multicast_lock_acquire) {
|
if (_multicast_lock_acquire) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(net_utils, _multicast_lock_acquire);
|
env->CallVoidMethod(net_utils, _multicast_lock_acquire);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetSocketAndroid::multicast_lock_release() {
|
void NetSocketAndroid::multicast_lock_release() {
|
||||||
if (_multicast_lock_release) {
|
if (_multicast_lock_release) {
|
||||||
JNIEnv *env = ThreadAndroid::get_env();
|
JNIEnv *env = get_jni_env();
|
||||||
env->CallVoidMethod(net_utils, _multicast_lock_release);
|
env->CallVoidMethod(net_utils, _multicast_lock_release);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,14 +37,14 @@
|
||||||
/**
|
/**
|
||||||
* Converts JNI jstring to Godot String.
|
* Converts JNI jstring to Godot String.
|
||||||
* @param source Source JNI string. If null an empty string is returned.
|
* @param source Source JNI string. If null an empty string is returned.
|
||||||
* @param env JNI environment instance. If null obtained by ThreadAndroid::get_env().
|
* @param env JNI environment instance. If null obtained by get_jni_env().
|
||||||
* @return Godot string instance.
|
* @return Godot string instance.
|
||||||
*/
|
*/
|
||||||
static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) {
|
static inline String jstring_to_string(jstring source, JNIEnv *env = nullptr) {
|
||||||
String result;
|
String result;
|
||||||
if (source) {
|
if (source) {
|
||||||
if (!env) {
|
if (!env) {
|
||||||
env = ThreadAndroid::get_env();
|
env = get_jni_env();
|
||||||
}
|
}
|
||||||
const char *const source_utf8 = env->GetStringUTFChars(source, nullptr);
|
const char *const source_utf8 = env->GetStringUTFChars(source, nullptr);
|
||||||
if (source_utf8) {
|
if (source_utf8) {
|
||||||
|
|
|
@ -30,116 +30,29 @@
|
||||||
|
|
||||||
#include "thread_jandroid.h"
|
#include "thread_jandroid.h"
|
||||||
|
|
||||||
#include "core/object/script_language.h"
|
#include "core/os/thread.h"
|
||||||
#include "core/os/memory.h"
|
|
||||||
#include "core/templates/safe_refcount.h"
|
|
||||||
|
|
||||||
static void _thread_id_key_destr_callback(void *p_value) {
|
static JavaVM *java_vm = nullptr;
|
||||||
memdelete(static_cast<Thread::ID *>(p_value));
|
static thread_local JNIEnv *env = nullptr;
|
||||||
}
|
|
||||||
|
|
||||||
static pthread_key_t _create_thread_id_key() {
|
static void init_thread() {
|
||||||
pthread_key_t key;
|
|
||||||
pthread_key_create(&key, &_thread_id_key_destr_callback);
|
|
||||||
return key;
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_key_t ThreadAndroid::thread_id_key = _create_thread_id_key();
|
|
||||||
Thread::ID ThreadAndroid::next_thread_id = 0;
|
|
||||||
|
|
||||||
Thread::ID ThreadAndroid::get_id() const {
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *ThreadAndroid::create_thread_jandroid() {
|
|
||||||
return memnew(ThreadAndroid);
|
|
||||||
}
|
|
||||||
|
|
||||||
void *ThreadAndroid::thread_callback(void *userdata) {
|
|
||||||
ThreadAndroid *t = reinterpret_cast<ThreadAndroid *>(userdata);
|
|
||||||
setup_thread();
|
|
||||||
ScriptServer::thread_enter(); //scripts may need to attach a stack
|
|
||||||
t->id = atomic_increment(&next_thread_id);
|
|
||||||
pthread_setspecific(thread_id_key, (void *)memnew(ID(t->id)));
|
|
||||||
t->callback(t->user);
|
|
||||||
ScriptServer::thread_exit();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread *ThreadAndroid::create_func_jandroid(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
|
|
||||||
ThreadAndroid *tr = memnew(ThreadAndroid);
|
|
||||||
tr->callback = p_callback;
|
|
||||||
tr->user = p_user;
|
|
||||||
pthread_attr_init(&tr->pthread_attr);
|
|
||||||
pthread_attr_setdetachstate(&tr->pthread_attr, PTHREAD_CREATE_JOINABLE);
|
|
||||||
|
|
||||||
pthread_create(&tr->pthread, &tr->pthread_attr, thread_callback, tr);
|
|
||||||
|
|
||||||
return tr;
|
|
||||||
}
|
|
||||||
|
|
||||||
Thread::ID ThreadAndroid::get_thread_id_func_jandroid() {
|
|
||||||
void *value = pthread_getspecific(thread_id_key);
|
|
||||||
|
|
||||||
if (value)
|
|
||||||
return *static_cast<ID *>(value);
|
|
||||||
|
|
||||||
ID new_id = atomic_increment(&next_thread_id);
|
|
||||||
pthread_setspecific(thread_id_key, (void *)memnew(ID(new_id)));
|
|
||||||
return new_id;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadAndroid::wait_to_finish_func_jandroid(Thread *p_thread) {
|
|
||||||
ThreadAndroid *tp = static_cast<ThreadAndroid *>(p_thread);
|
|
||||||
ERR_FAIL_COND(!tp);
|
|
||||||
ERR_FAIL_COND(tp->pthread == 0);
|
|
||||||
|
|
||||||
pthread_join(tp->pthread, nullptr);
|
|
||||||
tp->pthread = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ThreadAndroid::_thread_destroyed(void *value) {
|
|
||||||
/* The thread is being destroyed, detach it from the Java VM and set the mThreadKey value to NULL as required */
|
|
||||||
JNIEnv *env = (JNIEnv *)value;
|
|
||||||
if (env != nullptr) {
|
|
||||||
java_vm->DetachCurrentThread();
|
|
||||||
pthread_setspecific(jvm_key, nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pthread_key_t ThreadAndroid::jvm_key;
|
|
||||||
JavaVM *ThreadAndroid::java_vm = nullptr;
|
|
||||||
|
|
||||||
void ThreadAndroid::setup_thread() {
|
|
||||||
if (pthread_getspecific(jvm_key))
|
|
||||||
return; //already setup
|
|
||||||
JNIEnv *env;
|
|
||||||
java_vm->AttachCurrentThread(&env, nullptr);
|
java_vm->AttachCurrentThread(&env, nullptr);
|
||||||
pthread_setspecific(jvm_key, (void *)env);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ThreadAndroid::make_default(JavaVM *p_java_vm) {
|
static void term_thread() {
|
||||||
java_vm = p_java_vm;
|
java_vm->DetachCurrentThread();
|
||||||
create_func = create_func_jandroid;
|
|
||||||
get_thread_id_func = get_thread_id_func_jandroid;
|
|
||||||
wait_to_finish_func = wait_to_finish_func_jandroid;
|
|
||||||
pthread_key_create(&jvm_key, _thread_destroyed);
|
|
||||||
setup_thread();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
JNIEnv *ThreadAndroid::get_env() {
|
void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env) {
|
||||||
if (!pthread_getspecific(jvm_key)) {
|
java_vm = p_jvm;
|
||||||
setup_thread();
|
env = p_env;
|
||||||
}
|
Thread::_set_platform_funcs(nullptr, nullptr, &init_thread, &term_thread);
|
||||||
|
}
|
||||||
|
|
||||||
JNIEnv *env = nullptr;
|
void setup_android_thread() {
|
||||||
java_vm->AttachCurrentThread(&env, nullptr);
|
init_thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
JNIEnv *get_jni_env() {
|
||||||
return env;
|
return env;
|
||||||
}
|
}
|
||||||
|
|
||||||
ThreadAndroid::ThreadAndroid() {
|
|
||||||
pthread = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
ThreadAndroid::~ThreadAndroid() {
|
|
||||||
}
|
|
||||||
|
|
|
@ -28,46 +28,14 @@
|
||||||
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
|
||||||
/*************************************************************************/
|
/*************************************************************************/
|
||||||
|
|
||||||
#ifndef THREAD_POSIX_H
|
#ifndef THREAD_JANDROID_H
|
||||||
#define THREAD_POSIX_H
|
#define THREAD_JANDROID_H
|
||||||
|
|
||||||
#include "core/os/thread.h"
|
|
||||||
#include <jni.h>
|
#include <jni.h>
|
||||||
#include <pthread.h>
|
|
||||||
#include <sys/types.h>
|
|
||||||
|
|
||||||
class ThreadAndroid : public Thread {
|
void init_thread_jandroid(JavaVM *p_jvm, JNIEnv *p_env);
|
||||||
static pthread_key_t thread_id_key;
|
|
||||||
static ID next_thread_id;
|
|
||||||
|
|
||||||
pthread_t pthread;
|
void setup_android_thread();
|
||||||
pthread_attr_t pthread_attr;
|
JNIEnv *get_jni_env();
|
||||||
ThreadCreateCallback callback;
|
|
||||||
void *user;
|
|
||||||
ID id;
|
|
||||||
|
|
||||||
static Thread *create_thread_jandroid();
|
|
||||||
|
|
||||||
static void *thread_callback(void *userdata);
|
|
||||||
|
|
||||||
static Thread *create_func_jandroid(ThreadCreateCallback p_callback, void *, const Settings &);
|
|
||||||
static ID get_thread_id_func_jandroid();
|
|
||||||
static void wait_to_finish_func_jandroid(Thread *p_thread);
|
|
||||||
|
|
||||||
static void _thread_destroyed(void *value);
|
|
||||||
ThreadAndroid();
|
|
||||||
|
|
||||||
static pthread_key_t jvm_key;
|
|
||||||
static JavaVM *java_vm;
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ID get_id() const;
|
|
||||||
|
|
||||||
static void make_default(JavaVM *p_java_vm);
|
|
||||||
static void setup_thread();
|
|
||||||
static JNIEnv *get_env();
|
|
||||||
|
|
||||||
~ThreadAndroid();
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -57,7 +57,7 @@ class EditorExportPlatformIOS : public EditorExportPlatform {
|
||||||
|
|
||||||
// Plugins
|
// Plugins
|
||||||
volatile bool plugins_changed;
|
volatile bool plugins_changed;
|
||||||
Thread *check_for_changes_thread;
|
Thread check_for_changes_thread;
|
||||||
volatile bool quit_request;
|
volatile bool quit_request;
|
||||||
Mutex plugins_lock;
|
Mutex plugins_lock;
|
||||||
Vector<PluginConfigIOS> plugins;
|
Vector<PluginConfigIOS> plugins;
|
||||||
|
@ -1940,13 +1940,12 @@ EditorExportPlatformIOS::EditorExportPlatformIOS() {
|
||||||
plugins_changed = true;
|
plugins_changed = true;
|
||||||
quit_request = false;
|
quit_request = false;
|
||||||
|
|
||||||
check_for_changes_thread = Thread::create(_check_for_changes_poll_thread, this);
|
check_for_changes_thread.start(_check_for_changes_poll_thread, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
|
EditorExportPlatformIOS::~EditorExportPlatformIOS() {
|
||||||
quit_request = true;
|
quit_request = true;
|
||||||
Thread::wait_to_finish(check_for_changes_thread);
|
check_for_changes_thread.wait_to_finish();
|
||||||
memdelete(check_for_changes_thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_iphone_exporter() {
|
void register_iphone_exporter() {
|
||||||
|
|
|
@ -267,7 +267,7 @@ int AudioDriverJavaScript::WorkletNode::create(int p_buffer_size, int p_channels
|
||||||
|
|
||||||
void AudioDriverJavaScript::WorkletNode::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
|
void AudioDriverJavaScript::WorkletNode::start(float *p_out_buf, int p_out_buf_size, float *p_in_buf, int p_in_buf_size) {
|
||||||
godot_audio_worklet_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, state);
|
godot_audio_worklet_start(p_in_buf, p_in_buf_size, p_out_buf, p_out_buf_size, state);
|
||||||
thread = Thread::create(_audio_thread_func, this);
|
thread.start(_audio_thread_func, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioDriverJavaScript::WorkletNode::lock() {
|
void AudioDriverJavaScript::WorkletNode::lock() {
|
||||||
|
@ -280,8 +280,6 @@ void AudioDriverJavaScript::WorkletNode::unlock() {
|
||||||
|
|
||||||
void AudioDriverJavaScript::WorkletNode::finish() {
|
void AudioDriverJavaScript::WorkletNode::finish() {
|
||||||
quit = true; // Ask thread to quit.
|
quit = true; // Ask thread to quit.
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -59,7 +59,7 @@ public:
|
||||||
STATE_MAX,
|
STATE_MAX,
|
||||||
};
|
};
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
bool quit = false;
|
bool quit = false;
|
||||||
int32_t state[STATE_MAX] = { 0 };
|
int32_t state[STATE_MAX] = { 0 };
|
||||||
|
|
||||||
|
|
|
@ -213,7 +213,7 @@ class EditorExportPlatformJavaScript : public EditorExportPlatform {
|
||||||
Ref<EditorHTTPServer> server;
|
Ref<EditorHTTPServer> server;
|
||||||
bool server_quit = false;
|
bool server_quit = false;
|
||||||
Mutex server_lock;
|
Mutex server_lock;
|
||||||
Thread *server_thread = nullptr;
|
Thread server_thread;
|
||||||
|
|
||||||
enum ExportMode {
|
enum ExportMode {
|
||||||
EXPORT_MODE_NORMAL = 0,
|
EXPORT_MODE_NORMAL = 0,
|
||||||
|
@ -681,7 +681,7 @@ void EditorExportPlatformJavaScript::_server_thread_poll(void *data) {
|
||||||
|
|
||||||
EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
|
EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
|
||||||
server.instance();
|
server.instance();
|
||||||
server_thread = Thread::create(_server_thread_poll, this);
|
server_thread.start(_server_thread_poll, this);
|
||||||
|
|
||||||
Ref<Image> img = memnew(Image(_javascript_logo));
|
Ref<Image> img = memnew(Image(_javascript_logo));
|
||||||
logo.instance();
|
logo.instance();
|
||||||
|
@ -702,8 +702,7 @@ EditorExportPlatformJavaScript::EditorExportPlatformJavaScript() {
|
||||||
EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
|
EditorExportPlatformJavaScript::~EditorExportPlatformJavaScript() {
|
||||||
server->stop();
|
server->stop();
|
||||||
server_quit = true;
|
server_quit = true;
|
||||||
Thread::wait_to_finish(server_thread);
|
server_thread.wait_to_finish();
|
||||||
memdelete(server_thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_javascript_exporter() {
|
void register_javascript_exporter() {
|
||||||
|
|
|
@ -4266,7 +4266,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
events_thread = Thread::create(_poll_events_thread, this);
|
events_thread.start(_poll_events_thread, this);
|
||||||
|
|
||||||
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
|
_update_real_mouse_position(windows[MAIN_WINDOW_ID]);
|
||||||
|
|
||||||
|
@ -4280,9 +4280,7 @@ DisplayServerX11::~DisplayServerX11() {
|
||||||
_clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window);
|
_clipboard_transfer_ownership(XInternAtom(x11_display, "CLIPBOARD", 0), x11_main_window);
|
||||||
|
|
||||||
events_thread_done = true;
|
events_thread_done = true;
|
||||||
Thread::wait_to_finish(events_thread);
|
events_thread.wait_to_finish();
|
||||||
memdelete(events_thread);
|
|
||||||
events_thread = nullptr;
|
|
||||||
|
|
||||||
//destroy all windows
|
//destroy all windows
|
||||||
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
|
for (Map<WindowID, WindowData>::Element *E = windows.front(); E; E = E->next()) {
|
||||||
|
|
|
@ -252,7 +252,7 @@ class DisplayServerX11 : public DisplayServer {
|
||||||
void _dispatch_input_event(const Ref<InputEvent> &p_event);
|
void _dispatch_input_event(const Ref<InputEvent> &p_event);
|
||||||
|
|
||||||
mutable Mutex events_mutex;
|
mutable Mutex events_mutex;
|
||||||
Thread *events_thread = nullptr;
|
Thread events_thread;
|
||||||
bool events_thread_done = false;
|
bool events_thread_done = false;
|
||||||
LocalVector<XEvent> polled_events;
|
LocalVector<XEvent> polled_events;
|
||||||
static void _poll_events_thread(void *ud);
|
static void _poll_events_thread(void *ud);
|
||||||
|
|
|
@ -73,13 +73,12 @@ void JoypadLinux::Joypad::reset() {
|
||||||
JoypadLinux::JoypadLinux(Input *in) {
|
JoypadLinux::JoypadLinux(Input *in) {
|
||||||
exit_udev = false;
|
exit_udev = false;
|
||||||
input = in;
|
input = in;
|
||||||
joy_thread = Thread::create(joy_thread_func, this);
|
joy_thread.start(joy_thread_func, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
JoypadLinux::~JoypadLinux() {
|
JoypadLinux::~JoypadLinux() {
|
||||||
exit_udev = true;
|
exit_udev = true;
|
||||||
Thread::wait_to_finish(joy_thread);
|
joy_thread.wait_to_finish();
|
||||||
memdelete(joy_thread);
|
|
||||||
close_joypad();
|
close_joypad();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -72,7 +72,7 @@ private:
|
||||||
|
|
||||||
bool exit_udev;
|
bool exit_udev;
|
||||||
Mutex joy_mutex;
|
Mutex joy_mutex;
|
||||||
Thread *joy_thread;
|
Thread joy_thread;
|
||||||
Input *input;
|
Input *input;
|
||||||
Joypad joypads[JOYPADS_MAX];
|
Joypad joypads[JOYPADS_MAX];
|
||||||
Vector<String> attached_devices;
|
Vector<String> attached_devices;
|
||||||
|
|
|
@ -45,7 +45,6 @@
|
||||||
#include "servers/audio_server.h"
|
#include "servers/audio_server.h"
|
||||||
#include "servers/rendering/rendering_server_default.h"
|
#include "servers/rendering/rendering_server_default.h"
|
||||||
#include "servers/rendering/rendering_server_wrap_mt.h"
|
#include "servers/rendering/rendering_server_wrap_mt.h"
|
||||||
#include "thread_uwp.h"
|
|
||||||
|
|
||||||
#include <ppltasks.h>
|
#include <ppltasks.h>
|
||||||
#include <wrl.h>
|
#include <wrl.h>
|
||||||
|
@ -130,8 +129,6 @@ void OS_UWP::initialize_core() {
|
||||||
|
|
||||||
//RedirectIOToConsole();
|
//RedirectIOToConsole();
|
||||||
|
|
||||||
ThreadUWP::make_default();
|
|
||||||
|
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
/*************************************************************************/
|
|
||||||
/* thread_uwp.cpp */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* Copyright (c) 2014-2021 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 "thread_uwp.h"
|
|
||||||
|
|
||||||
#include "core/os/memory.h"
|
|
||||||
|
|
||||||
Thread *ThreadUWP::create_func_uwp(ThreadCreateCallback p_callback, void *p_user, const Settings &) {
|
|
||||||
ThreadUWP *thread = memnew(ThreadUWP);
|
|
||||||
|
|
||||||
std::thread new_thread(p_callback, p_user);
|
|
||||||
std::swap(thread->thread, new_thread);
|
|
||||||
|
|
||||||
return thread;
|
|
||||||
};
|
|
||||||
|
|
||||||
Thread::ID ThreadUWP::get_thread_id_func_uwp() {
|
|
||||||
return std::hash<std::thread::id>()(std::this_thread::get_id());
|
|
||||||
};
|
|
||||||
|
|
||||||
void ThreadUWP::wait_to_finish_func_uwp(Thread *p_thread) {
|
|
||||||
ThreadUWP *tp = static_cast<ThreadUWP *>(p_thread);
|
|
||||||
tp->thread.join();
|
|
||||||
};
|
|
||||||
|
|
||||||
Thread::ID ThreadUWP::get_id() const {
|
|
||||||
return std::hash<std::thread::id>()(thread.get_id());
|
|
||||||
};
|
|
||||||
|
|
||||||
void ThreadUWP::make_default() {
|
|
||||||
create_func = create_func_uwp;
|
|
||||||
get_thread_id_func = get_thread_id_func_uwp;
|
|
||||||
wait_to_finish_func = wait_to_finish_func_uwp;
|
|
||||||
};
|
|
|
@ -1,59 +0,0 @@
|
||||||
/*************************************************************************/
|
|
||||||
/* thread_uwp.h */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* This file is part of: */
|
|
||||||
/* GODOT ENGINE */
|
|
||||||
/* https://godotengine.org */
|
|
||||||
/*************************************************************************/
|
|
||||||
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
|
|
||||||
/* Copyright (c) 2014-2021 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 THREAD_UWP_H
|
|
||||||
#define THREAD_UWP_H
|
|
||||||
|
|
||||||
#ifdef UWP_ENABLED
|
|
||||||
|
|
||||||
#include "core/os/thread.h"
|
|
||||||
|
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
class ThreadUWP : public Thread {
|
|
||||||
std::thread thread;
|
|
||||||
|
|
||||||
static Thread *create_func_uwp(ThreadCreateCallback p_callback, void *, const Settings &);
|
|
||||||
static ID get_thread_id_func_uwp();
|
|
||||||
static void wait_to_finish_func_uwp(Thread *p_thread);
|
|
||||||
|
|
||||||
ThreadUWP() {}
|
|
||||||
|
|
||||||
public:
|
|
||||||
virtual ID get_id() const;
|
|
||||||
|
|
||||||
static void make_default();
|
|
||||||
|
|
||||||
~ThreadUWP() {}
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif // UWP_ENABLED
|
|
||||||
|
|
||||||
#endif // THREAD_UWP_H
|
|
|
@ -39,7 +39,6 @@
|
||||||
#include "core/version_generated.gen.h"
|
#include "core/version_generated.gen.h"
|
||||||
#include "drivers/windows/dir_access_windows.h"
|
#include "drivers/windows/dir_access_windows.h"
|
||||||
#include "drivers/windows/file_access_windows.h"
|
#include "drivers/windows/file_access_windows.h"
|
||||||
#include "drivers/windows/thread_windows.h"
|
|
||||||
#include "joypad_windows.h"
|
#include "joypad_windows.h"
|
||||||
#include "lang_table.h"
|
#include "lang_table.h"
|
||||||
#include "main/main.h"
|
#include "main/main.h"
|
||||||
|
@ -176,8 +175,6 @@ void OS_Windows::initialize() {
|
||||||
|
|
||||||
//RedirectIOToConsole();
|
//RedirectIOToConsole();
|
||||||
|
|
||||||
ThreadWindows::make_default();
|
|
||||||
|
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_RESOURCES);
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_USERDATA);
|
||||||
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
FileAccess::make_default<FileAccessWindows>(FileAccess::ACCESS_FILESYSTEM);
|
||||||
|
|
|
@ -170,18 +170,17 @@ void _bake_navigation_mesh(void *p_user_data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3D::bake_navigation_mesh() {
|
void NavigationRegion3D::bake_navigation_mesh() {
|
||||||
ERR_FAIL_COND(bake_thread != nullptr);
|
ERR_FAIL_COND(bake_thread.is_started());
|
||||||
|
|
||||||
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
|
BakeThreadsArgs *args = memnew(BakeThreadsArgs);
|
||||||
args->nav_region = this;
|
args->nav_region = this;
|
||||||
|
|
||||||
bake_thread = Thread::create(_bake_navigation_mesh, args);
|
bake_thread.start(_bake_navigation_mesh, args);
|
||||||
ERR_FAIL_COND(bake_thread == nullptr);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
|
void NavigationRegion3D::_bake_finished(Ref<NavigationMesh> p_nav_mesh) {
|
||||||
set_navigation_mesh(p_nav_mesh);
|
set_navigation_mesh(p_nav_mesh);
|
||||||
bake_thread = nullptr;
|
bake_thread.wait_to_finish();
|
||||||
emit_signal("bake_finished");
|
emit_signal("bake_finished");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@ class NavigationRegion3D : public Node3D {
|
||||||
|
|
||||||
Navigation3D *navigation = nullptr;
|
Navigation3D *navigation = nullptr;
|
||||||
Node *debug_view = nullptr;
|
Node *debug_view = nullptr;
|
||||||
Thread *bake_thread = nullptr;
|
Thread bake_thread;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void _notification(int p_what);
|
void _notification(int p_what);
|
||||||
|
|
|
@ -163,7 +163,7 @@ Error HTTPRequest::request_raw(const String &p_url, const Vector<String> &p_cust
|
||||||
thread_done = false;
|
thread_done = false;
|
||||||
thread_request_quit = false;
|
thread_request_quit = false;
|
||||||
client->set_blocking_mode(true);
|
client->set_blocking_mode(true);
|
||||||
thread = Thread::create(_thread_func, this);
|
thread.start(_thread_func, this);
|
||||||
} else {
|
} else {
|
||||||
client->set_blocking_mode(false);
|
client->set_blocking_mode(false);
|
||||||
err = _request();
|
err = _request();
|
||||||
|
@ -209,9 +209,7 @@ void HTTPRequest::cancel_request() {
|
||||||
set_process_internal(false);
|
set_process_internal(false);
|
||||||
} else {
|
} else {
|
||||||
thread_request_quit = true;
|
thread_request_quit = true;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
|
@ -640,8 +638,6 @@ void HTTPRequest::_bind_methods() {
|
||||||
}
|
}
|
||||||
|
|
||||||
HTTPRequest::HTTPRequest() {
|
HTTPRequest::HTTPRequest() {
|
||||||
thread = nullptr;
|
|
||||||
|
|
||||||
port = 80;
|
port = 80;
|
||||||
redirections = 0;
|
redirections = 0;
|
||||||
max_redirects = 8;
|
max_redirects = 8;
|
||||||
|
|
|
@ -110,7 +110,7 @@ private:
|
||||||
volatile bool thread_done;
|
volatile bool thread_done;
|
||||||
volatile bool thread_request_quit;
|
volatile bool thread_request_quit;
|
||||||
|
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
|
|
||||||
void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
|
void _request_done(int p_status, int p_code, const PackedStringArray &p_headers, const PackedByteArray &p_data);
|
||||||
static void _thread_func(void *p_userdata);
|
static void _thread_func(void *p_userdata);
|
||||||
|
|
|
@ -48,7 +48,7 @@ Error AudioDriverDummy::init() {
|
||||||
|
|
||||||
samples_in = memnew_arr(int32_t, buffer_frames * channels);
|
samples_in = memnew_arr(int32_t, buffer_frames * channels);
|
||||||
|
|
||||||
thread = Thread::create(AudioDriverDummy::thread_func, this);
|
thread.start(AudioDriverDummy::thread_func, this);
|
||||||
|
|
||||||
return OK;
|
return OK;
|
||||||
};
|
};
|
||||||
|
@ -86,31 +86,18 @@ AudioDriver::SpeakerMode AudioDriverDummy::get_speaker_mode() const {
|
||||||
};
|
};
|
||||||
|
|
||||||
void AudioDriverDummy::lock() {
|
void AudioDriverDummy::lock() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mutex.lock();
|
mutex.lock();
|
||||||
};
|
};
|
||||||
|
|
||||||
void AudioDriverDummy::unlock() {
|
void AudioDriverDummy::unlock() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mutex.unlock();
|
mutex.unlock();
|
||||||
};
|
};
|
||||||
|
|
||||||
void AudioDriverDummy::finish() {
|
void AudioDriverDummy::finish() {
|
||||||
if (!thread) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
exit_thread = true;
|
exit_thread = true;
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
|
|
||||||
if (samples_in) {
|
if (samples_in) {
|
||||||
memdelete_arr(samples_in);
|
memdelete_arr(samples_in);
|
||||||
};
|
};
|
||||||
|
|
||||||
memdelete(thread);
|
|
||||||
thread = nullptr;
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -37,7 +37,7 @@
|
||||||
#include "core/os/thread.h"
|
#include "core/os/thread.h"
|
||||||
|
|
||||||
class AudioDriverDummy : public AudioDriver {
|
class AudioDriverDummy : public AudioDriver {
|
||||||
Thread *thread = nullptr;
|
Thread thread;
|
||||||
Mutex mutex;
|
Mutex mutex;
|
||||||
|
|
||||||
int32_t *samples_in;
|
int32_t *samples_in;
|
||||||
|
|
|
@ -118,7 +118,7 @@ void AudioEffectRecordInstance::init() {
|
||||||
#ifdef NO_THREADS
|
#ifdef NO_THREADS
|
||||||
AudioServer::get_singleton()->add_update_callback(&AudioEffectRecordInstance::_update, this);
|
AudioServer::get_singleton()->add_update_callback(&AudioEffectRecordInstance::_update, this);
|
||||||
#else
|
#else
|
||||||
io_thread = Thread::create(_thread_callback, this);
|
io_thread.start(_thread_callback, this);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -126,9 +126,7 @@ void AudioEffectRecordInstance::finish() {
|
||||||
#ifdef NO_THREADS
|
#ifdef NO_THREADS
|
||||||
AudioServer::get_singleton()->remove_update_callback(&AudioEffectRecordInstance::_update, this);
|
AudioServer::get_singleton()->remove_update_callback(&AudioEffectRecordInstance::_update, this);
|
||||||
#else
|
#else
|
||||||
if (thread_active) {
|
io_thread.wait_to_finish();
|
||||||
Thread::wait_to_finish(io_thread);
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -48,7 +48,7 @@ class AudioEffectRecordInstance : public AudioEffectInstance {
|
||||||
Ref<AudioEffectRecord> base;
|
Ref<AudioEffectRecord> base;
|
||||||
|
|
||||||
bool is_recording;
|
bool is_recording;
|
||||||
Thread *io_thread;
|
Thread io_thread;
|
||||||
bool thread_active = false;
|
bool thread_active = false;
|
||||||
|
|
||||||
Vector<AudioFrame> ring_buffer;
|
Vector<AudioFrame> ring_buffer;
|
||||||
|
|
|
@ -76,7 +76,7 @@ void PhysicsServer2DWrapMT::step(real_t p_step) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsServer2DWrapMT::sync() {
|
void PhysicsServer2DWrapMT::sync() {
|
||||||
if (thread) {
|
if (create_thread) {
|
||||||
if (first_frame) {
|
if (first_frame) {
|
||||||
first_frame = false;
|
first_frame = false;
|
||||||
} else {
|
} else {
|
||||||
|
@ -97,7 +97,7 @@ void PhysicsServer2DWrapMT::end_sync() {
|
||||||
void PhysicsServer2DWrapMT::init() {
|
void PhysicsServer2DWrapMT::init() {
|
||||||
if (create_thread) {
|
if (create_thread) {
|
||||||
//OS::get_singleton()->release_rendering_thread();
|
//OS::get_singleton()->release_rendering_thread();
|
||||||
thread = Thread::create(_thread_callback, this);
|
thread.start(_thread_callback, this);
|
||||||
while (!step_thread_up) {
|
while (!step_thread_up) {
|
||||||
OS::get_singleton()->delay_usec(1000);
|
OS::get_singleton()->delay_usec(1000);
|
||||||
}
|
}
|
||||||
|
@ -107,12 +107,9 @@ void PhysicsServer2DWrapMT::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhysicsServer2DWrapMT::finish() {
|
void PhysicsServer2DWrapMT::finish() {
|
||||||
if (thread) {
|
if (thread.is_started()) {
|
||||||
command_queue.push(this, &PhysicsServer2DWrapMT::thread_exit);
|
command_queue.push(this, &PhysicsServer2DWrapMT::thread_exit);
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
|
|
||||||
thread = nullptr;
|
|
||||||
} else {
|
} else {
|
||||||
physics_2d_server->finish();
|
physics_2d_server->finish();
|
||||||
}
|
}
|
||||||
|
@ -135,7 +132,6 @@ PhysicsServer2DWrapMT::PhysicsServer2DWrapMT(PhysicsServer2D *p_contained, bool
|
||||||
command_queue(p_create_thread) {
|
command_queue(p_create_thread) {
|
||||||
physics_2d_server = p_contained;
|
physics_2d_server = p_contained;
|
||||||
create_thread = p_create_thread;
|
create_thread = p_create_thread;
|
||||||
thread = nullptr;
|
|
||||||
step_pending = 0;
|
step_pending = 0;
|
||||||
step_thread_up = false;
|
step_thread_up = false;
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@ class PhysicsServer2DWrapMT : public PhysicsServer2D {
|
||||||
Thread::ID server_thread;
|
Thread::ID server_thread;
|
||||||
Thread::ID main_thread;
|
Thread::ID main_thread;
|
||||||
volatile bool exit;
|
volatile bool exit;
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
volatile bool step_thread_up;
|
volatile bool step_thread_up;
|
||||||
bool create_thread;
|
bool create_thread;
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ void RenderingServerWrapMT::init() {
|
||||||
print_verbose("RenderingServerWrapMT: Creating render thread");
|
print_verbose("RenderingServerWrapMT: Creating render thread");
|
||||||
DisplayServer::get_singleton()->release_rendering_thread();
|
DisplayServer::get_singleton()->release_rendering_thread();
|
||||||
if (create_thread) {
|
if (create_thread) {
|
||||||
thread = Thread::create(_thread_callback, this);
|
thread.start(_thread_callback, this);
|
||||||
print_verbose("RenderingServerWrapMT: Starting render thread");
|
print_verbose("RenderingServerWrapMT: Starting render thread");
|
||||||
}
|
}
|
||||||
while (!draw_thread_up) {
|
while (!draw_thread_up) {
|
||||||
|
@ -136,12 +136,9 @@ void RenderingServerWrapMT::finish() {
|
||||||
canvas_light_occluder_free_cached_ids();
|
canvas_light_occluder_free_cached_ids();
|
||||||
canvas_occluder_polygon_free_cached_ids();
|
canvas_occluder_polygon_free_cached_ids();
|
||||||
|
|
||||||
if (thread) {
|
if (create_thread) {
|
||||||
command_queue.push(this, &RenderingServerWrapMT::thread_exit);
|
command_queue.push(this, &RenderingServerWrapMT::thread_exit);
|
||||||
Thread::wait_to_finish(thread);
|
thread.wait_to_finish();
|
||||||
memdelete(thread);
|
|
||||||
|
|
||||||
thread = nullptr;
|
|
||||||
} else {
|
} else {
|
||||||
rendering_server->finish();
|
rendering_server->finish();
|
||||||
}
|
}
|
||||||
|
@ -160,7 +157,6 @@ RenderingServerWrapMT::RenderingServerWrapMT(RenderingServer *p_contained, bool
|
||||||
|
|
||||||
rendering_server = p_contained;
|
rendering_server = p_contained;
|
||||||
create_thread = p_create_thread;
|
create_thread = p_create_thread;
|
||||||
thread = nullptr;
|
|
||||||
draw_pending = 0;
|
draw_pending = 0;
|
||||||
draw_thread_up = false;
|
draw_thread_up = false;
|
||||||
pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc");
|
pool_max_size = GLOBAL_GET("memory/limits/multithreaded_server/rid_pool_prealloc");
|
||||||
|
|
|
@ -46,7 +46,7 @@ class RenderingServerWrapMT : public RenderingServer {
|
||||||
|
|
||||||
Thread::ID server_thread;
|
Thread::ID server_thread;
|
||||||
volatile bool exit;
|
volatile bool exit;
|
||||||
Thread *thread;
|
Thread thread;
|
||||||
volatile bool draw_thread_up;
|
volatile bool draw_thread_up;
|
||||||
bool create_thread;
|
bool create_thread;
|
||||||
|
|
||||||
|
|
|
@ -122,8 +122,8 @@ public:
|
||||||
int message_count_to_read = 0;
|
int message_count_to_read = 0;
|
||||||
bool exit_threads = false;
|
bool exit_threads = false;
|
||||||
|
|
||||||
Thread *reader_thread = nullptr;
|
Thread reader_thread;
|
||||||
Thread *writer_thread = nullptr;
|
Thread writer_thread;
|
||||||
|
|
||||||
int func1_count = 0;
|
int func1_count = 0;
|
||||||
|
|
||||||
|
@ -221,20 +221,16 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void init_threads() {
|
void init_threads() {
|
||||||
reader_thread = Thread::create(&SharedThreadState::static_reader_thread_loop, this);
|
reader_thread.start(&SharedThreadState::static_reader_thread_loop, this);
|
||||||
writer_thread = Thread::create(&SharedThreadState::static_writer_thread_loop, this);
|
writer_thread.start(&SharedThreadState::static_writer_thread_loop, this);
|
||||||
}
|
}
|
||||||
void destroy_threads() {
|
void destroy_threads() {
|
||||||
exit_threads = true;
|
exit_threads = true;
|
||||||
reader_threadwork.main_start_work();
|
reader_threadwork.main_start_work();
|
||||||
writer_threadwork.main_start_work();
|
writer_threadwork.main_start_work();
|
||||||
|
|
||||||
Thread::wait_to_finish(reader_thread);
|
reader_thread.wait_to_finish();
|
||||||
memdelete(reader_thread);
|
writer_thread.wait_to_finish();
|
||||||
reader_thread = nullptr;
|
|
||||||
Thread::wait_to_finish(writer_thread);
|
|
||||||
memdelete(writer_thread);
|
|
||||||
writer_thread = nullptr;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue