2014-02-10 01:10:30 +00: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-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "message_queue.h"
2017-01-16 07:04:19 +00:00
2020-11-07 22:33:38 +00:00
# include "core/config/project_settings.h"
2020-02-19 19:27:19 +00:00
# include "core/core_string_names.h"
2022-03-09 13:58:40 +00:00
# include "core/object/class_db.h"
2020-11-07 22:33:38 +00:00
# include "core/object/script_language.h"
2017-01-16 07:04:19 +00:00
2023-11-26 17:53:28 +00:00
# include <stdio.h>
2023-05-19 08:46:49 +00:00
# ifdef DEV_ENABLED
2023-09-28 13:42:55 +00:00
// Includes safety checks to ensure that a queue set as a thread singleton override
2023-05-19 08:46:49 +00:00
// is only ever called from the thread it was set for.
2024-01-28 20:51:39 +00:00
# define LOCK_MUTEX \
if ( this ! = MessageQueue : : thread_singleton ) { \
DEV_ASSERT ( ! is_current_thread_override ) ; \
mutex . lock ( ) ; \
} else { \
DEV_ASSERT ( is_current_thread_override ) ; \
2023-05-19 08:46:49 +00:00
}
# else
2023-05-10 08:00:33 +00:00
# define LOCK_MUTEX \
if ( this ! = MessageQueue : : thread_singleton ) { \
mutex . lock ( ) ; \
}
2023-05-19 08:46:49 +00:00
# endif
2023-05-10 08:00:33 +00:00
# define UNLOCK_MUTEX \
if ( this ! = MessageQueue : : thread_singleton ) { \
mutex . unlock ( ) ; \
}
2023-04-11 15:55:06 +00:00
void CallQueue : : _add_page ( ) {
2023-05-10 08:00:33 +00:00
if ( pages_used = = page_bytes . size ( ) ) {
2023-04-11 15:55:06 +00:00
pages . push_back ( allocator - > alloc ( ) ) ;
2023-05-10 08:00:33 +00:00
page_bytes . push_back ( 0 ) ;
2014-02-10 01:10:30 +00:00
}
2023-05-10 08:00:33 +00:00
page_bytes [ pages_used ] = 0 ;
2023-04-11 15:55:06 +00:00
pages_used + + ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
Error CallQueue : : 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 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
Error CallQueue : : push_callp ( Object * p_object , const StringName & p_method , const Variant * * p_args , int p_argcount , bool p_show_error ) {
2022-03-09 13:58:40 +00:00
return push_callp ( p_object - > get_instance_id ( ) , p_method , p_args , p_argcount , p_show_error ) ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
Error CallQueue : : push_notification ( Object * p_object , int p_notification ) {
2017-08-07 10:17:31 +00:00
return push_notification ( p_object - > get_instance_id ( ) , p_notification ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2023-04-11 15:55:06 +00:00
Error CallQueue : : push_set ( Object * p_object , const StringName & p_prop , const Variant & p_value ) {
2017-08-07 10:17:31 +00:00
return push_set ( p_object - > get_instance_id ( ) , p_prop , p_value ) ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
Error CallQueue : : push_callablep ( const Callable & p_callable , const Variant * * p_args , int p_argcount , bool p_show_error ) {
uint32_t room_needed = sizeof ( Message ) + sizeof ( Variant ) * p_argcount ;
2020-02-19 19:27:19 +00:00
2023-04-11 15:55:06 +00:00
ERR_FAIL_COND_V_MSG ( room_needed > uint32_t ( PAGE_SIZE_BYTES ) , ERR_INVALID_PARAMETER , " Message is too large to fit on a page ( " + itos ( PAGE_SIZE_BYTES ) + " bytes), consider passing less arguments. " ) ;
2020-02-19 19:27:19 +00:00
2023-05-10 08:00:33 +00:00
LOCK_MUTEX ;
2023-05-05 10:50:10 +00:00
2023-04-11 15:55:06 +00:00
_ensure_first_page ( ) ;
2023-05-10 08:00:33 +00:00
if ( ( page_bytes [ pages_used - 1 ] + room_needed ) > uint32_t ( PAGE_SIZE_BYTES ) ) {
2023-05-05 10:50:10 +00:00
if ( pages_used = = max_pages ) {
2023-11-26 17:53:28 +00:00
fprintf ( stderr , " Failed method: %s. Message queue out of memory. %s \n " , String ( p_callable ) . utf8 ( ) . get_data ( ) , error_text . utf8 ( ) . get_data ( ) ) ;
2023-04-11 15:55:06 +00:00
statistics ( ) ;
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return ERR_OUT_OF_MEMORY ;
}
_add_page ( ) ;
2020-02-19 19:27:19 +00:00
}
2023-04-11 15:55:06 +00:00
Page * page = pages [ pages_used - 1 ] ;
2023-05-10 08:00:33 +00:00
uint8_t * buffer_end = & page - > data [ page_bytes [ pages_used - 1 ] ] ;
2023-04-11 15:55:06 +00:00
Message * msg = memnew_placement ( buffer_end , Message ) ;
2023-01-18 18:14:14 +00:00
msg - > args = p_argcount ;
2020-02-19 19:27:19 +00:00
msg - > callable = p_callable ;
msg - > type = TYPE_CALL ;
2020-05-14 14:41:43 +00:00
if ( p_show_error ) {
2020-02-19 19:27:19 +00:00
msg - > type | = FLAG_SHOW_ERROR ;
2020-05-14 14:41:43 +00:00
}
2023-01-18 21:04:09 +00:00
// Support callables of static methods.
if ( p_callable . get_object_id ( ) . is_null ( ) & & p_callable . is_valid ( ) ) {
msg - > type | = FLAG_NULL_IS_OK ;
}
2020-02-19 19:27:19 +00:00
buffer_end + = sizeof ( Message ) ;
for ( int i = 0 ; i < p_argcount ; i + + ) {
2023-04-11 15:55:06 +00:00
Variant * v = memnew_placement ( buffer_end , Variant ) ;
2020-02-19 19:27:19 +00:00
buffer_end + = sizeof ( Variant ) ;
* v = * p_args [ i ] ;
}
2023-05-10 08:00:33 +00:00
page_bytes [ pages_used - 1 ] + = room_needed ;
2023-04-11 15:55:06 +00:00
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
2020-02-19 19:27:19 +00:00
return OK ;
}
2023-04-11 15:55:06 +00:00
Error CallQueue : : push_set ( ObjectID p_id , const StringName & p_prop , const Variant & p_value ) {
2023-05-10 08:00:33 +00:00
LOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
uint32_t room_needed = sizeof ( Message ) + sizeof ( Variant ) ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
_ensure_first_page ( ) ;
2014-02-10 01:10:30 +00:00
2023-05-10 08:00:33 +00:00
if ( ( page_bytes [ pages_used - 1 ] + room_needed ) > uint32_t ( PAGE_SIZE_BYTES ) ) {
2023-04-11 15:55:06 +00:00
if ( pages_used = = max_pages ) {
String type ;
if ( ObjectDB : : get_instance ( p_id ) ) {
type = ObjectDB : : get_instance ( p_id ) - > get_class ( ) ;
}
2023-11-26 17:53:28 +00:00
fprintf ( stderr , " Failed set: %s: %s target ID: %s. Message queue out of memory. %s \n " , type . utf8 ( ) . get_data ( ) , String ( p_prop ) . utf8 ( ) . get_data ( ) , itos ( p_id ) . utf8 ( ) . get_data ( ) , error_text . utf8 ( ) . get_data ( ) ) ;
2023-04-11 15:55:06 +00:00
statistics ( ) ;
2014-02-10 01:10:30 +00:00
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return ERR_OUT_OF_MEMORY ;
}
_add_page ( ) ;
}
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Page * page = pages [ pages_used - 1 ] ;
2023-05-10 08:00:33 +00:00
uint8_t * buffer_end = & page - > data [ page_bytes [ pages_used - 1 ] ] ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Message * msg = memnew_placement ( buffer_end , Message ) ;
msg - > args = 1 ;
msg - > callable = Callable ( p_id , p_prop ) ;
msg - > type = TYPE_SET ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
buffer_end + = sizeof ( Message ) ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Variant * v = memnew_placement ( buffer_end , Variant ) ;
* v = p_value ;
2014-02-10 01:10:30 +00:00
2023-05-10 08:00:33 +00:00
page_bytes [ pages_used - 1 ] + = room_needed ;
UNLOCK_MUTEX ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
return OK ;
}
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Error CallQueue : : push_notification ( ObjectID p_id , int p_notification ) {
ERR_FAIL_COND_V ( p_notification < 0 , ERR_INVALID_PARAMETER ) ;
2023-05-10 08:00:33 +00:00
LOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
uint32_t room_needed = sizeof ( Message ) ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
_ensure_first_page ( ) ;
2014-02-10 01:10:30 +00:00
2023-05-10 08:00:33 +00:00
if ( ( page_bytes [ pages_used - 1 ] + room_needed ) > uint32_t ( PAGE_SIZE_BYTES ) ) {
2023-04-11 15:55:06 +00:00
if ( pages_used = = max_pages ) {
2024-03-14 19:32:31 +00:00
fprintf ( stderr , " Failed notification: %d target ID: %s. Message queue out of memory. %s \n " , p_notification , itos ( p_id ) . utf8 ( ) . get_data ( ) , error_text . utf8 ( ) . get_data ( ) ) ;
2023-04-11 15:55:06 +00:00
statistics ( ) ;
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return ERR_OUT_OF_MEMORY ;
2020-05-14 14:41:43 +00:00
}
2023-04-11 15:55:06 +00:00
_add_page ( ) ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
Page * page = pages [ pages_used - 1 ] ;
2023-05-10 08:00:33 +00:00
uint8_t * buffer_end = & page - > data [ page_bytes [ pages_used - 1 ] ] ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Message * msg = memnew_placement ( buffer_end , Message ) ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
msg - > type = TYPE_NOTIFICATION ;
msg - > callable = Callable ( p_id , CoreStringNames : : get_singleton ( ) - > notification ) ; //name is meaningless but callable needs it
//msg->target;
msg - > notification = p_notification ;
2014-02-10 01:10:30 +00:00
2023-05-10 08:00:33 +00:00
page_bytes [ pages_used - 1 ] + = room_needed ;
UNLOCK_MUTEX ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
return OK ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
void CallQueue : : _call_function ( const Callable & p_callable , const Variant * p_args , int p_argcount , bool p_show_error ) {
2020-04-01 23:20:12 +00:00
const Variant * * argptrs = nullptr ;
2016-01-04 12:35:21 +00:00
if ( p_argcount ) {
argptrs = ( const Variant * * ) alloca ( sizeof ( Variant * ) * p_argcount ) ;
for ( int i = 0 ; i < p_argcount ; i + + ) {
argptrs [ i ] = & p_args [ i ] ;
}
}
2020-02-19 19:27:19 +00:00
Callable : : CallError ce ;
Variant ret ;
2022-07-28 20:56:41 +00:00
p_callable . callp ( argptrs , p_argcount , ret , ce ) ;
2020-02-19 19:27:19 +00: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 12:35:21 +00:00
}
}
2023-06-23 11:27:34 +00:00
Error CallQueue : : _transfer_messages_to_main_queue ( ) {
if ( pages . size ( ) = = 0 ) {
return OK ;
}
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
CallQueue * mq = MessageQueue : : main_singleton ;
DEV_ASSERT ( ! mq - > allocator_is_custom & & ! allocator_is_custom ) ; // Transferring pages is only safe if using the same alloator parameters.
mq - > mutex . lock ( ) ;
// Here we're transferring the data from this queue to the main one.
// However, it's very unlikely big amounts of messages will be queued here,
// so PagedArray/Pool would be overkill. Also, in most cases the data will fit
// an already existing page of the main queue.
// Let's see if our first (likely only) page fits the current target queue page.
uint32_t src_page = 0 ;
{
if ( mq - > pages_used ) {
uint32_t dst_page = mq - > pages_used - 1 ;
uint32_t dst_offset = mq - > page_bytes [ dst_page ] ;
if ( dst_offset + page_bytes [ 0 ] < uint32_t ( PAGE_SIZE_BYTES ) ) {
memcpy ( mq - > pages [ dst_page ] - > data + dst_offset , pages [ 0 ] - > data , page_bytes [ 0 ] ) ;
mq - > page_bytes [ dst_page ] + = page_bytes [ 0 ] ;
src_page + + ;
2023-05-10 08:00:33 +00:00
}
}
2023-06-23 11:27:34 +00:00
}
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
// Any other possibly existing source page needs to be added.
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
if ( mq - > pages_used + ( pages_used - src_page ) > mq - > max_pages ) {
2024-03-14 19:32:31 +00:00
fprintf ( stderr , " Failed appending thread queue. Message queue out of memory. %s \n " , mq - > error_text . utf8 ( ) . get_data ( ) ) ;
2023-06-23 11:27:34 +00:00
mq - > statistics ( ) ;
mq - > mutex . unlock ( ) ;
return ERR_OUT_OF_MEMORY ;
}
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
for ( ; src_page < pages_used ; src_page + + ) {
mq - > _add_page ( ) ;
memcpy ( mq - > pages [ mq - > pages_used - 1 ] - > data , pages [ src_page ] - > data , page_bytes [ src_page ] ) ;
mq - > page_bytes [ mq - > pages_used - 1 ] = page_bytes [ src_page ] ;
}
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
mq - > mutex . unlock ( ) ;
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
page_bytes [ 0 ] = 0 ;
pages_used = 1 ;
2023-05-10 08:00:33 +00:00
2023-06-23 11:27:34 +00:00
return OK ;
}
Error CallQueue : : flush ( ) {
// Thread overrides are not meant to be flushed, but appended to the main one.
if ( unlikely ( this = = MessageQueue : : thread_singleton ) ) {
return _transfer_messages_to_main_queue ( ) ;
2023-05-10 08:00:33 +00:00
}
2014-02-10 01:10:30 +00:00
2023-06-23 11:27:34 +00:00
LOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
if ( pages . size ( ) = = 0 ) {
// Never allocated
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return OK ; // Do nothing.
}
2015-06-07 01:06:58 +00:00
2020-03-02 18:17:20 +00:00
if ( flushing ) {
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return ERR_BUSY ;
2020-03-02 18:17:20 +00:00
}
2023-04-11 15:55:06 +00:00
2019-01-14 13:59:28 +00:00
flushing = true ;
2023-04-11 15:55:06 +00:00
uint32_t i = 0 ;
uint32_t offset = 0 ;
2023-05-10 08:00:33 +00:00
while ( i < pages_used & & offset < page_bytes [ i ] ) {
2023-04-11 15:55:06 +00:00
Page * page = pages [ i ] ;
2018-02-21 16:30:55 +00:00
//lock on each iteration, so a call can re-add itself to the message queue
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Message * message = ( Message * ) & page - > data [ offset ] ;
2014-02-10 01:10:30 +00:00
2017-05-18 11:01:12 +00:00
uint32_t advance = sizeof ( Message ) ;
2020-05-14 14:41:43 +00:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
2017-05-18 11:01:12 +00:00
advance + = sizeof ( Variant ) * message - > args ;
2020-05-14 14:41:43 +00:00
}
2017-05-18 11:01:12 +00:00
//pre-advance so this function is reentrant
2023-04-11 15:55:06 +00:00
offset + = advance ;
2017-05-18 11:01:12 +00:00
2020-02-19 19:27:19 +00:00
Object * target = message - > callable . get_object ( ) ;
2014-02-10 01:10:30 +00:00
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-07-31 08:45:20 +00:00
switch ( message - > type & FLAG_MASK ) {
case TYPE_CALL : {
if ( target | | ( message - > type & FLAG_NULL_IS_OK ) ) {
Variant * args = ( Variant * ) ( message + 1 ) ;
_call_function ( message - > callable , args , message - > args , message - > type & FLAG_SHOW_ERROR ) ;
}
} break ;
case TYPE_NOTIFICATION : {
if ( target ) {
target - > notification ( message - > notification ) ;
}
} break ;
case TYPE_SET : {
if ( target ) {
Variant * arg = ( Variant * ) ( message + 1 ) ;
target - > set ( message - > callable . get_method ( ) , * arg ) ;
}
} break ;
2014-02-10 01:10:30 +00:00
}
2019-05-19 13:05:15 +00:00
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
Variant * args = ( Variant * ) ( message + 1 ) ;
2023-04-11 15:55:06 +00:00
for ( int k = 0 ; k < message - > args ; k + + ) {
args [ k ] . ~ Variant ( ) ;
2019-05-19 13:05:15 +00:00
}
}
2014-04-05 15:39:30 +00:00
message - > ~ Message ( ) ;
2023-05-10 08:00:33 +00:00
LOCK_MUTEX ;
if ( offset = = page_bytes [ i ] ) {
2023-04-11 15:55:06 +00:00
i + + ;
offset = 0 ;
}
2014-02-10 01:10:30 +00:00
}
2023-05-10 08:00:33 +00:00
page_bytes [ 0 ] = 0 ;
2023-04-11 15:55:06 +00:00
pages_used = 1 ;
2019-01-14 13:59:28 +00:00
flushing = false ;
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return OK ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
void CallQueue : : clear ( ) {
2023-05-10 08:00:33 +00:00
LOCK_MUTEX ;
2019-01-14 13:59:28 +00:00
2023-04-11 15:55:06 +00:00
if ( pages . size ( ) = = 0 ) {
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
return ; // Nothing to clear.
}
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
for ( uint32_t i = 0 ; i < pages_used ; i + + ) {
uint32_t offset = 0 ;
2023-05-10 08:00:33 +00:00
while ( offset < page_bytes [ i ] ) {
2023-04-11 15:55:06 +00:00
Page * page = pages [ i ] ;
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
//lock on each iteration, so a call can re-add itself to the message queue
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
Message * message = ( Message * ) & page - > data [ offset ] ;
uint32_t advance = sizeof ( Message ) ;
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
advance + = sizeof ( Variant ) * message - > args ;
2020-05-14 14:41:43 +00:00
}
2023-04-11 15:55:06 +00:00
offset + = advance ;
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
Variant * args = ( Variant * ) ( message + 1 ) ;
for ( int k = 0 ; k < message - > args ; k + + ) {
args [ k ] . ~ Variant ( ) ;
}
}
message - > ~ Message ( ) ;
2014-02-10 01:10:30 +00:00
}
2023-04-11 15:55:06 +00:00
}
2014-02-10 01:10:30 +00:00
2023-04-11 15:55:06 +00:00
pages_used = 1 ;
2023-05-10 08:00:33 +00:00
page_bytes [ 0 ] = 0 ;
2023-04-11 15:55:06 +00:00
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
}
void CallQueue : : statistics ( ) {
2023-05-10 08:00:33 +00:00
LOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
HashMap < StringName , int > set_count ;
HashMap < int , int > notify_count ;
HashMap < Callable , int > call_count ;
int null_count = 0 ;
for ( uint32_t i = 0 ; i < pages_used ; i + + ) {
uint32_t offset = 0 ;
2023-05-10 08:00:33 +00:00
while ( offset < page_bytes [ i ] ) {
2023-04-11 15:55:06 +00:00
Page * page = pages [ i ] ;
//lock on each iteration, so a call can re-add itself to the message queue
Message * message = ( Message * ) & page - > data [ offset ] ;
uint32_t advance = sizeof ( Message ) ;
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
advance + = sizeof ( Variant ) * message - > args ;
}
Object * target = message - > callable . get_object ( ) ;
2023-01-18 21:04:09 +00:00
bool null_target = true ;
switch ( message - > type & FLAG_MASK ) {
case TYPE_CALL : {
if ( target | | ( message - > type & FLAG_NULL_IS_OK ) ) {
2023-04-11 15:55:06 +00:00
if ( ! call_count . has ( message - > callable ) ) {
call_count [ message - > callable ] = 0 ;
}
call_count [ message - > callable ] + + ;
2023-01-18 21:04:09 +00:00
null_target = false ;
}
} break ;
case TYPE_NOTIFICATION : {
if ( target ) {
2023-04-11 15:55:06 +00:00
if ( ! notify_count . has ( message - > notification ) ) {
notify_count [ message - > notification ] = 0 ;
}
notify_count [ message - > notification ] + + ;
2023-01-18 21:04:09 +00:00
null_target = false ;
}
} break ;
case TYPE_SET : {
if ( target ) {
2023-04-11 15:55:06 +00:00
StringName t = message - > callable . get_method ( ) ;
if ( ! set_count . has ( t ) ) {
set_count [ t ] = 0 ;
}
set_count [ t ] + + ;
2023-01-18 21:04:09 +00:00
null_target = false ;
}
} break ;
}
if ( null_target ) {
2024-03-14 19:32:31 +00:00
// Object was deleted.
fprintf ( stdout , " Object was deleted while awaiting a callback. \n " ) ;
2023-04-11 15:55:06 +00:00
null_count + + ;
}
offset + = advance ;
if ( ( message - > type & FLAG_MASK ) ! = TYPE_NOTIFICATION ) {
Variant * args = ( Variant * ) ( message + 1 ) ;
for ( int k = 0 ; k < message - > args ; k + + ) {
args [ k ] . ~ Variant ( ) ;
}
}
message - > ~ Message ( ) ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
2024-03-14 19:32:31 +00:00
fprintf ( stdout , " TOTAL PAGES: %d (%d bytes). \n " , pages_used , pages_used * PAGE_SIZE_BYTES ) ;
fprintf ( stdout , " NULL count: %d. \n " , null_count ) ;
2023-04-11 15:55:06 +00:00
for ( const KeyValue < StringName , int > & E : set_count ) {
2024-03-14 19:32:31 +00:00
fprintf ( stdout , " SET %s: %d. \n " , String ( E . key ) . utf8 ( ) . get_data ( ) , E . value ) ;
2023-04-11 15:55:06 +00:00
}
for ( const KeyValue < Callable , int > & E : call_count ) {
2024-03-14 19:32:31 +00:00
fprintf ( stdout , " CALL %s: %d. \n " , String ( E . key ) . utf8 ( ) . get_data ( ) , E . value ) ;
2023-04-11 15:55:06 +00:00
}
for ( const KeyValue < int , int > & E : notify_count ) {
2024-03-14 19:32:31 +00:00
fprintf ( stdout , " NOTIFY %d: %d. \n " , E . key , E . value ) ;
2023-04-11 15:55:06 +00:00
}
2023-05-10 08:00:33 +00:00
UNLOCK_MUTEX ;
2023-04-11 15:55:06 +00:00
}
bool CallQueue : : is_flushing ( ) const {
return flushing ;
}
2023-04-10 16:45:53 +00:00
bool CallQueue : : has_messages ( ) const {
if ( pages_used = = 0 ) {
return false ;
}
2023-05-10 08:00:33 +00:00
if ( pages_used = = 1 & & page_bytes [ 0 ] = = 0 ) {
2023-04-10 16:45:53 +00:00
return false ;
}
return true ;
}
2023-04-11 15:55:06 +00:00
int CallQueue : : get_max_buffer_usage ( ) const {
return pages . size ( ) * PAGE_SIZE_BYTES ;
}
CallQueue : : CallQueue ( Allocator * p_custom_allocator , uint32_t p_max_pages , const String & p_error_text ) {
if ( p_custom_allocator ) {
allocator = p_custom_allocator ;
allocator_is_custom = true ;
} else {
allocator = memnew ( Allocator ( 16 ) ) ; // 16 elements per allocator page, 64kb per allocator page. Anything small will do, though.
allocator_is_custom = false ;
}
max_pages = p_max_pages ;
error_text = p_error_text ;
}
CallQueue : : ~ CallQueue ( ) {
clear ( ) ;
// Let go of pages.
for ( uint32_t i = 0 ; i < pages . size ( ) ; i + + ) {
allocator - > free ( pages [ i ] ) ;
}
if ( ! allocator_is_custom ) {
memdelete ( allocator ) ;
}
2023-09-28 13:42:55 +00:00
// This is done here to avoid a circular dependency between the safety checks and the thread singleton pointer.
2023-05-19 08:46:49 +00:00
if ( this = = MessageQueue : : thread_singleton ) {
MessageQueue : : thread_singleton = nullptr ;
}
2023-04-11 15:55:06 +00:00
}
//////////////////////
2023-05-10 08:00:33 +00:00
CallQueue * MessageQueue : : main_singleton = nullptr ;
thread_local CallQueue * MessageQueue : : thread_singleton = nullptr ;
void MessageQueue : : set_thread_singleton_override ( CallQueue * p_thread_singleton ) {
2023-05-19 08:46:49 +00:00
DEV_ASSERT ( p_thread_singleton ) ; // To unset the thread singleton, don't call this with nullptr, but just memfree() it.
# ifdef DEV_ENABLED
if ( thread_singleton ) {
thread_singleton - > is_current_thread_override = false ;
}
# endif
2023-05-10 08:00:33 +00:00
thread_singleton = p_thread_singleton ;
2023-05-19 08:46:49 +00:00
# ifdef DEV_ENABLED
if ( thread_singleton ) {
thread_singleton - > is_current_thread_override = true ;
}
# endif
2023-05-10 08:00:33 +00:00
}
2023-04-11 15:55:06 +00:00
MessageQueue : : MessageQueue ( ) :
CallQueue ( nullptr ,
int ( GLOBAL_DEF_RST ( PropertyInfo ( Variant : : INT , " memory/limits/message_queue/max_size_mb " , PROPERTY_HINT_RANGE , " 1,512,1,or_greater " ) , 32 ) ) * 1024 * 1024 / PAGE_SIZE_BYTES ,
" Message queue out of memory. Try increasing 'memory/limits/message_queue/max_size_mb' in project settings. " ) {
2023-05-10 08:00:33 +00:00
ERR_FAIL_COND_MSG ( main_singleton ! = nullptr , " A MessageQueue singleton already exists. " ) ;
main_singleton = this ;
2023-04-11 15:55:06 +00:00
}
MessageQueue : : ~ MessageQueue ( ) {
2023-05-10 08:00:33 +00:00
main_singleton = nullptr ;
2014-02-10 01:10:30 +00:00
}