4949#include "rcutils/time.h"
5050#include "rcutils/types/hash_map.h"
5151
52+ #include "./rwlock.h"
53+
5254
5355#define RCUTILS_LOGGING_SEPARATOR_CHAR '.'
5456
@@ -90,6 +92,7 @@ static const char * g_rcutils_logging_default_output_format =
9092static rcutils_allocator_t g_rcutils_logging_allocator ;
9193
9294static rcutils_logging_output_handler_t g_rcutils_logging_output_handler = NULL ;
95+ static rcutils_rwlock_t g_rcutils_logging_map_lock ;
9396static rcutils_hash_map_t g_rcutils_logging_severities_map ;
9497
9598// If this is false, attempts to use the severities map will be skipped.
@@ -645,6 +648,10 @@ rcutils_ret_t rcutils_logging_initialize_with_allocator(rcutils_allocator_t allo
645648 return RCUTILS_RET_ERROR ;
646649 }
647650
651+ g_rcutils_logging_map_lock = rcutils_get_zero_initialized_rwlock ();
652+ // TODO(clalancette): Error checking
653+ rcutils_rwlock_init (& g_rcutils_logging_map_lock , allocator );
654+
648655 parse_and_create_handlers_list ();
649656
650657 g_rcutils_logging_severities_map_valid = true;
@@ -665,6 +672,7 @@ rcutils_ret_t rcutils_logging_shutdown(void)
665672 // Iterate over the map, getting every key so we can free it
666673 char * key = NULL ;
667674 int level ;
675+ rcutils_rwlock_write_lock (& g_rcutils_logging_map_lock );
668676 rcutils_ret_t hash_map_ret = rcutils_hash_map_get_next_key_and_data (
669677 & g_rcutils_logging_severities_map , NULL , & key , & level );
670678 while (RCUTILS_RET_OK == hash_map_ret ) {
@@ -688,6 +696,9 @@ rcutils_ret_t rcutils_logging_shutdown(void)
688696 rcutils_get_error_string ().str );
689697 ret = RCUTILS_RET_LOGGING_SEVERITY_MAP_INVALID ;
690698 }
699+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
700+ rcutils_rwlock_fini (& g_rcutils_logging_map_lock );
701+
691702 g_rcutils_logging_severities_map_valid = false;
692703 }
693704 g_num_log_msg_handlers = 0 ;
@@ -775,6 +786,18 @@ static rcutils_ret_t add_key_to_hash_map(const char * name, int level, bool set_
775786 // Check if key already exists, to avoid extra memory allocation
776787 // If the key already exists, then rcutils_hash_map_set will not maintain the key we give it,
777788 // so we do not need to copy the name
789+ if (set_by_user ) {
790+ rcutils_rwlock_write_lock (& g_rcutils_logging_map_lock );
791+ } else {
792+ // In the specific case where this is not being set by the user, we know
793+ // that this is a cache set. Just try to get the lock here; if we fail,
794+ // the worst case is that we don't cache this in the map and we'll try
795+ // again next time.
796+ rcutils_ret_t lockret = rcutils_rwlock_write_trylock (& g_rcutils_logging_map_lock );
797+ if (lockret != RCUTILS_RET_OK ) {
798+ return RCUTILS_RET_OK ;
799+ }
800+ }
778801 bool already_exists = rcutils_hash_map_key_exists (& g_rcutils_logging_severities_map , & copy_name );
779802
780803 if (!already_exists ) {
@@ -783,6 +806,7 @@ static rcutils_ret_t add_key_to_hash_map(const char * name, int level, bool set_
783806 if (copy_name == NULL ) {
784807 // Don't report an error to the error handling machinery; some uses of this function are for
785808 // caching so this is not necessarily fatal.
809+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
786810 return RCUTILS_RET_ERROR ;
787811 }
788812 }
@@ -800,16 +824,21 @@ static rcutils_ret_t add_key_to_hash_map(const char * name, int level, bool set_
800824 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
801825 "Error setting severity level for logger named '%s': %s" ,
802826 name , rcutils_get_error_string ().str );
827+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
803828 return RCUTILS_RET_ERROR ;
804829 }
805830
831+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
832+
806833 return RCUTILS_RET_OK ;
807834}
808835
809836static rcutils_ret_t get_severity_level (const char * name , int * severity )
810837{
838+ rcutils_rwlock_read_lock (& g_rcutils_logging_map_lock );
811839 rcutils_ret_t ret =
812840 rcutils_hash_map_get (& g_rcutils_logging_severities_map , & name , severity );
841+ rcutils_rwlock_read_unlock (& g_rcutils_logging_map_lock );
813842 if (ret != RCUTILS_RET_OK ) {
814843 // One possible response is RCUTILS_RET_NOT_FOUND, but the higher layers may be OK with that.
815844 return ret ;
@@ -845,7 +874,9 @@ int rcutils_logging_get_logger_leveln(const char * name, size_t name_length)
845874 }
846875
847876 int severity ;
877+ rcutils_rwlock_read_lock (& g_rcutils_logging_map_lock );
848878 rcutils_ret_t ret = get_severity_level (short_name , & severity );
879+ rcutils_rwlock_read_unlock (& g_rcutils_logging_map_lock );
849880 g_rcutils_logging_allocator .deallocate (short_name , g_rcutils_logging_allocator .state );
850881 if (ret != RCUTILS_RET_OK ) {
851882 // The error message was already set by get_severity_level
@@ -863,8 +894,10 @@ int rcutils_logging_get_logger_effective_level(const char * name)
863894 }
864895
865896 size_t hash_map_size ;
897+ rcutils_rwlock_read_lock (& g_rcutils_logging_map_lock );
866898 rcutils_ret_t hash_map_ret = rcutils_hash_map_get_size (
867899 & g_rcutils_logging_severities_map , & hash_map_size );
900+ rcutils_rwlock_read_unlock (& g_rcutils_logging_map_lock );
868901 if (hash_map_ret != RCUTILS_RET_OK ) {
869902 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
870903 "Error getting severity level for logger named '%s': %s" ,
@@ -943,20 +976,18 @@ int rcutils_logging_get_logger_effective_level(const char * name)
943976 severity = g_rcutils_logging_default_logger_level ;
944977 }
945978
946- // TODO(wjwwood): restore or replace this optimization when thread-safety is addressed
947- // see: https://github.com/ros2/rcutils/pull/393
948- // // If the calculated severity is anything but UNSET, we place it into the hashmap for speedier
949- // // lookup next time. If the severity is UNSET, we don't bother because we are going to have to
950- // // walk the hierarchy next time anyway.
951- // if (severity != RCUTILS_LOG_SEVERITY_UNSET) {
952- // ret = add_key_to_hash_map(name, severity, false);
953- // if (ret != RCUTILS_RET_OK) {
954- // // Continue on if we failed to add the key to the hashmap.
955- // // This will affect performance but is not fatal.
956- // RCUTILS_SAFE_FWRITE_TO_STDERR(
957- // "Failed to cache severity; this is not fatal but will impact performance\n");
958- // }
959- // }
979+ // If the calculated severity is anything but UNSET, we place it into the hashmap for speedier
980+ // lookup next time. If the severity is UNSET, we don't bother because we are going to have to
981+ // walk the hierarchy next time anyway.
982+ if (severity != RCUTILS_LOG_SEVERITY_UNSET ) {
983+ ret = add_key_to_hash_map (name , severity , false);
984+ if (ret != RCUTILS_RET_OK ) {
985+ // Continue on if we failed to add the key to the hashmap.
986+ // This will affect performance but is not fatal.
987+ RCUTILS_SAFE_FWRITE_TO_STDERR (
988+ "Failed to cache severity; this is not fatal but will impact performance\n" );
989+ }
990+ }
960991
961992 return severity ;
962993}
@@ -990,6 +1021,13 @@ rcutils_ret_t rcutils_logging_set_logger_level(const char * name, int level)
9901021
9911022 size_t name_length = strlen (name );
9921023
1024+ // TODO(clalancette): We could *almost* make this a read lock, except for the fact that the loop
1025+ // below might unset keys. If we find that this write lock is a bottleneck, we could split
1026+ // that unsetting of keys into a separate function which acquires the write lock itself.
1027+ // However, don't forget that when unlocking as read and relocking as write, you have to
1028+ // double-check that the thing you were operating on still exists (since it may have been
1029+ // changed in the meantime).
1030+ rcutils_rwlock_write_lock (& g_rcutils_logging_map_lock );
9931031 if (rcutils_hash_map_key_exists (& g_rcutils_logging_severities_map , & name )) {
9941032 // Iterate over the entire contents of the severities map, looking for entries that start with
9951033 // this key name. For any ones that match, check whether the user explicitly set them. If the
@@ -1029,6 +1067,7 @@ rcutils_ret_t rcutils_logging_set_logger_level(const char * name, int level)
10291067 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
10301068 "Error accessing hash map when setting logger level for '%s': %s" ,
10311069 name , rcutils_get_error_string ().str );
1070+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
10321071 return hash_map_ret ;
10331072 }
10341073
@@ -1042,12 +1081,14 @@ rcutils_ret_t rcutils_logging_set_logger_level(const char * name, int level)
10421081 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
10431082 "Error clearing old severity level for logger named '%s': %s" ,
10441083 name , rcutils_get_error_string ().str );
1084+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
10451085 return unset_ret ;
10461086 }
10471087 g_rcutils_logging_allocator .deallocate (previous_key , g_rcutils_logging_allocator .state );
10481088 }
10491089 }
10501090 }
1091+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
10511092
10521093 rcutils_ret_t add_key_ret = add_key_to_hash_map (name , level , true);
10531094 if (add_key_ret != RCUTILS_RET_OK ) {
0 commit comments