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,17 @@ 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+
653+ if (rcutils_rwlock_init (& g_rcutils_logging_map_lock , allocator ) != RCUTILS_RET_OK ) {
654+ RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
655+ "Failed to initialize rwlock for logger severities [%s]." , rcutils_get_error_string ().str );
656+ if (rcutils_hash_map_fini (& g_rcutils_logging_severities_map ) != RCUTILS_RET_OK ) {
657+ RCUTILS_SAFE_FWRITE_TO_STDERR ("Additionally, failed to free memory when cleaning up\n" );
658+ }
659+ return RCUTILS_RET_ERROR ;
660+ }
661+
648662 parse_and_create_handlers_list ();
649663
650664 g_rcutils_logging_severities_map_valid = true;
@@ -665,6 +679,7 @@ rcutils_ret_t rcutils_logging_shutdown(void)
665679 // Iterate over the map, getting every key so we can free it
666680 char * key = NULL ;
667681 int level ;
682+ rcutils_rwlock_write_lock (& g_rcutils_logging_map_lock );
668683 rcutils_ret_t hash_map_ret = rcutils_hash_map_get_next_key_and_data (
669684 & g_rcutils_logging_severities_map , NULL , & key , & level );
670685 while (RCUTILS_RET_OK == hash_map_ret ) {
@@ -688,6 +703,9 @@ rcutils_ret_t rcutils_logging_shutdown(void)
688703 rcutils_get_error_string ().str );
689704 ret = RCUTILS_RET_LOGGING_SEVERITY_MAP_INVALID ;
690705 }
706+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
707+ rcutils_rwlock_fini (& g_rcutils_logging_map_lock );
708+
691709 g_rcutils_logging_severities_map_valid = false;
692710 }
693711 g_num_log_msg_handlers = 0 ;
@@ -775,6 +793,18 @@ static rcutils_ret_t add_key_to_hash_map(const char * name, int level, bool set_
775793 // Check if key already exists, to avoid extra memory allocation
776794 // If the key already exists, then rcutils_hash_map_set will not maintain the key we give it,
777795 // so we do not need to copy the name
796+ if (set_by_user ) {
797+ rcutils_rwlock_write_lock (& g_rcutils_logging_map_lock );
798+ } else {
799+ // In the specific case where this is not being set by the user, we know
800+ // that this is a cache set. Just try to get the lock here; if we fail,
801+ // the worst case is that we don't cache this in the map and we'll try
802+ // again next time.
803+ rcutils_ret_t lockret = rcutils_rwlock_write_trylock (& g_rcutils_logging_map_lock );
804+ if (lockret != RCUTILS_RET_OK ) {
805+ return RCUTILS_RET_OK ;
806+ }
807+ }
778808 bool already_exists = rcutils_hash_map_key_exists (& g_rcutils_logging_severities_map , & copy_name );
779809
780810 if (!already_exists ) {
@@ -783,6 +813,7 @@ static rcutils_ret_t add_key_to_hash_map(const char * name, int level, bool set_
783813 if (copy_name == NULL ) {
784814 // Don't report an error to the error handling machinery; some uses of this function are for
785815 // caching so this is not necessarily fatal.
816+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
786817 return RCUTILS_RET_ERROR ;
787818 }
788819 }
@@ -800,16 +831,21 @@ static rcutils_ret_t add_key_to_hash_map(const char * name, int level, bool set_
800831 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
801832 "Error setting severity level for logger named '%s': %s" ,
802833 name , rcutils_get_error_string ().str );
834+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
803835 return RCUTILS_RET_ERROR ;
804836 }
805837
838+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
839+
806840 return RCUTILS_RET_OK ;
807841}
808842
809843static rcutils_ret_t get_severity_level (const char * name , int * severity )
810844{
845+ rcutils_rwlock_read_lock (& g_rcutils_logging_map_lock );
811846 rcutils_ret_t ret =
812847 rcutils_hash_map_get (& g_rcutils_logging_severities_map , & name , severity );
848+ rcutils_rwlock_read_unlock (& g_rcutils_logging_map_lock );
813849 if (ret != RCUTILS_RET_OK ) {
814850 // One possible response is RCUTILS_RET_NOT_FOUND, but the higher layers may be OK with that.
815851 return ret ;
@@ -845,7 +881,9 @@ int rcutils_logging_get_logger_leveln(const char * name, size_t name_length)
845881 }
846882
847883 int severity ;
884+ rcutils_rwlock_read_lock (& g_rcutils_logging_map_lock );
848885 rcutils_ret_t ret = get_severity_level (short_name , & severity );
886+ rcutils_rwlock_read_unlock (& g_rcutils_logging_map_lock );
849887 g_rcutils_logging_allocator .deallocate (short_name , g_rcutils_logging_allocator .state );
850888 if (ret != RCUTILS_RET_OK ) {
851889 // The error message was already set by get_severity_level
@@ -863,8 +901,10 @@ int rcutils_logging_get_logger_effective_level(const char * name)
863901 }
864902
865903 size_t hash_map_size ;
904+ rcutils_rwlock_read_lock (& g_rcutils_logging_map_lock );
866905 rcutils_ret_t hash_map_ret = rcutils_hash_map_get_size (
867906 & g_rcutils_logging_severities_map , & hash_map_size );
907+ rcutils_rwlock_read_unlock (& g_rcutils_logging_map_lock );
868908 if (hash_map_ret != RCUTILS_RET_OK ) {
869909 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
870910 "Error getting severity level for logger named '%s': %s" ,
@@ -943,20 +983,18 @@ int rcutils_logging_get_logger_effective_level(const char * name)
943983 severity = g_rcutils_logging_default_logger_level ;
944984 }
945985
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- // }
986+ // If the calculated severity is anything but UNSET, we place it into the hashmap for speedier
987+ // lookup next time. If the severity is UNSET, we don't bother because we are going to have to
988+ // walk the hierarchy next time anyway.
989+ if (severity != RCUTILS_LOG_SEVERITY_UNSET ) {
990+ ret = add_key_to_hash_map (name , severity , false);
991+ if (ret != RCUTILS_RET_OK ) {
992+ // Continue on if we failed to add the key to the hashmap.
993+ // This will affect performance but is not fatal.
994+ RCUTILS_SAFE_FWRITE_TO_STDERR (
995+ "Failed to cache severity; this is not fatal but will impact performance\n" );
996+ }
997+ }
960998
961999 return severity ;
9621000}
@@ -990,6 +1028,13 @@ rcutils_ret_t rcutils_logging_set_logger_level(const char * name, int level)
9901028
9911029 size_t name_length = strlen (name );
9921030
1031+ // TODO(clalancette): We could *almost* make this a read lock, except for the fact that the loop
1032+ // below might unset keys. If we find that this write lock is a bottleneck, we could split
1033+ // that unsetting of keys into a separate function which acquires the write lock itself.
1034+ // However, don't forget that when unlocking as read and relocking as write, you have to
1035+ // double-check that the thing you were operating on still exists (since it may have been
1036+ // changed in the meantime).
1037+ rcutils_rwlock_write_lock (& g_rcutils_logging_map_lock );
9931038 if (rcutils_hash_map_key_exists (& g_rcutils_logging_severities_map , & name )) {
9941039 // Iterate over the entire contents of the severities map, looking for entries that start with
9951040 // this key name. For any ones that match, check whether the user explicitly set them. If the
@@ -1029,6 +1074,7 @@ rcutils_ret_t rcutils_logging_set_logger_level(const char * name, int level)
10291074 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
10301075 "Error accessing hash map when setting logger level for '%s': %s" ,
10311076 name , rcutils_get_error_string ().str );
1077+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
10321078 return hash_map_ret ;
10331079 }
10341080
@@ -1042,12 +1088,14 @@ rcutils_ret_t rcutils_logging_set_logger_level(const char * name, int level)
10421088 RCUTILS_SET_ERROR_MSG_WITH_FORMAT_STRING (
10431089 "Error clearing old severity level for logger named '%s': %s" ,
10441090 name , rcutils_get_error_string ().str );
1091+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
10451092 return unset_ret ;
10461093 }
10471094 g_rcutils_logging_allocator .deallocate (previous_key , g_rcutils_logging_allocator .state );
10481095 }
10491096 }
10501097 }
1098+ rcutils_rwlock_write_unlock (& g_rcutils_logging_map_lock );
10511099
10521100 rcutils_ret_t add_key_ret = add_key_to_hash_map (name , level , true);
10531101 if (add_key_ret != RCUTILS_RET_OK ) {
0 commit comments