2023-01-05 13:25:55 +01:00
/**************************************************************************/
/* message_queue.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* 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. */
/**************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "message_queue.h"
2017-01-16 08:04:19 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2020-02-19 20:27:19 +01:00
# include "core/core_string_names.h"
2022-03-09 14:58:40 +01:00
# include "core/object/class_db.h"
2020-11-07 23:33:38 +01:00
# include "core/object/script_language.h"
2017-01-16 08:04:19 +01:00
2020-04-02 01:20:12 +02:00
MessageQueue * MessageQueue : : singleton = nullptr ;
2014-02-10 02:10:30 +01:00
MessageQueue * MessageQueue : : get_singleton ( ) {
return singleton ;
}
2022-03-09 14:58:40 +01:00
Error MessageQueue : : push_callp ( ObjectID p_id , const StringName & p_method , const Variant * * p_args , int p_argcount , bool p_show_error ) {
return push_callablep ( Callable ( p_id , p_method ) , p_args , p_argcount , p_show_error ) ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Error MessageQueue : : push_set ( ObjectID p_id , const StringName & p_prop , const Variant & p_value ) {
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_METHOD_
2017-03-05 16:44:50 +01:00
uint8_t room_needed = sizeof ( Message ) + sizeof ( Variant ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ( buffer_end + room_needed ) > = buffer_size ) {
2014-02-10 02:10:30 +01:00
String type ;
2020-05-14 16:41:43 +02:00
if ( ObjectDB : : get_instance ( p_id ) ) {
2017-03-05 16:44:50 +01:00
type = ObjectDB : : get_instance ( p_id ) - > get_class ( ) ;
2020-05-14 16:41:43 +02:00
}
2021-08-17 12:44:07 +02:00
ERR_PRINT ( " Failed set: " + type + " : " + p_prop + " target ID: " + itos ( p_id ) + " . Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings. " ) ;
2014-02-10 02:10:30 +01:00
statistics ( ) ;
2021-08-17 12:44:07 +02:00
return ERR_OUT_OF_MEMORY ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Message * msg = memnew_placement ( & buffer [ buffer_end ] , Message ) ;
msg - > args = 1 ;
2020-02-19 20:27:19 +01:00
msg - > callable = Callable ( p_id , p_prop ) ;
2017-03-05 16:44:50 +01:00
msg - > type = TYPE_SET ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
buffer_end + = sizeof ( Message ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Variant * v = memnew_placement ( & buffer [ buffer_end ] , Variant ) ;
buffer_end + = sizeof ( Variant ) ;
* v = p_value ;
2014-02-10 02:10:30 +01:00
return OK ;
}
Error MessageQueue : : push_notification ( ObjectID p_id , int p_notification ) {
_THREAD_SAFE_METHOD_
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND_V ( p_notification < 0 , ERR_INVALID_PARAMETER ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
uint8_t room_needed = sizeof ( Message ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
if ( ( buffer_end + room_needed ) > = buffer_size ) {
2021-08-17 12:44:07 +02:00
ERR_PRINT ( " Failed notification: " + itos ( p_notification ) + " target ID: " + itos ( p_id ) + " . Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings. " ) ;
2014-02-10 02:10:30 +01:00
statistics ( ) ;
2021-08-17 12:44:07 +02:00
return ERR_OUT_OF_MEMORY ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
Message * msg = memnew_placement ( & buffer [ buffer_end ] , Message ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
msg - > type = TYPE_NOTIFICATION ;
2020-02-19 20:27:19 +01:00
msg - > callable = Callable ( p_id , CoreStringNames : : get_singleton ( ) - > notification ) ; //name is meaningless but callable needs it
2014-02-10 02:10:30 +01:00
//msg->target;
2017-03-05 16:44:50 +01:00
msg - > notification = p_notification ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
buffer_end + = sizeof ( Message ) ;
2014-02-10 02:10:30 +01:00
return OK ;
}
2022-03-09 14:58:40 +01:00
Error MessageQueue : : push_callp ( Object * p_object , const StringName & p_method , const Variant * * p_args , int p_argcount , bool p_show_error ) {
return push_callp ( p_object - > get_instance_id ( ) , p_method , p_args , p_argcount , p_show_error ) ;
2014-02-10 02:10:30 +01:00
}
Error MessageQueue : : push_notification ( Object * p_object , int p_notification ) {
2017-08-07 12:17:31 +02:00
return push_notification ( p_object - > get_instance_id ( ) , p_notification ) ;
2014-02-10 02:10:30 +01:00
}
2020-05-14 14:29:06 +02:00
2017-03-05 16:44:50 +01:00
Error MessageQueue : : push_set ( Object * p_object , const StringName & p_prop , const Variant & p_value ) {
2017-08-07 12:17:31 +02:00
return push_set ( p_object - > get_instance_id ( ) , p_prop , p_value ) ;
2014-02-10 02:10:30 +01:00
}
2022-03-09 14:58:40 +01:00
Error MessageQueue : : push_callablep ( const Callable & p_callable , const Variant * * p_args , int p_argcount , bool p_show_error ) {
2020-02-19 20:27:19 +01:00
_THREAD_SAFE_METHOD_
2023-01-18 19:14:14 +01:00
int room_needed = sizeof ( Message ) + sizeof ( Variant ) * p_argcount ;
2020-02-19 20:27:19 +01:00
if ( ( buffer_end + room_needed ) > = buffer_size ) {
2021-08-17 12:44:07 +02:00
ERR_PRINT ( " Failed method: " + p_callable + " . Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_kb' in project settings. " ) ;
2020-02-19 20:27:19 +01:00
statistics ( ) ;
2021-08-17 12:44:07 +02:00
return ERR_OUT_OF_MEMORY ;
2020-02-19 20:27:19 +01:00
}
Message * msg = memnew_placement ( & buffer [ buffer_end ] , Message ) ;
2023-01-18 19:14:14 +01:00
msg - > args = p_argcount ;
2020-02-19 20:27:19 +01:00
msg - > callable = p_callable ;
msg - > type = TYPE_CALL ;
2020-05-14 16:41:43 +02:00
if ( p_show_error ) {
2020-02-19 20:27:19 +01:00
msg - > type | = FLAG_SHOW_ERROR ;
2020-05-14 16:41:43 +02:00
}
2020-02-19 20:27:19 +01:00
buffer_end + = sizeof ( Message ) ;
for ( int i = 0 ; i < p_argcount ; i + + ) {
Variant * v = memnew_placement ( & buffer [ buffer_end ] , Variant ) ;
buffer_end + = sizeof ( Variant ) ;
* v = * p_args [ i ] ;
}
return OK ;
}
2014-02-10 02:10:30 +01:00
void MessageQueue : : statistics ( ) {
2022-05-13 15:04:37 +02:00
HashMap < StringName , int > set_count ;
HashMap < int , int > notify_count ;
HashMap < Callable , int > call_count ;
2017-03-05 16:44:50 +01:00
int null_count = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
uint32_t read_pos = 0 ;
while ( read_pos < buffer_end ) {
Message * message = ( Message * ) & buffer [ read_pos ] ;
2014-02-10 02:10:30 +01:00
2020-02-19 20:27:19 +01:00
Object * target = message - > callable . get_object ( ) ;
2014-02-10 02:10:30 +01:00
2020-04-02 01:20:12 +02:00
if ( target ! = nullptr ) {
2017-03-05 16:44:50 +01:00
switch ( message - > type & FLAG_MASK ) {
2014-02-10 02:10:30 +01:00
case TYPE_CALL : {
2020-05-14 16:41:43 +02:00
if ( ! call_count . has ( message - > callable ) ) {
2020-02-19 20:27:19 +01:00
call_count [ message - > callable ] = 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-02-19 20:27:19 +01:00
call_count [ message - > callable ] + + ;
2014-02-10 02:10:30 +01:00
} break ;
case TYPE_NOTIFICATION : {
2020-05-14 16:41:43 +02:00
if ( ! notify_count . has ( message - > notification ) ) {
2017-03-05 16:44:50 +01:00
notify_count [ message - > notification ] = 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
notify_count [ message - > notification ] + + ;
} break ;
case TYPE_SET : {
2020-02-19 20:27:19 +01:00
StringName t = message - > callable . get_method ( ) ;
2020-05-14 16:41:43 +02:00
if ( ! set_count . has ( t ) ) {
2020-02-19 20:27:19 +01:00
set_count [ t ] = 0 ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-02-19 20:27:19 +01:00
set_count [ t ] + + ;
2014-02-10 02:10:30 +01:00
} break ;
}
2018-12-06 16:02:40 +01:00
} else {
2014-02-10 02:10:30 +01:00
//object was deleted
2018-08-24 09:35:07 +02:00
print_line ( " Object was deleted while awaiting a callback " ) ;
2014-02-10 02:10:30 +01:00
null_count + + ;
}
2017-03-05 16:44:50 +01:00
read_pos + = sizeof ( Message ) ;
2020-05-14 16:41:43 +02:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
2017-03-05 16:44:50 +01:00
read_pos + = sizeof ( Variant ) * message - > args ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
print_line ( " TOTAL BYTES: " + itos ( buffer_end ) ) ;
print_line ( " NULL count: " + itos ( null_count ) ) ;
2014-02-10 02:10:30 +01:00
2021-08-09 22:13:42 +02:00
for ( const KeyValue < StringName , int > & E : set_count ) {
print_line ( " SET " + E . key + " : " + itos ( E . value ) ) ;
2014-02-10 02:10:30 +01:00
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < Callable , int > & E : call_count ) {
print_line ( " CALL " + E . key + " : " + itos ( E . value ) ) ;
2014-02-10 02:10:30 +01:00
}
2021-08-09 22:13:42 +02:00
for ( const KeyValue < int , int > & E : notify_count ) {
print_line ( " NOTIFY " + itos ( E . key ) + " : " + itos ( E . value ) ) ;
2014-02-10 02:10:30 +01:00
}
}
int MessageQueue : : get_max_buffer_usage ( ) const {
return buffer_max_used ;
}
2020-02-19 20:27:19 +01:00
void MessageQueue : : _call_function ( const Callable & p_callable , const Variant * p_args , int p_argcount , bool p_show_error ) {
2020-04-02 01:20:12 +02:00
const Variant * * argptrs = nullptr ;
2016-01-04 13:35:21 +01:00
if ( p_argcount ) {
2017-03-05 16:44:50 +01:00
argptrs = ( const Variant * * ) alloca ( sizeof ( Variant * ) * p_argcount ) ;
for ( int i = 0 ; i < p_argcount ; i + + ) {
argptrs [ i ] = & p_args [ i ] ;
2016-01-04 13:35:21 +01:00
}
}
2020-02-19 20:27:19 +01:00
Callable : : CallError ce ;
Variant ret ;
2022-07-28 22:56:41 +02:00
p_callable . callp ( argptrs , p_argcount , ret , ce ) ;
2020-02-19 20:27:19 +01:00
if ( p_show_error & & ce . error ! = Callable : : CallError : : CALL_OK ) {
ERR_PRINT ( " Error calling deferred method: " + Variant : : get_callable_error_text ( p_callable , argptrs , p_argcount , ce ) + " . " ) ;
2016-01-04 13:35:21 +01:00
}
}
2014-02-10 02:10:30 +01:00
void MessageQueue : : flush ( ) {
2016-06-28 00:07:58 +02:00
if ( buffer_end > buffer_max_used ) {
2017-03-05 16:44:50 +01:00
buffer_max_used = buffer_end ;
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
uint32_t read_pos = 0 ;
2014-02-10 02:10:30 +01:00
2015-06-07 03:06:58 +02:00
//using reverse locking strategy
_THREAD_SAFE_LOCK_
2020-03-02 19:17:20 +01:00
if ( flushing ) {
_THREAD_SAFE_UNLOCK_
ERR_FAIL_COND ( flushing ) ; //already flushing, you did something odd
}
2019-01-14 14:59:28 +01:00
flushing = true ;
2017-03-05 16:44:50 +01:00
while ( read_pos < buffer_end ) {
2018-02-21 17:30:55 +01:00
//lock on each iteration, so a call can re-add itself to the message queue
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
Message * message = ( Message * ) & buffer [ read_pos ] ;
2014-02-10 02:10:30 +01:00
2017-05-18 13:01:12 +02:00
uint32_t advance = sizeof ( Message ) ;
2020-05-14 16:41:43 +02:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
2017-05-18 13:01:12 +02:00
advance + = sizeof ( Variant ) * message - > args ;
2020-05-14 16:41:43 +02:00
}
2017-05-18 13:01:12 +02:00
//pre-advance so this function is reentrant
read_pos + = advance ;
_THREAD_SAFE_UNLOCK_
2020-02-19 20:27:19 +01:00
Object * target = message - > callable . get_object ( ) ;
2014-02-10 02:10:30 +01:00
2020-04-02 01:20:12 +02:00
if ( target ! = nullptr ) {
2017-03-05 16:44:50 +01:00
switch ( message - > type & FLAG_MASK ) {
2014-02-10 02:10:30 +01:00
case TYPE_CALL : {
2017-03-05 16:44:50 +01:00
Variant * args = ( Variant * ) ( message + 1 ) ;
2014-02-10 02:10:30 +01:00
// messages don't expect a return value
2020-02-19 20:27:19 +01:00
_call_function ( message - > callable , args , message - > args , message - > type & FLAG_SHOW_ERROR ) ;
2014-02-10 02:10:30 +01:00
} break ;
case TYPE_NOTIFICATION : {
// messages don't expect a return value
target - > notification ( message - > notification ) ;
} break ;
case TYPE_SET : {
2017-03-05 16:44:50 +01:00
Variant * arg = ( Variant * ) ( message + 1 ) ;
2014-02-10 02:10:30 +01:00
// messages don't expect a return value
2020-02-19 20:27:19 +01:00
target - > set ( message - > callable . get_method ( ) , * arg ) ;
2014-02-10 02:10:30 +01:00
} break ;
}
}
2019-05-19 15:05:15 +02:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
Variant * args = ( Variant * ) ( message + 1 ) ;
for ( int i = 0 ; i < message - > args ; i + + ) {
args [ i ] . ~ Variant ( ) ;
}
}
2014-04-05 17:39:30 +02:00
message - > ~ Message ( ) ;
2015-06-07 03:06:58 +02:00
_THREAD_SAFE_LOCK_
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
buffer_end = 0 ; // reset buffer
2019-01-14 14:59:28 +01:00
flushing = false ;
2014-02-10 02:10:30 +01:00
_THREAD_SAFE_UNLOCK_
}
2019-01-14 14:59:28 +01:00
bool MessageQueue : : is_flushing ( ) const {
return flushing ;
}
2014-02-10 02:10:30 +01:00
MessageQueue : : MessageQueue ( ) {
2020-04-02 01:20:12 +02:00
ERR_FAIL_COND_MSG ( singleton ! = nullptr , " A MessageQueue singleton already exists. " ) ;
2017-03-05 16:44:50 +01:00
singleton = this ;
2014-02-10 02:10:30 +01:00
2022-11-08 19:53:22 +01:00
buffer_size = GLOBAL_DEF_RST ( PropertyInfo ( Variant : : INT , " memory/limits/message_queue/max_size_kb " , PROPERTY_HINT_RANGE , " 1024,4096,1,or_greater " ) , DEFAULT_QUEUE_SIZE_KB ) ;
2017-03-05 16:44:50 +01:00
buffer_size * = 1024 ;
buffer = memnew_arr ( uint8_t , buffer_size ) ;
2014-02-10 02:10:30 +01:00
}
MessageQueue : : ~ MessageQueue ( ) {
2017-03-05 16:44:50 +01:00
uint32_t read_pos = 0 ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
while ( read_pos < buffer_end ) {
Message * message = ( Message * ) & buffer [ read_pos ] ;
Variant * args = ( Variant * ) ( message + 1 ) ;
2014-02-10 02:10:30 +01:00
int argc = message - > args ;
2017-03-05 16:44:50 +01:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
2020-05-14 16:41:43 +02:00
for ( int i = 0 ; i < argc ; i + + ) {
2014-02-10 02:10:30 +01:00
args [ i ] . ~ Variant ( ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
message - > ~ Message ( ) ;
2017-03-05 16:44:50 +01:00
read_pos + = sizeof ( Message ) ;
2020-05-14 16:41:43 +02:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
2017-03-05 16:44:50 +01:00
read_pos + = sizeof ( Variant ) * message - > args ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
2020-04-02 01:20:12 +02:00
singleton = nullptr ;
2017-03-05 16:44:50 +01:00
memdelete_arr ( buffer ) ;
2014-02-10 02:10:30 +01:00
}