| ... | ...@@ -44,7 +44,8 @@ |
| 44 | 44 | #if !defined HAVE_ATOLL && defined HAVE_STRTOLL |
| 45 | 45 | #define atoll(nptr) strtoll(nptr, (char **)NULL, 10) |
| 46 | 46 | #endif |
| 47 | | static void stats_global_calc (void); |
| 47 | |
| 48 | #define VAL_BUFSIZE 20 |
| 48 | 49 | |
| 49 | 50 | #define STATS_EVENT_SET 0 |
| 50 | 51 | #define STATS_EVENT_INC 1 |
| ... | ...@@ -54,6 +55,31 @@ |
| 54 | 55 | #define STATS_EVENT_REMOVE 5 |
| 55 | 56 | #define STATS_EVENT_HIDDEN 6 |
| 56 | 57 | |
| 58 | typedef struct _stats_node_tag |
| 59 | { |
| 60 | char *name; |
| 61 | char *value; |
| 62 | int hidden; |
| 63 | } stats_node_t; |
| 64 | |
| 65 | typedef struct _stats_event_tag |
| 66 | { |
| 67 | char *source; |
| 68 | char *name; |
| 69 | char *value; |
| 70 | int hidden; |
| 71 | int action; |
| 72 | |
| 73 | struct _stats_event_tag *next; |
| 74 | } stats_event_t; |
| 75 | |
| 76 | typedef struct _stats_source_tag |
| 77 | { |
| 78 | char *source; |
| 79 | int hidden; |
| 80 | avl_tree *stats_tree; |
| 81 | } stats_source_t; |
| 82 | |
| 57 | 83 | typedef struct _event_queue_tag |
| 58 | 84 | { |
| 59 | 85 | volatile stats_event_t *head; |
| ... | ...@@ -73,19 +99,14 @@ |
| 73 | 99 | } event_listener_t; |
| 74 | 100 | |
| 75 | 101 | static volatile int _stats_running = 0; |
| 76 | | static thread_type *_stats_thread_id; |
| 77 | 102 | static volatile int _stats_threads = 0; |
| 78 | 103 | |
| 79 | 104 | static stats_t _stats; |
| 80 | 105 | static mutex_t _stats_mutex; |
| 81 | 106 | |
| 82 | | static event_queue_t _global_event_queue; |
| 83 | | spin_t _global_event_mutex; |
| 84 | | |
| 85 | 107 | static volatile event_listener_t *_event_listeners; |
| 86 | 108 | |
| 87 | 109 | |
| 88 | | static void *_stats_thread(void *arg); |
| 89 | 110 | static int _compare_stats(void *a, void *b, void *arg); |
| 90 | 111 | static int _compare_source_stats(void *a, void *b, void *arg); |
| 91 | 112 | static int _free_stats(void *key); |
| ... | ...@@ -95,34 +116,21 @@ |
| 95 | 116 | static stats_source_t *_find_source(avl_tree *tree, const char *source); |
| 96 | 117 | static void _free_event(stats_event_t *event); |
| 97 | 118 | static stats_event_t *_get_event_from_queue (event_queue_t *queue); |
| 119 | static void process_event (stats_event_t *event); |
| 98 | 120 | |
| 99 | 121 | |
| 100 | 122 | /* simple helper function for creating an event */ |
| 101 | | static stats_event_t *build_event (const char *source, const char *name, const char *value) |
| 123 | static void build_event (stats_event_t *event, const char *source, const char *name, const char *value) |
| 102 | 124 | { |
| 103 | | stats_event_t *event; |
| 104 | | |
| 105 | | event = (stats_event_t *)calloc(1, sizeof(stats_event_t)); |
| 106 | | if (event) |
| 107 | | { |
| 108 | | if (source) |
| 109 | | event->source = (char *)strdup(source); |
| 110 | | if (name) |
| 111 | | event->name = (char *)strdup(name); |
| 112 | | if (value) |
| 113 | | event->value = (char *)strdup(value); |
| 114 | | else |
| 115 | | event->action = STATS_EVENT_REMOVE; |
| 116 | | } |
| 117 | | return event; |
| 125 | event->source = (char *)source; |
| 126 | event->name = (char *)name; |
| 127 | event->value = (char *)value; |
| 128 | if (value) |
| 129 | event->action = 0; |
| 130 | else |
| 131 | event->action = STATS_EVENT_REMOVE; |
| 118 | 132 | } |
| 119 | 133 | |
| 120 | | static void queue_global_event (stats_event_t *event) |
| 121 | | { |
| 122 | | thread_spin_lock(&_global_event_mutex); |
| 123 | | _add_event_to_queue (event, &_global_event_queue); |
| 124 | | thread_spin_unlock(&_global_event_mutex); |
| 125 | | } |
| 126 | 134 | |
| 127 | 135 | void stats_initialize(void) |
| 128 | 136 | { |
| ... | ...@@ -135,13 +143,26 @@ |
| 135 | 143 | /* set up global mutex */ |
| 136 | 144 | thread_mutex_create("stats", &_stats_mutex); |
| 137 | 145 | |
| 138 | | /* set up stats queues */ |
| 139 | | event_queue_init (&_global_event_queue); |
| 140 | | thread_spin_create("stats_global_event", &_global_event_mutex); |
| 141 | | |
| 142 | 146 | /* fire off the stats thread */ |
| 143 | 147 | _stats_running = 1; |
| 144 | | _stats_thread_id = thread_create("Stats Thread", _stats_thread, NULL, THREAD_ATTACHED); |
| 148 | |
| 149 | stats_event_time (NULL, "server_start"); |
| 150 | |
| 151 | /* global currently active stats */ |
| 152 | stats_event (NULL, "clients", "0"); |
| 153 | stats_event (NULL, "connections", "0"); |
| 154 | stats_event (NULL, "sources", "0"); |
| 155 | stats_event (NULL, "stats", "0"); |
| 156 | stats_event (NULL, "listeners", "0"); |
| 157 | |
| 158 | /* global accumulating stats */ |
| 159 | stats_event (NULL, "client_connections", "0"); |
| 160 | stats_event (NULL, "source_client_connections", "0"); |
| 161 | stats_event (NULL, "source_relay_connections", "0"); |
| 162 | stats_event (NULL, "source_total_connections", "0"); |
| 163 | stats_event (NULL, "stats_connections", "0"); |
| 164 | stats_event (NULL, "listener_connections", "0"); |
| 165 | stats_event (NULL, "outgoing_kbitrate", "0"); |
| 145 | 166 | } |
| 146 | 167 | |
| 147 | 168 | void stats_shutdown(void) |
| ... | ...@@ -153,7 +174,6 @@ |
| 153 | 174 | |
| 154 | 175 | /* wait for thread to exit */ |
| 155 | 176 | _stats_running = 0; |
| 156 | | thread_join(_stats_thread_id); |
| 157 | 177 | |
| 158 | 178 | /* wait for other threads to shut down */ |
| 159 | 179 | do { |
| ... | ...@@ -166,25 +186,9 @@ |
| 166 | 186 | |
| 167 | 187 | /* free the queues */ |
| 168 | 188 | |
| 169 | | /* destroy the queue mutexes */ |
| 170 | | thread_spin_destroy(&_global_event_mutex); |
| 171 | | |
| 172 | 189 | thread_mutex_destroy(&_stats_mutex); |
| 173 | 190 | avl_tree_free(_stats.source_tree, _free_source_stats); |
| 174 | 191 | avl_tree_free(_stats.global_tree, _free_stats); |
| 175 | | |
| 176 | | while (1) |
| 177 | | { |
| 178 | | stats_event_t *event = _get_event_from_queue (&_global_event_queue); |
| 179 | | if (event == NULL) break; |
| 180 | | if(event->source) |
| 181 | | free(event->source); |
| 182 | | if(event->value) |
| 183 | | free(event->value); |
| 184 | | if(event->name) |
| 185 | | free(event->name); |
| 186 | | free(event); |
| 187 | | } |
| 188 | 192 | } |
| 189 | 193 | |
| 190 | 194 | stats_t *stats_get_stats(void) |
| ... | ...@@ -203,16 +207,15 @@ |
| 203 | 207 | /* simple name=tag stat create/update */ |
| 204 | 208 | void stats_event(const char *source, const char *name, const char *value) |
| 205 | 209 | { |
| 206 | | stats_event_t *event; |
| 210 | stats_event_t event; |
| 207 | 211 | |
| 208 | 212 | if (value && xmlCheckUTF8 ((unsigned char *)value) == 0) |
| 209 | 213 | { |
| 210 | 214 | WARN2 ("seen non-UTF8 data, probably incorrect metadata (%s, %s)", name, value); |
| 211 | 215 | return; |
| 212 | 216 | } |
| 213 | | event = build_event (source, name, value); |
| 214 | | if (event) |
| 215 | | queue_global_event (event); |
| 217 | build_event (&event, source, name, (char *)value); |
| 218 | process_event (&event); |
| 216 | 219 | } |
| 217 | 220 | |
| 218 | 221 | |
| ... | ...@@ -258,17 +261,14 @@ |
| 258 | 261 | * source stats tree. */ |
| 259 | 262 | void stats_event_hidden (const char *source, const char *name, int hidden) |
| 260 | 263 | { |
| 261 | | stats_event_t *event; |
| 262 | 264 | const char *str = NULL; |
| 265 | stats_event_t event; |
| 263 | 266 | |
| 264 | 267 | if (hidden) |
| 265 | 268 | str = ""; |
| 266 | | event = build_event (source, name, str); |
| 267 | | if (event) |
| 268 | | { |
| 269 | | event->action = STATS_EVENT_HIDDEN; |
| 270 | | queue_global_event (event); |
| 271 | | } |
| 269 | build_event (&event, source, name, NULL); |
| 270 | event.action = STATS_EVENT_HIDDEN; |
| 271 | process_event (&event); |
| 272 | 272 | } |
| 273 | 273 | |
| 274 | 274 | /* printf style formatting for stat create/update */ |
| ... | ...@@ -325,51 +325,46 @@ |
| 325 | 325 | /* increase the value in the provided stat by 1 */ |
| 326 | 326 | void stats_event_inc(const char *source, const char *name) |
| 327 | 327 | { |
| 328 | | stats_event_t *event = build_event (source, name, NULL); |
| 328 | stats_event_t event; |
| 329 | char buffer[VAL_BUFSIZE]; |
| 330 | build_event (&event, source, name, buffer); |
| 329 | 331 | /* DEBUG2("%s on %s", name, source==NULL?"global":source); */ |
| 330 | | if (event) |
| 331 | | { |
| 332 | | event->action = STATS_EVENT_INC; |
| 333 | | queue_global_event (event); |
| 334 | | } |
| 332 | event.action = STATS_EVENT_INC; |
| 333 | process_event (&event); |
| 335 | 334 | } |
| 336 | 335 | |
| 337 | 336 | void stats_event_add(const char *source, const char *name, unsigned long value) |
| 338 | 337 | { |
| 339 | | stats_event_t *event = build_event (source, name, NULL); |
| 338 | stats_event_t event; |
| 339 | char buffer [VAL_BUFSIZE]; |
| 340 | |
| 341 | build_event (&event, source, name, buffer); |
| 342 | snprintf (buffer, VAL_BUFSIZE, "%ld", value); |
| 343 | event.action = STATS_EVENT_ADD; |
| 340 | 344 | /* DEBUG2("%s on %s", name, source==NULL?"global":source); */ |
| 341 | | if (event) |
| 342 | | { |
| 343 | | event->value = malloc (16); |
| 344 | | snprintf (event->value, 16, "%ld", value); |
| 345 | | event->action = STATS_EVENT_ADD; |
| 346 | | queue_global_event (event); |
| 347 | | } |
| 345 | process_event (&event); |
| 348 | 346 | } |
| 349 | 347 | |
| 350 | 348 | void stats_event_sub(const char *source, const char *name, unsigned long value) |
| 351 | 349 | { |
| 352 | | stats_event_t *event = build_event (source, name, NULL); |
| 350 | stats_event_t event; |
| 351 | char buffer[VAL_BUFSIZE]; |
| 352 | build_event (&event, source, name, buffer); |
| 353 | 353 | /* DEBUG2("%s on %s", name, source==NULL?"global":source); */ |
| 354 | | if (event) |
| 355 | | { |
| 356 | | event->value = malloc (16); |
| 357 | | snprintf (event->value, 16, "%ld", value); |
| 358 | | event->action = STATS_EVENT_SUB; |
| 359 | | queue_global_event (event); |
| 360 | | } |
| 354 | snprintf (buffer, VAL_BUFSIZE, "%ld", value); |
| 355 | event.action = STATS_EVENT_SUB; |
| 356 | process_event (&event); |
| 361 | 357 | } |
| 362 | 358 | |
| 363 | 359 | /* decrease the value in the provided stat by 1 */ |
| 364 | 360 | void stats_event_dec(const char *source, const char *name) |
| 365 | 361 | { |
| 362 | stats_event_t event; |
| 363 | char buffer[VAL_BUFSIZE]; |
| 366 | 364 | /* DEBUG2("%s on %s", name, source==NULL?"global":source); */ |
| 367 | | stats_event_t *event = build_event (source, name, NULL); |
| 368 | | if (event) |
| 369 | | { |
| 370 | | event->action = STATS_EVENT_DEC; |
| 371 | | queue_global_event (event); |
| 372 | | } |
| 365 | build_event (&event, source, name, buffer); |
| 366 | event.action = STATS_EVENT_DEC; |
| 367 | process_event (&event); |
| 373 | 368 | } |
| 374 | 369 | |
| 375 | 370 | /* note: you must call this function only when you have exclusive access |
| ... | ...@@ -448,8 +443,8 @@ |
| 448 | 443 | /* helper to apply specialised changes to a stats node */ |
| 449 | 444 | static void modify_node_event (stats_node_t *node, stats_event_t *event) |
| 450 | 445 | { |
| 451 | | char *str; |
| 452 | | |
| 446 | if (node == NULL || event == NULL) |
| 447 | return; |
| 453 | 448 | if (event->action == STATS_EVENT_HIDDEN) |
| 454 | 449 | { |
| 455 | 450 | if (event->value) |
| ... | ...@@ -479,15 +474,10 @@ |
| 479 | 474 | default: |
| 480 | 475 | break; |
| 481 | 476 | } |
| 482 | | str = malloc (20); |
| 483 | | snprintf (str, 20, "%" PRId64, value); |
| 484 | | free (event->value); |
| 485 | | event->value = strdup (str); |
| 477 | snprintf (event->value, VAL_BUFSIZE, "%" PRId64, value); |
| 486 | 478 | } |
| 487 | | else |
| 488 | | str = (char *)strdup (event->value); |
| 489 | 479 | free (node->value); |
| 490 | | node->value = str; |
| 480 | node->value = strdup (event->value); |
| 491 | 481 | } |
| 492 | 482 | |
| 493 | 483 | |
| ... | ...@@ -604,7 +594,7 @@ |
| 604 | 594 | char buffer[100]; |
| 605 | 595 | |
| 606 | 596 | localtime_r (&now, &local); |
| 607 | | strftime (buffer, sizeof (buffer), "%a, %d %b %Y %H:%M:%S %z", &local); |
| 597 | strftime (buffer, sizeof (buffer), ICECAST_TIME_FMT, &local); |
| 608 | 598 | stats_event (mount, name, buffer); |
| 609 | 599 | } |
| 610 | 600 | |
| ... | ...@@ -640,64 +630,26 @@ |
| 640 | 630 | stats_event (NULL, "admin", config->admin); |
| 641 | 631 | } |
| 642 | 632 | |
| 643 | | |
| 644 | | static void *_stats_thread(void *arg) |
| 633 | static void process_event_unlocked (stats_event_t *event) |
| 645 | 634 | { |
| 646 | | stats_event_t *event; |
| 647 | | |
| 648 | | stats_event_time (NULL, "server_start"); |
| 649 | | |
| 650 | | /* global currently active stats */ |
| 651 | | stats_event (NULL, "clients", "0"); |
| 652 | | stats_event (NULL, "connections", "0"); |
| 653 | | stats_event (NULL, "sources", "0"); |
| 654 | | stats_event (NULL, "stats", "0"); |
| 655 | | stats_event (NULL, "listeners", "0"); |
| 656 | | |
| 657 | | /* global accumulating stats */ |
| 658 | | stats_event (NULL, "client_connections", "0"); |
| 659 | | stats_event (NULL, "source_client_connections", "0"); |
| 660 | | stats_event (NULL, "source_relay_connections", "0"); |
| 661 | | stats_event (NULL, "source_total_connections", "0"); |
| 662 | | stats_event (NULL, "stats_connections", "0"); |
| 663 | | stats_event (NULL, "listener_connections", "0"); |
| 664 | | stats_event (NULL, "outgoing_kbitrate", "0"); |
| 665 | | |
| 666 | | INFO0 ("stats thread started"); |
| 667 | | while (_stats_running) { |
| 668 | | if (_global_event_queue.head != NULL) { |
| 669 | | /* grab the next event from the queue */ |
| 670 | | thread_spin_lock(&_global_event_mutex); |
| 671 | | event = _get_event_from_queue (&_global_event_queue); |
| 672 | | thread_spin_unlock(&_global_event_mutex); |
| 673 | | |
| 674 | | if (event == NULL) |
| 675 | | continue; |
| 676 | | event->next = NULL; |
| 677 | | |
| 678 | | thread_mutex_lock(&_stats_mutex); |
| 679 | | |
| 680 | | /* check if we are dealing with a global or source event */ |
| 681 | | if (event->source == NULL) |
| 682 | | process_global_event (event); |
| 683 | | else |
| 684 | | process_source_event (event); |
| 685 | | |
| 686 | | /* now we have an event that's been processed into the running stats */ |
| 687 | | /* this event should get copied to event listeners' queues */ |
| 688 | | stats_listener_send (event); |
| 689 | | |
| 690 | | /* now we need to destroy the event */ |
| 691 | | _free_event(event); |
| 635 | /* check if we are dealing with a global or source event */ |
| 636 | if (event->source == NULL) |
| 637 | process_global_event (event); |
| 638 | else |
| 639 | process_source_event (event); |
| 692 | 640 | |
| 693 | | thread_mutex_unlock(&_stats_mutex); |
| 694 | | continue; |
| 695 | | } |
| 696 | | thread_sleep(500000); |
| 697 | | stats_global_calc(); |
| 698 | | } |
| 641 | /* now we have an event that's been processed into the running stats */ |
| 642 | /* this event should get copied to event listeners' queues */ |
| 643 | stats_listener_send (event); |
| 644 | } |
| 699 | 645 | |
| 700 | | return NULL; |
| 646 | static void process_event (stats_event_t *event) |
| 647 | { |
| 648 | if (event == NULL) |
| 649 | return; |
| 650 | thread_mutex_lock (&_stats_mutex); |
| 651 | process_event_unlocked (event); |
| 652 | thread_mutex_unlock (&_stats_mutex); |
| 701 | 653 | } |
| 702 | 654 | |
| 703 | 655 | /* you must have the _stats_mutex locked here */ |
| ... | ...@@ -705,6 +657,9 @@ |
| 705 | 657 | { |
| 706 | 658 | event_listener_t **prev = (event_listener_t **)&_event_listeners, |
| 707 | 659 | *current = *prev; |
| 660 | stats_event_t stats_count, *event; |
| 661 | char buffer [VAL_BUFSIZE]; |
| 662 | |
| 708 | 663 | while (current) |
| 709 | 664 | { |
| 710 | 665 | if (current == listener) |
| ... | ...@@ -715,6 +670,15 @@ |
| 715 | 670 | prev = ¤t->next; |
| 716 | 671 | current = *prev; |
| 717 | 672 | } |
| 673 | |
| 674 | /* remove this listener before sending this change */ |
| 675 | build_event (&stats_count, NULL, "stats_connections", buffer); |
| 676 | stats_count.action = STATS_EVENT_DEC; |
| 677 | process_event_unlocked (&stats_count); |
| 678 | |
| 679 | /* flush any extra that sneaked on at the last moment */ |
| 680 | while ((event = _get_event_from_queue (&listener->queue)) != NULL) |
| 681 | _free_event (event); |
| 718 | 682 | } |
| 719 | 683 | |
| 720 | 684 | |
| ... | ...@@ -823,8 +787,12 @@ |
| 823 | 787 | avl_node *node2; |
| 824 | 788 | stats_event_t *event; |
| 825 | 789 | stats_source_t *source; |
| 790 | stats_event_t stats_count; |
| 791 | char buffer[20]; |
| 826 | 792 | |
| 827 | | thread_mutex_lock(&_stats_mutex); |
| 793 | build_event (&stats_count, NULL, "stats_connections", buffer); |
| 794 | stats_count.action = STATS_EVENT_INC; |
| 795 | process_event_unlocked (&stats_count); |
| 828 | 796 | |
| 829 | 797 | /* first we fill our queue with the current stats */ |
| 830 | 798 | |
| ... | ...@@ -855,11 +823,8 @@ |
| 855 | 823 | /* now we register to receive future event notices */ |
| 856 | 824 | listener->next = (event_listener_t *)_event_listeners; |
| 857 | 825 | _event_listeners = listener; |
| 858 | | |
| 859 | | thread_mutex_unlock(&_stats_mutex); |
| 860 | 826 | } |
| 861 | 827 | |
| 862 | | |
| 863 | 828 | static void check_uri (event_listener_t *listener, client_t *client) |
| 864 | 829 | { |
| 865 | 830 | const char *mount = httpp_getvar (client->parser, HTTPP_VAR_URI); |
| ... | ...@@ -883,12 +848,10 @@ |
| 883 | 848 | /* increment the thread count */ |
| 884 | 849 | thread_mutex_lock(&_stats_mutex); |
| 885 | 850 | _stats_threads++; |
| 886 | | stats_event_args (NULL, "stats", "%d", _stats_threads); |
| 887 | | thread_mutex_unlock(&_stats_mutex); |
| 888 | | |
| 889 | 851 | thread_mutex_create("stats local event", &listener.mutex); |
| 890 | 852 | |
| 891 | 853 | _register_listener (&listener); |
| 854 | thread_mutex_unlock(&_stats_mutex); |
| 892 | 855 | |
| 893 | 856 | while (_stats_running) { |
| 894 | 857 | thread_mutex_lock (&listener.mutex); |
| ... | ...@@ -908,7 +871,6 @@ |
| 908 | 871 | thread_mutex_lock(&_stats_mutex); |
| 909 | 872 | _unregister_listener (&listener); |
| 910 | 873 | _stats_threads--; |
| 911 | | stats_event_args (NULL, "stats", "%d", _stats_threads); |
| 912 | 874 | thread_mutex_unlock(&_stats_mutex); |
| 913 | 875 | |
| 914 | 876 | thread_mutex_destroy (&listener.mutex); |
| ... | ...@@ -939,7 +901,7 @@ |
| 939 | 901 | struct _source_xml_tag *next; |
| 940 | 902 | } source_xml_t; |
| 941 | 903 | |
| 942 | | static xmlNodePtr _find_xml_node(char *mount, source_xml_t **list, xmlNodePtr root) |
| 904 | static xmlNodePtr _find_xml_node(const char *mount, source_xml_t **list, xmlNodePtr root) |
| 943 | 905 | { |
| 944 | 906 | source_xml_t *node, *node2; |
| 945 | 907 | int found = 0; |
| ... | ...@@ -1185,30 +1147,21 @@ |
| 1185 | 1147 | } |
| 1186 | 1148 | |
| 1187 | 1149 | |
| 1188 | | static void stats_global_calc (void) |
| 1150 | void stats_global_calc (void) |
| 1189 | 1151 | { |
| 1190 | 1152 | stats_event_t event; |
| 1191 | | stats_node_t *node; |
| 1192 | | char buffer [20]; |
| 1153 | char buffer [VAL_BUFSIZE]; |
| 1193 | 1154 | static time_t next_update = 0; |
| 1194 | 1155 | |
| 1195 | 1156 | if (global.time < next_update) |
| 1196 | 1157 | return; |
| 1197 | 1158 | next_update = global.time + 1; |
| 1198 | | event.source = NULL; |
| 1199 | | event.name = "outgoing_kbitrate"; |
| 1200 | | event.value = buffer; |
| 1201 | | event.action = STATS_EVENT_SET; |
| 1159 | build_event (&event, NULL, "outgoing_kbitrate", buffer); |
| 1202 | 1160 | |
| 1203 | 1161 | thread_mutex_lock (&_stats_mutex); |
| 1204 | | node = _find_node(_stats.global_tree, event.name); |
| 1205 | | if (node) |
| 1206 | | { |
| 1207 | | snprintf (buffer, sizeof(buffer), "%" PRIu64, |
| 1162 | snprintf (buffer, sizeof(buffer), "%" PRIu64, |
| 1208 | 1163 | (int64_t)(global_getrate_avg (global.out_bitrate) * 8 / 1000.0 + 0.5)); |
| 1209 | | modify_node_event (node, &event); |
| 1210 | | stats_listener_send (&event); |
| 1211 | | } |
| 1164 | process_event_unlocked (&event); |
| 1212 | 1165 | thread_mutex_unlock (&_stats_mutex); |
| 1213 | 1166 | } |
| 1214 | 1167 | |