Streaming improvements No 12 (#19299)
* do not log when a socket is closed on accept() * waiting queues use futex under linux; use waiting-queues for pgc-queue-locks * do not use waiting queue for pgc-queue-lock * multiplex releasing to ensure better ordering; disable futex implementation until further testing * initialize pulse counters to zero * fixed web connected clients count in pulse * coverity fixes
Costa Tsaousis committed
Dec 30, 2024 at 21:07 UTC
c2075ca19f74c7cf05a56544901f9fa81a76265e
20 files changed
+250
-151
src/collectors/debugfs.plugin/module-libsensors.c
+4
@@ -924,6 +924,10 @@ static SENSOR *sensor_get_or_create(DICTIONARY *dict, const sensors_chip_name *c
924
string2str(s->feature.name));
925
}
926
927
+ // we have to free this, because it is malloced from libsensors
928
+ if(label)
929
+ free((void *)label); // do not use freez() here - libsensors uses malloc()
930
+
931
netdata_fix_chart_id(buf);
932
s->id = string_strdupz(buf);
933
src/collectors/proc.plugin/proc_diskstats.c
+4
-7
@@ -340,7 +340,6 @@ static inline int is_major_enabled(int major) {
340
341
static inline int get_disk_name_from_path(const char *path, char *result, size_t result_size, unsigned long major, unsigned long minor, char *disk, char *prefix, int depth) {
342
//collector_info("DEVICE-MAPPER ('%s', %lu:%lu): examining directory '%s' (allowed depth %d).", disk, major, minor, path, depth);
343
-
343
int found = 0, preferred = 0;
344
345
char *first_result = mallocz(result_size + 1);
@@ -401,9 +400,8 @@ static inline int get_disk_name_from_path(const char *path, char *result, size_t
400
else
401
strncpyz(filename, result, FILENAME_MAX);
402
}
404
- else {
403
+ else
404
snprintfz(filename, FILENAME_MAX, "%s/%s", path, de->d_name);
406
- }
405
406
struct stat sb;
407
if(stat(filename, &sb) == -1) {
@@ -426,10 +424,11 @@ static inline int get_disk_name_from_path(const char *path, char *result, size_t
424
snprintfz(result, result_size - 1, "%s%s%s", (prefix)?prefix:"", (prefix)?"_":"", de->d_name);
425
426
if(!found) {
429
- strncpyz(first_result, result, result_size);
427
+ strncpyz(first_result, result, result_size - 1);
428
found = 1;
429
}
430
431
+ result[result_size - 1] = '\0';
432
if(simple_pattern_matches(preferred_ids, result)) {
433
preferred = 1;
434
break;
@@ -438,13 +437,11 @@ static inline int get_disk_name_from_path(const char *path, char *result, size_t
437
}
438
closedir(dir);
439
441
-
440
failed:
443
-
441
if(!found)
442
result[0] = '\0';
443
else if(!preferred)
447
- strncpyz(result, first_result, result_size);
444
+ strncpyz(result, first_result, result_size - 1);
445
446
freez(first_result);
447
src/daemon/pulse/pulse-daemon-memory.c
+1
-1
@@ -7,7 +7,7 @@
7
#define dictionary_stats_memory_total(stats) \
8
((stats).memory.dict + (stats).memory.values + (stats).memory.index)
9
10
-struct netdata_buffers_statistics netdata_buffers_statistics = {};
10
+struct netdata_buffers_statistics netdata_buffers_statistics = { 0 };
11
12
void pulse_daemon_memory_do(bool extended) {
13
{
src/daemon/pulse/pulse-http-api.c
+23
-26
@@ -8,8 +8,7 @@
8
static struct web_statistics {
9
bool extended;
10
11
- PAD64(uint16_t) connected_clients;
12
- PAD64(uint64_t) web_client_count; // oops! this is used for giving unique IDs to web_clients!
11
+ PAD64(int32_t) connected_clients;
12
13
PAD64(uint64_t) web_requests;
14
PAD64(uint64_t) web_usec;
@@ -19,15 +18,14 @@ static struct web_statistics {
18
19
PAD64(uint64_t) content_size_uncompressed;
20
PAD64(uint64_t) content_size_compressed;
22
-} web_statistics;
21
+} live_stats = { 0 };
22
24
-uint64_t pulse_web_client_connected(void) {
25
- __atomic_fetch_add(&web_statistics.connected_clients, 1, __ATOMIC_RELAXED);
26
- return __atomic_fetch_add(&web_statistics.web_client_count, 1, __ATOMIC_RELAXED);
23
+void pulse_web_client_connected(void) {
24
+ __atomic_fetch_add(&live_stats.connected_clients, 1, __ATOMIC_RELAXED);
25
}
26
27
void pulse_web_client_disconnected(void) {
30
- __atomic_fetch_sub(&web_statistics.connected_clients, 1, __ATOMIC_RELAXED);
28
+ __atomic_fetch_sub(&live_stats.connected_clients, 1, __ATOMIC_RELAXED);
29
}
30
31
void pulse_web_request_completed(uint64_t dt,
@@ -35,32 +33,31 @@ void pulse_web_request_completed(uint64_t dt,
33
uint64_t bytes_sent,
34
uint64_t content_size,
35
uint64_t compressed_content_size) {
38
- uint64_t old_web_usec_max = web_statistics.web_usec_max;
36
+ uint64_t old_web_usec_max = live_stats.web_usec_max;
37
while(dt > old_web_usec_max)
40
- __atomic_compare_exchange(&web_statistics.web_usec_max, &old_web_usec_max, &dt, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
41
-
42
- __atomic_fetch_add(&web_statistics.web_requests, 1, __ATOMIC_RELAXED);
43
- __atomic_fetch_add(&web_statistics.web_usec, dt, __ATOMIC_RELAXED);
44
- __atomic_fetch_add(&web_statistics.bytes_received, bytes_received, __ATOMIC_RELAXED);
45
- __atomic_fetch_add(&web_statistics.bytes_sent, bytes_sent, __ATOMIC_RELAXED);
46
- __atomic_fetch_add(&web_statistics.content_size_uncompressed, content_size, __ATOMIC_RELAXED);
47
- __atomic_fetch_add(&web_statistics.content_size_compressed, compressed_content_size, __ATOMIC_RELAXED);
38
+ __atomic_compare_exchange(&live_stats.web_usec_max, &old_web_usec_max, &dt, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
39
+
40
+ __atomic_fetch_add(&live_stats.web_requests, 1, __ATOMIC_RELAXED);
41
+ __atomic_fetch_add(&live_stats.web_usec, dt, __ATOMIC_RELAXED);
42
+ __atomic_fetch_add(&live_stats.bytes_received, bytes_received, __ATOMIC_RELAXED);
43
+ __atomic_fetch_add(&live_stats.bytes_sent, bytes_sent, __ATOMIC_RELAXED);
44
+ __atomic_fetch_add(&live_stats.content_size_uncompressed, content_size, __ATOMIC_RELAXED);
45
+ __atomic_fetch_add(&live_stats.content_size_compressed, compressed_content_size, __ATOMIC_RELAXED);
46
}
47
48
static inline void pulse_web_copy(struct web_statistics *gs, uint8_t options) {
51
- gs->connected_clients = __atomic_load_n(&web_statistics.connected_clients, __ATOMIC_RELAXED);
52
- gs->web_requests = __atomic_load_n(&web_statistics.web_requests, __ATOMIC_RELAXED);
53
- gs->web_usec = __atomic_load_n(&web_statistics.web_usec, __ATOMIC_RELAXED);
54
- gs->web_usec_max = __atomic_load_n(&web_statistics.web_usec_max, __ATOMIC_RELAXED);
55
- gs->bytes_received = __atomic_load_n(&web_statistics.bytes_received, __ATOMIC_RELAXED);
56
- gs->bytes_sent = __atomic_load_n(&web_statistics.bytes_sent, __ATOMIC_RELAXED);
57
- gs->content_size_uncompressed = __atomic_load_n(&web_statistics.content_size_uncompressed, __ATOMIC_RELAXED);
58
- gs->content_size_compressed = __atomic_load_n(&web_statistics.content_size_compressed, __ATOMIC_RELAXED);
59
- gs->web_client_count = __atomic_load_n(&web_statistics.web_client_count, __ATOMIC_RELAXED);
49
+ gs->connected_clients = __atomic_load_n(&live_stats.connected_clients, __ATOMIC_RELAXED);
50
+ gs->web_requests = __atomic_load_n(&live_stats.web_requests, __ATOMIC_RELAXED);
51
+ gs->web_usec = __atomic_load_n(&live_stats.web_usec, __ATOMIC_RELAXED);
52
+ gs->web_usec_max = __atomic_load_n(&live_stats.web_usec_max, __ATOMIC_RELAXED);
53
+ gs->bytes_received = __atomic_load_n(&live_stats.bytes_received, __ATOMIC_RELAXED);
54
+ gs->bytes_sent = __atomic_load_n(&live_stats.bytes_sent, __ATOMIC_RELAXED);
55
+ gs->content_size_uncompressed = __atomic_load_n(&live_stats.content_size_uncompressed, __ATOMIC_RELAXED);
56
+ gs->content_size_compressed = __atomic_load_n(&live_stats.content_size_compressed, __ATOMIC_RELAXED);
57
58
if(options & GLOBAL_STATS_RESET_WEB_USEC_MAX) {
59
uint64_t n = 0;
63
- __atomic_compare_exchange(&web_statistics.web_usec_max, (uint64_t *) &gs->web_usec_max, &n, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
60
+ __atomic_compare_exchange(&live_stats.web_usec_max, (uint64_t *) &gs->web_usec_max, &n, 1, __ATOMIC_RELAXED, __ATOMIC_RELAXED);
61
}
62
}
63
src/daemon/pulse/pulse-http-api.h
+1
-1
@@ -5,7 +5,7 @@
5
6
#include "daemon/common.h"
7
8
-uint64_t pulse_web_client_connected(void);
8
+void pulse_web_client_connected(void);
9
void pulse_web_client_disconnected(void);
10
11
void pulse_web_request_completed(uint64_t dt,
src/daemon/pulse/pulse-ingestion.c
+1
-1
@@ -5,7 +5,7 @@
5
6
static struct ingest_statistics {
7
uint64_t db_points_stored_per_tier[RRD_STORAGE_TIERS];
8
-} ingest_statistics;
8
+} ingest_statistics = { 0 };
9
10
void pulse_queries_rrdset_collection_completed(size_t *points_read_per_tier_array) {
11
for(size_t tier = 0; tier < nd_profile.storage_tiers;tier++) {
src/daemon/pulse/pulse-ml.c
+1
-1
@@ -12,7 +12,7 @@ static struct ml_statistics {
12
PAD64(uint64_t) ml_memory_consumption;
13
PAD64(uint64_t) ml_memory_new;
14
PAD64(uint64_t) ml_memory_delete;
15
-} ml_statistics = {0};
15
+} ml_statistics = { 0 };
16
17
void pulse_ml_models_received()
18
{
src/daemon/pulse/pulse-queries.c
+1
-1
@@ -30,7 +30,7 @@ static struct query_statistics {
30
31
PAD64(uint64_t) exporters_queries_made;
32
PAD64(uint64_t) exporters_db_points_read;
33
-} query_statistics;
33
+} query_statistics = { 0 };
34
35
void pulse_queries_ml_query_completed(size_t points_read) {
36
__atomic_fetch_add(&query_statistics.ml_queries_made, 1, __ATOMIC_RELAXED);
src/daemon/pulse/pulse-sqlite3.c
+1
-1
@@ -20,7 +20,7 @@ static struct sqlite3_statistics {
20
PAD64(uint64_t) sqlite3_context_cache_spill;
21
PAD64(uint64_t) sqlite3_metadata_cache_write;
22
PAD64(uint64_t) sqlite3_context_cache_write;
23
-} sqlite3_statistics = { };
23
+} sqlite3_statistics = { 0 };
24
25
void pulse_sqlite3_query_completed(bool success, bool busy, bool locked) {
26
if(!sqlite3_statistics.enabled) return;
src/database/engine/cache.c
+71
-43
@@ -22,6 +22,9 @@ typedef int32_t REFCOUNT;
22
#define PGC_WITH_ARAL 1
23
#endif
24
25
+// unfortunately waiting queue is significantly slower than spinlock
26
+// #define PGC_QUEUE_LOCK_AS_WAITING_QUEUE 1
27
+
28
typedef enum __attribute__ ((__packed__)) {
29
// mutually exclusive flags
30
PGC_PAGE_CLEAN = (1 << 0), // none of the following
@@ -71,7 +74,11 @@ struct pgc_page {
74
};
75
76
struct pgc_queue {
77
+#if defined(PGC_QUEUE_LOCK_AS_WAITING_QUEUE)
78
+ WAITING_QUEUE *wq;
79
+#else
80
SPINLOCK spinlock;
81
+#endif
82
union {
83
PGC_PAGE *base;
84
Pvoid_t sections_judy;
@@ -239,9 +246,20 @@ static inline size_t pgc_indexing_partition(PGC *cache, Word_t metric_id) {
246
_result; \
247
})
248
242
-#define pgc_queue_trylock(cache, ll) spinlock_trylock(&(ll)->spinlock)
243
-#define pgc_queue_lock(cache, ll) spinlock_lock(&(ll)->spinlock)
244
-#define pgc_queue_unlock(cache, ll) spinlock_unlock(&(ll)->spinlock)
249
+#define PGC_QUEUE_LOCK_PRIO_COLLECTORS WAITING_QUEUE_PRIO_URGENT
250
+#define PGC_QUEUE_LOCK_PRIO_EVICTORS WAITING_QUEUE_PRIO_HIGH
251
+#define PGC_QUEUE_LOCK_PRIO_FLUSHERS WAITING_QUEUE_PRIO_NORMAL
252
+#define PGC_QUEUE_LOCK_PRIO_OTHERS WAITING_QUEUE_PRIO_LOW
253
+
254
+#if defined(PGC_QUEUE_LOCK_AS_WAITING_QUEUE)
255
+#define pgc_queue_trylock(cache, ll) waiting_queue_try_acquire((ll)->wq)
256
+#define pgc_queue_lock(cache, ll, prio) waiting_queue_acquire((ll)->wq, prio)
257
+#define pgc_queue_unlock(cache, ll) waiting_queue_release((ll)->wq)
258
+#else
259
+#define pgc_queue_trylock(cache, ll) spinlock_trylock(&((ll)->spinlock))
260
+#define pgc_queue_lock(cache, ll, prio) spinlock_lock(&((ll)->spinlock))
261
+#define pgc_queue_unlock(cache, ll) spinlock_unlock(&((ll)->spinlock))
262
+#endif
263
264
#define page_transition_trylock(cache, page) spinlock_trylock(&(page)->transition_spinlock)
265
#define page_transition_lock(cache, page) spinlock_lock(&(page)->transition_spinlock)
@@ -590,9 +608,9 @@ static inline void pgc_stats_index_judy_change(PGC *cache, size_t mem_before_jud
608
}
609
}
610
593
-static void pgc_queue_add(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PAGE *page, bool having_lock) {
611
+static void pgc_queue_add(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PAGE *page, bool having_lock, WAITING_QUEUE_PRIORITY prio __maybe_unused) {
612
if(!having_lock)
595
- pgc_queue_lock(cache, q);
613
+ pgc_queue_lock(cache, q, prio);
614
615
internal_fatal(page_get_status_flags(page) != 0,
616
"DBENGINE CACHE: invalid page flags, the page has %d, but it is should be %d",
@@ -659,7 +677,7 @@ static void pgc_queue_add(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PA
677
pgc_size_histogram_add(cache, &q->stats->size_histogram, page);
678
}
679
662
-static void pgc_queue_del(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PAGE *page, bool having_lock) {
680
+static void pgc_queue_del(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PAGE *page, bool having_lock, WAITING_QUEUE_PRIORITY prio __maybe_unused) {
681
if(cache->config.stats)
682
pgc_size_histogram_del(cache, &q->stats->size_histogram, page);
683
@@ -669,7 +687,7 @@ static void pgc_queue_del(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PA
687
__atomic_add_fetch(&q->stats->removed_size, page->assumed_size, __ATOMIC_RELAXED);
688
689
if(!having_lock)
672
- pgc_queue_lock(cache, q);
690
+ pgc_queue_lock(cache, q, prio);
691
692
internal_fatal(page_get_status_flags(page) != q->flags,
693
"DBENGINE CACHE: invalid page flags, the page has %d, but it is should be %d",
@@ -688,7 +706,7 @@ static void pgc_queue_del(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PA
706
DOUBLE_LINKED_LIST_REMOVE_ITEM_UNSAFE(sp->base, page, link.prev, link.next);
707
708
if(!sp->base) {
691
- size_t mem_before_judyl, mem_after_judyl;
709
+ ssize_t mem_before_judyl, mem_after_judyl;
710
711
mem_before_judyl = JudyLMemUsed(q->sections_judy);
712
int rc = JudyLDel(&q->sections_judy, page->section, PJE0);
@@ -699,7 +717,6 @@ static void pgc_queue_del(PGC *cache __maybe_unused, struct pgc_queue *q, PGC_PA
717
718
// freez(sp);
719
aral_freez(pgc_sections_aral, sp);
702
- mem_after_judyl -= sizeof(struct section_pages);
720
pgc_stats_queue_judy_change(cache, q, mem_before_judyl, mem_after_judyl);
721
}
722
}
@@ -735,7 +752,7 @@ static inline void page_has_been_accessed(PGC *cache, PGC_PAGE *page) {
752
// ----------------------------------------------------------------------------
753
// state transitions
754
738
-static inline void page_set_clean(PGC *cache, PGC_PAGE *page, bool having_transition_lock, bool having_clean_lock) {
755
+static inline void page_set_clean(PGC *cache, PGC_PAGE *page, bool having_transition_lock, bool having_clean_lock, WAITING_QUEUE_PRIORITY prio) {
756
if(!having_transition_lock)
757
page_transition_lock(cache, page);
758
@@ -748,23 +765,23 @@ static inline void page_set_clean(PGC *cache, PGC_PAGE *page, bool having_transi
765
}
766
767
if(flags & PGC_PAGE_HOT)
751
- pgc_queue_del(cache, &cache->hot, page, false);
768
+ pgc_queue_del(cache, &cache->hot, page, false, prio);
769
770
if(flags & PGC_PAGE_DIRTY)
754
- pgc_queue_del(cache, &cache->dirty, page, false);
771
+ pgc_queue_del(cache, &cache->dirty, page, false, prio);
772
773
// first add to linked list, the set the flag (required for move_page_last())
757
- pgc_queue_add(cache, &cache->clean, page, having_clean_lock);
774
+ pgc_queue_add(cache, &cache->clean, page, having_clean_lock, prio);
775
776
if(!having_transition_lock)
777
page_transition_unlock(cache, page);
778
}
779
763
-static inline void page_set_dirty(PGC *cache, PGC_PAGE *page, bool having_hot_lock) {
780
+static inline void page_set_dirty(PGC *cache, PGC_PAGE *page, bool having_hot_lock, WAITING_QUEUE_PRIORITY prio) {
781
if(!having_hot_lock)
782
// to avoid deadlocks, we have to get the hot lock before the page transition
783
// since this is what all_hot_to_dirty() does
767
- pgc_queue_lock(cache, &cache->hot);
784
+ pgc_queue_lock(cache, &cache->hot, prio);
785
786
page_transition_lock(cache, page);
787
@@ -784,17 +801,17 @@ static inline void page_set_dirty(PGC *cache, PGC_PAGE *page, bool having_hot_lo
801
__atomic_add_fetch(&cache->stats.hot2dirty_size, page->assumed_size, __ATOMIC_RELAXED);
802
803
if(likely(flags & PGC_PAGE_HOT))
787
- pgc_queue_del(cache, &cache->hot, page, true);
804
+ pgc_queue_del(cache, &cache->hot, page, true, prio);
805
806
if(!having_hot_lock)
807
// we don't need the hot lock anymore
808
pgc_queue_unlock(cache, &cache->hot);
809
810
if(unlikely(flags & PGC_PAGE_CLEAN))
794
- pgc_queue_del(cache, &cache->clean, page, false);
811
+ pgc_queue_del(cache, &cache->clean, page, false, prio);
812
813
// first add to linked list, the set the flag (required for move_page_last())
797
- pgc_queue_add(cache, &cache->dirty, page, false);
814
+ pgc_queue_add(cache, &cache->dirty, page, false, prio);
815
816
__atomic_sub_fetch(&cache->stats.hot2dirty_entries, 1, __ATOMIC_RELAXED);
817
__atomic_sub_fetch(&cache->stats.hot2dirty_size, page->assumed_size, __ATOMIC_RELAXED);
@@ -802,7 +819,7 @@ static inline void page_set_dirty(PGC *cache, PGC_PAGE *page, bool having_hot_lo
819
page_transition_unlock(cache, page);
820
}
821
805
-static inline void page_set_hot(PGC *cache, PGC_PAGE *page) {
822
+static inline void page_set_hot(PGC *cache, PGC_PAGE *page, WAITING_QUEUE_PRIORITY prio) {
823
page_transition_lock(cache, page);
824
825
PGC_PAGE_FLAGS flags = page_get_status_flags(page);
@@ -813,13 +830,13 @@ static inline void page_set_hot(PGC *cache, PGC_PAGE *page) {
830
}
831
832
if(flags & PGC_PAGE_DIRTY)
816
- pgc_queue_del(cache, &cache->dirty, page, false);
833
+ pgc_queue_del(cache, &cache->dirty, page, false, prio);
834
835
if(flags & PGC_PAGE_CLEAN)
819
- pgc_queue_del(cache, &cache->clean, page, false);
836
+ pgc_queue_del(cache, &cache->clean, page, false, prio);
837
838
// first add to linked list, the set the flag (required for move_page_last())
822
- pgc_queue_add(cache, &cache->hot, page, false);
839
+ pgc_queue_add(cache, &cache->hot, page, false, prio);
840
841
page_transition_unlock(cache, page);
842
}
@@ -1098,10 +1115,10 @@ static inline bool make_acquired_page_clean_and_evict_or_page_release(PGC *cache
1115
pointer_check(cache, page);
1116
1117
page_transition_lock(cache, page);
1101
- pgc_queue_lock(cache, &cache->clean);
1118
+ pgc_queue_lock(cache, &cache->clean, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1119
1120
// make it clean - it does not have any accesses, so it will be prepended
1104
- page_set_clean(cache, page, true, true);
1121
+ page_set_clean(cache, page, true, true, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1122
1123
if(!acquired_page_get_for_deletion_or_release_it(cache, page)) {
1124
pgc_queue_unlock(cache, &cache->clean);
@@ -1110,7 +1127,7 @@ static inline bool make_acquired_page_clean_and_evict_or_page_release(PGC *cache
1127
}
1128
1129
// remove it from the linked list
1113
- pgc_queue_del(cache, &cache->clean, page, true);
1130
+ pgc_queue_del(cache, &cache->clean, page, true, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1131
pgc_queue_unlock(cache, &cache->clean);
1132
page_transition_unlock(cache, page);
1133
@@ -1213,7 +1230,7 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1230
// at this point we have the clean lock
1231
}
1232
else
1216
- pgc_queue_lock(cache, &cache->clean);
1233
+ pgc_queue_lock(cache, &cache->clean, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1234
1235
timing_dbengine_evict_step(TIMING_STEP_DBENGINE_EVICT_LOCK);
1236
@@ -1242,7 +1259,7 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1259
// we can delete this page
1260
1261
// remove it from the clean list
1245
- pgc_queue_del(cache, &cache->clean, page, true);
1262
+ pgc_queue_del(cache, &cache->clean, page, true, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1263
1264
__atomic_add_fetch(&cache->stats.evicting_entries, 1, __ATOMIC_RELAXED);
1265
__atomic_add_fetch(&cache->stats.evicting_size, page->assumed_size, __ATOMIC_RELAXED);
@@ -1388,7 +1405,7 @@ static bool evict_pages_with_filter(PGC *cache, size_t max_skip, size_t max_evic
1405
} while(all_of_them || (total_pages_evicted < max_evict && total_pages_relocated < max_skip));
1406
1407
if(all_of_them && !filter) {
1391
- pgc_queue_lock(cache, &cache->clean);
1408
+ pgc_queue_lock(cache, &cache->clean, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1409
size_t entries = __atomic_load_n(&cache->clean.stats->entries, __ATOMIC_RELAXED);
1410
if(entries) {
1411
nd_log_limit_static_global_var(erl, 1, 0);
@@ -1491,9 +1508,9 @@ static PGC_PAGE *page_add(PGC *cache, PGC_ENTRY *entry, bool *added) {
1508
pgc_index_write_unlock(cache, partition);
1509
1510
if (entry->hot)
1494
- page_set_hot(cache, page);
1511
+ page_set_hot(cache, page, PGC_QUEUE_LOCK_PRIO_COLLECTORS);
1512
else
1496
- page_set_clean(cache, page, false, false);
1513
+ page_set_clean(cache, page, false, false, PGC_QUEUE_LOCK_PRIO_EVICTORS);
1514
1515
PGC_REFERENCED_PAGES_PLUS1(cache, page);
1516
@@ -1706,7 +1723,7 @@ cleanup:
1723
}
1724
1725
static void all_hot_pages_to_dirty(PGC *cache, Word_t section) {
1709
- pgc_queue_lock(cache, &cache->hot);
1726
+ pgc_queue_lock(cache, &cache->hot, PGC_QUEUE_LOCK_PRIO_COLLECTORS);
1727
1728
bool first = true;
1729
Word_t last_section = (section == PGC_SECTION_ALL) ? 0 : section;
@@ -1722,7 +1739,7 @@ static void all_hot_pages_to_dirty(PGC *cache, Word_t section) {
1739
PGC_PAGE *next = page->link.next;
1740
1741
if(page_acquire(cache, page)) {
1725
- page_set_dirty(cache, page, true);
1742
+ page_set_dirty(cache, page, true, PGC_QUEUE_LOCK_PRIO_COLLECTORS);
1743
page_release(cache, page, false);
1744
// page ptr may be invalid now
1745
}
@@ -1750,7 +1767,7 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1767
// we got the lock at this point
1768
}
1769
else
1753
- pgc_queue_lock(cache, &cache->dirty);
1770
+ pgc_queue_lock(cache, &cache->dirty, PGC_QUEUE_LOCK_PRIO_FLUSHERS);
1771
1772
size_t optimal_flush_size = cache->config.max_dirty_pages_per_call;
1773
size_t dirty_version_at_entry = cache->dirty.version;
@@ -1847,7 +1864,7 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1864
__atomic_add_fetch(&cache->stats.flushing_size, tpg->assumed_size, __ATOMIC_RELAXED);
1865
1866
// remove it from the dirty list
1850
- pgc_queue_del(cache, &cache->dirty, tpg, true);
1867
+ pgc_queue_del(cache, &cache->dirty, tpg, true, PGC_QUEUE_LOCK_PRIO_FLUSHERS);
1868
1869
pages_removed_dirty_size += tpg->assumed_size;
1870
pages_removed_dirty++;
@@ -1914,7 +1931,7 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1931
if(!tpg->accesses)
1932
pages_to_evict++;
1933
1917
- page_set_clean(cache, tpg, true, false);
1934
+ page_set_clean(cache, tpg, true, false, PGC_QUEUE_LOCK_PRIO_FLUSHERS);
1935
page_transition_unlock(cache, tpg);
1936
page_release(cache, tpg, false);
1937
// tpg ptr may be invalid now
@@ -1934,7 +1951,7 @@ static bool flush_pages(PGC *cache, size_t max_flushes, Word_t section, bool wai
1951
}
1952
}
1953
else {
1937
- pgc_queue_lock(cache, &cache->dirty);
1954
+ pgc_queue_lock(cache, &cache->dirty, PGC_QUEUE_LOCK_PRIO_FLUSHERS);
1955
have_dirty_lock = true;
1956
}
1957
}
@@ -1974,7 +1991,7 @@ static void *pgc_evict_thread(void *ptr) {
1991
job_id = new_job_id;
1992
1993
if (nd_thread_signaled_to_cancel())
1977
- return NULL;
1994
+ break;
1995
1996
evict_pages(cache, 0, 0, true, false);
1997
@@ -2081,9 +2098,15 @@ PGC *pgc_create(const char *name,
2098
#endif
2099
2100
2101
+#if defined(PGC_QUEUE_LOCK_AS_WAITING_QUEUE)
2102
+ cache->hot.wq = waiting_queue_create();
2103
+ cache->dirty.wq = waiting_queue_create();
2104
+ cache->clean.wq = waiting_queue_create();
2105
+#else
2106
spinlock_init(&cache->hot.spinlock);
2107
spinlock_init(&cache->dirty.spinlock);
2108
spinlock_init(&cache->clean.spinlock);
2109
+#endif
2110
2111
cache->hot.flags = PGC_PAGE_HOT;
2112
cache->hot.linked_list_in_sections_judy = true;
@@ -2150,6 +2173,11 @@ void pgc_destroy(PGC *cache) {
2173
#endif
2174
}
2175
2176
+#if defined(PGC_QUEUE_LOCK_AS_WAITING_QUEUE)
2177
+ waiting_queue_destroy(cache->hot.wq);
2178
+ waiting_queue_destroy(cache->dirty.wq);
2179
+ waiting_queue_destroy(cache->clean.wq);
2180
+#endif
2181
freez(cache->index);
2182
freez(cache);
2183
}
@@ -2180,7 +2208,7 @@ void pgc_page_hot_to_dirty_and_release(PGC *cache, PGC_PAGE *page, bool never_fl
2208
//#endif
2209
2210
// make page dirty
2183
- page_set_dirty(cache, page, false);
2211
+ page_set_dirty(cache, page, false, PGC_QUEUE_LOCK_PRIO_COLLECTORS);
2212
2213
// release the page
2214
page_release(cache, page, true);
@@ -2425,7 +2453,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2453
__atomic_add_fetch(&rrdeng_cache_efficiency_stats.journal_v2_indexing_started, 1, __ATOMIC_RELAXED);
2454
p2_add_fetch(&cache->stats.p2_workers_jv2_flush, 1);
2455
2428
- pgc_queue_lock(cache, &cache->hot);
2456
+ pgc_queue_lock(cache, &cache->hot, PGC_QUEUE_LOCK_PRIO_OTHERS);
2457
2458
Pvoid_t JudyL_metrics = NULL;
2459
Pvoid_t JudyL_extents_pos = NULL;
@@ -2556,7 +2584,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2584
}
2585
2586
yield_the_processor(); // do not lock too aggressively
2559
- pgc_queue_lock(cache, &cache->hot);
2587
+ pgc_queue_lock(cache, &cache->hot, PGC_QUEUE_LOCK_PRIO_OTHERS);
2588
}
2589
2590
spinlock_unlock(&sp->migration_to_v2_spinlock);
@@ -2580,7 +2608,7 @@ void pgc_open_cache_to_journal_v2(PGC *cache, Word_t section, unsigned datafile_
2608
2609
// balance-parents: transition from hot to clean directly
2610
yield_the_processor(); // do not lock too aggressively
2583
- page_set_clean(cache, pi->page, true, false);
2611
+ page_set_clean(cache, pi->page, true, false, PGC_QUEUE_LOCK_PRIO_OTHERS);
2612
page_transition_unlock(cache, pi->page);
2613
page_release(cache, pi->page, true);
2614
@@ -2631,7 +2659,7 @@ void pgc_open_evict_clean_pages_of_datafile(PGC *cache, struct rrdengine_datafil
2659
size_t pgc_count_clean_pages_having_data_ptr(PGC *cache, Word_t section, void *ptr) {
2660
size_t found = 0;
2661
2634
- pgc_queue_lock(cache, &cache->clean);
2662
+ pgc_queue_lock(cache, &cache->clean, PGC_QUEUE_LOCK_PRIO_OTHERS);
2663
for(PGC_PAGE *page = cache->clean.base; page ;page = page->link.next)
2664
found += (page->data == ptr && page->section == section) ? 1 : 0;
2665
pgc_queue_unlock(cache, &cache->clean);
@@ -2642,7 +2670,7 @@ size_t pgc_count_clean_pages_having_data_ptr(PGC *cache, Word_t section, void *p
2670
size_t pgc_count_hot_pages_having_data_ptr(PGC *cache, Word_t section, void *ptr) {
2671
size_t found = 0;
2672
2645
- pgc_queue_lock(cache, &cache->hot);
2673
+ pgc_queue_lock(cache, &cache->hot, PGC_QUEUE_LOCK_PRIO_OTHERS);
2674
Pvoid_t *section_pages_pptr = JudyLGet(cache->hot.sections_judy, section, PJE0);
2675
if(section_pages_pptr) {
2676
struct section_pages *sp = *section_pages_pptr;
src/database/rrdlabels.c
+2
-5
@@ -168,14 +168,11 @@ static void delete_label(RRDLABEL *label)
168
if (refcount == 0) {
169
JudyAllocThreadPulseReset();
170
171
- int ret = JudyHSDel(&global_labels.JudyHS, (void *)label, sizeof(*label), PJE0);
171
+ JudyHSDel(&global_labels.JudyHS, (void *)label, sizeof(*label), PJE0);
172
173
int64_t judy_mem = JudyAllocThreadPulseGetAndReset();
174
175
- if (unlikely(ret == JERR))
176
- STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(*rrdlabel), 0);
177
- else
178
- STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(*rrdlabel), 0);
175
+ STATS_MINUS_MEMORY(&dictionary_stats_category_rrdlabels, judy_mem, sizeof(*rrdlabel), 0);
176
string_freez(label->index.key);
177
string_freez(label->index.value);
178
freez(rrdlabel);
src/libnetdata/local-sockets/local-sockets.h
+2
-2
@@ -472,8 +472,8 @@ static inline const char *local_sockets_protocol_name(LOCAL_SOCKET *n) {
472
static inline void local_listeners_print_socket(LS_STATE *ls __maybe_unused, const LOCAL_SOCKET *nn, void *data __maybe_unused) {
473
LOCAL_SOCKET *n = (LOCAL_SOCKET *)nn;
474
475
- char local_address[INET6_ADDRSTRLEN];
476
- char remote_address[INET6_ADDRSTRLEN];
475
+ char local_address[INET6_ADDRSTRLEN] = "";
476
+ char remote_address[INET6_ADDRSTRLEN] = "";
477
478
if(n->local.family == AF_INET) {
479
ipv4_address_to_txt(n->local.ip.ipv4, local_address);
src/libnetdata/log/systemd-cat-native.c
+5
-5
@@ -231,13 +231,13 @@ static CURLcode journal_remote_send_buffer(CURL* curl, BUFFER *msg) {
231
if(verbose)
232
log_message_to_stderr(msg, "REMOTE");
233
234
- struct upload_data upload = {0};
235
-
234
if (!curl || !buffer_strlen(msg))
235
return CURLE_FAILED_INIT;
236
239
- upload.data = (char *) buffer_tostring(msg);
240
- upload.length = buffer_strlen(msg);
237
+ struct upload_data upload = {
238
+ .data = (char *) buffer_tostring(msg),
239
+ .length = buffer_strlen(msg),
240
+ };
241
242
curl_easy_setopt(curl, CURLOPT_READDATA, &upload);
243
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, (curl_off_t)upload.length);
@@ -290,7 +290,7 @@ static log_to_journal_remote_ret_t log_input_to_journal_remote(const char *url,
290
fprintf(stderr, "WARNING: cannot read '%s'. Will generate a random _MACHINE_ID.\n", MACHINE_ID_PATH);
291
nd_uuid_t uuid;
292
uuid_generate_random(uuid);
293
- uuid_unparse_lower_compact(uuid, global_boot_id);
293
+ uuid_unparse_lower_compact(uuid, global_machine_id);
294
}
295
296
if(global_stream_id[0] == '\0') {
src/libnetdata/socket/poll-events.c
+3
-6
@@ -280,12 +280,9 @@ static int poll_process_new_tcp_connection(POLLINFO *pi, time_t now) {
280
"POLLFD: LISTENER: accept() failed.");
281
282
}
283
- else if(is_socket_closed(nfd)) {
284
- nd_log_limit_static_global_var(erl, 10, 1000);
285
- nd_log_limit(&erl, NDLS_DAEMON, NDLP_ERR,
286
- "POLLFD: LISTENER: received client socket %d is closed on accept(), dropping connection", nfd);
287
- close(nfd);
288
- }
283
+ else if(is_socket_closed(nfd))
284
+ close(nfd);
285
+
286
else {
287
// accept ok
288
src/libnetdata/socket/socket.c
+8
-4
@@ -159,12 +159,14 @@ int sock_enlarge_rcv_buf(int fd) {
159
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, ¤t_bs, &optlen) == 0) {
160
// Set the buffer size only if it's smaller than the desired size
161
if (current_bs < bs) {
162
- setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bs, sizeof(bs));
162
+ if(setsockopt(fd, SOL_SOCKET, SO_RCVBUF, &bs, sizeof(bs)) != 0)
163
+ return -1;
164
165
// Re-check the buffer size after attempting to set it
166
if (getsockopt(fd, SOL_SOCKET, SO_RCVBUF, ¤t_bs, &optlen) == 0)
167
ret = current_bs;
167
- } else {
168
+ }
169
+ else {
170
// Current buffer size is already large enough
171
ret = current_bs;
172
}
@@ -184,12 +186,14 @@ int sock_enlarge_snd_buf(int fd) {
186
if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, ¤t_bs, &optlen) == 0) {
187
// Set the buffer size only if it's smaller than the desired size
188
if (current_bs < bs) {
187
- setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bs, sizeof(bs));
189
+ if(setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &bs, sizeof(bs)) != 0)
190
+ return -1;
191
192
// Re-check the buffer size after attempting to set it
193
if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, ¤t_bs, &optlen) == 0)
194
ret = current_bs;
192
- } else {
195
+ }
196
+ else {
197
// Current buffer size is already large enough
198
ret = current_bs;
199
}
src/libnetdata/waiting-queue/waiting-queue.c
+101
-34
@@ -2,19 +2,75 @@
2
3
#include "waiting-queue.h"
4
5
+// #define WAITING_QUEUE_USE_FUTEX 1
6
+
7
+#if defined(WAITING_QUEUE_USE_FUTEX)
8
+#include <linux/futex.h>
9
+#include <sys/syscall.h>
10
+
11
+typedef int WQ_COND;
12
+typedef SPINLOCK WQ_MUTEX;
13
+
14
+static inline int futex_wait(int *uaddr, int val) {
15
+ return syscall(SYS_futex, uaddr, FUTEX_WAIT_PRIVATE, val, NULL, NULL, 0);
16
+}
17
+
18
+static inline int futex_wake(int *uaddr, int n) {
19
+ return syscall(SYS_futex, uaddr, FUTEX_WAKE_PRIVATE, n, NULL, NULL, 0);
20
+}
21
+
22
+static int WQ_COND_init(WQ_COND *cond) { *(cond) = 0; return 0; }
23
+#define WQ_COND_destroy(cond) debug_dummy()
24
+
25
+static inline int WQ_COND_wait(WQ_COND *cond, WQ_MUTEX *mutex) {
26
+ int value = *cond;
27
+ spinlock_unlock(mutex);
28
+ futex_wait(cond, value);
29
+ spinlock_lock(mutex);
30
+ return 0;
31
+}
32
+
33
+static inline int WQ_COND_signal(WQ_COND *cond) {
34
+ __atomic_add_fetch(cond, 1, __ATOMIC_SEQ_CST);
35
+ futex_wake(cond, 1);
36
+ return 0;
37
+}
38
+
39
+#define WQ_MUTEX_init(mutex) ({ spinlock_init(mutex) ; 0; })
40
+#define WQ_MUTEX_destroy(mutex) debug_dummy()
41
+#define WQ_MUTEX_lock(mutex) spinlock_lock(mutex)
42
+#define WQ_MUTEX_unlock(mutex) spinlock_unlock(mutex)
43
+
44
+#else // !USE_FUTEX
45
+
46
+typedef uv_cond_t WQ_COND;
47
+typedef uv_mutex_t WQ_MUTEX;
48
+
49
+#define WQ_COND_init(cond) uv_cond_init(cond)
50
+#define WQ_COND_destroy(cond) uv_cond_destroy(cond)
51
+#define WQ_COND_wait(cond, mutex) uv_cond_wait(cond, mutex)
52
+#define WQ_COND_signal(cond) uv_cond_signal(cond)
53
+
54
+#define WQ_MUTEX_init(mutex) uv_mutex_init(mutex)
55
+#define WQ_MUTEX_destroy(mutex) uv_mutex_destroy(mutex)
56
+#define WQ_MUTEX_lock(mutex) uv_mutex_lock(mutex)
57
+#define WQ_MUTEX_unlock(mutex) uv_mutex_unlock(mutex)
58
+
59
+#endif // USE_FUTEX
60
+
61
typedef struct waiting_thread {
6
- uv_cond_t cond; // condition variable for this thread
62
+ WQ_COND cond; // condition variable for this thread
63
usec_t waiting_since_ut; // when we started waiting
8
- Word_t order;
64
+ Word_t priority;
65
struct waiting_thread *prev, *next;
66
} WAITING_THREAD;
67
68
struct waiting_queue {
13
- uv_mutex_t mutex; // protect the queue structure
69
+ WQ_MUTEX mutex; // ensure acquirers and releasers are synchronized
70
Word_t last_seqno; // incrementing sequence counter
15
- WAITING_THREAD *list;
16
- size_t running; // number of threads currently running/waiting
17
- SPINLOCK spinlock;
71
+ SPINLOCK spinlock; // ensures there is only 1 runner at a time
72
+ REFCOUNT running; // number of threads, including the one holding the lock
73
+ WAITING_THREAD *list; // the list of threads waiting, not including the 1 holding the lock
74
};
75
76
// Determine available bits based on system word size
@@ -41,7 +97,7 @@ static inline Word_t key_get_seqno(Word_t key) {
97
WAITING_QUEUE *waiting_queue_create(void) {
98
WAITING_QUEUE *wq = callocz(1, sizeof(WAITING_QUEUE));
99
44
- int ret = uv_mutex_init(&wq->mutex);
100
+ int ret = WQ_MUTEX_init(&wq->mutex);
101
if(ret != 0) {
102
freez(wq);
103
return NULL;
@@ -57,15 +113,15 @@ void waiting_queue_destroy(WAITING_QUEUE *wq) {
113
if(!wq) return;
114
115
if(wq->running)
60
- fatal("WAITING_QUEUE: destroying waiting queue that still has %zu threads running/waiting", wq->running);
116
+ fatal("WAITING_QUEUE: destroying waiting queue that still has %d threads running/waiting", wq->running);
117
62
- uv_mutex_destroy(&wq->mutex);
118
+ WQ_MUTEX_destroy(&wq->mutex);
119
freez(wq);
120
}
121
122
static inline void WAITERS_SET(WAITING_QUEUE *wq, WAITING_THREAD *wt) {
123
for(WAITING_THREAD *t = wq->list ; t ;t = t->next) {
68
- if(wt->order < t->order) {
124
+ if(wt->priority < t->priority) {
125
DOUBLE_LINKED_LIST_INSERT_ITEM_BEFORE_UNSAFE(wq->list, t, wt, prev, next);
126
return;
127
}
@@ -83,24 +139,34 @@ static inline WAITING_THREAD *WAITERS_FIRST(WAITING_QUEUE *wq) {
139
140
static inline void WAITING_THREAD_init(WAITING_QUEUE *wq, WAITING_THREAD *wt, WAITING_QUEUE_PRIORITY priority) {
141
Word_t seqno = __atomic_add_fetch(&wq->last_seqno, 1, __ATOMIC_RELAXED);
86
- wt->order = make_key(priority, seqno);
142
+ wt->priority = make_key(priority, seqno);
143
wt->waiting_since_ut = now_monotonic_usec();
144
wt->prev = wt->next = NULL;
145
90
- int ret = uv_cond_init(&wt->cond);
146
+ int ret = WQ_COND_init(&wt->cond);
147
if(ret != 0)
148
fatal("WAITING_QUEUE: cannot initialize condition variable");
149
}
150
95
-static inline void WAITING_THREAD_cleanup(WAITING_QUEUE *wq __maybe_unused, WAITING_THREAD *wt) {
96
- uv_cond_destroy(&wt->cond);
151
+static inline void WAITING_THREAD_cleanup(WAITING_QUEUE *wq __maybe_unused, WAITING_THREAD *wt __maybe_unused) {
152
+ WQ_COND_destroy(&wt->cond);
153
}
154
99
-usec_t waiting_queue_wait(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority) {
155
+bool waiting_queue_try_acquire(WAITING_QUEUE *wq) {
156
+ if(__atomic_add_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 1 &&
157
+ spinlock_trylock(&wq->spinlock)) {
158
+ return true;
159
+ }
160
+
161
+ __atomic_sub_fetch(&wq->running, 1, __ATOMIC_RELAXED);
162
+ return false;
163
+}
164
+
165
+usec_t waiting_queue_acquire(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority) {
166
// Try fast path first - if we're the only one, just go
101
- if(__atomic_add_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 1) {
102
- if(spinlock_trylock(&wq->spinlock))
103
- return 0;
167
+ if(__atomic_add_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 1 &&
168
+ spinlock_trylock(&wq->spinlock)) {
169
+ return 0;
170
}
171
172
// Slow path - need to wait
@@ -108,7 +174,7 @@ usec_t waiting_queue_wait(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority) {
174
WAITING_THREAD wt;
175
WAITING_THREAD_init(wq, &wt, priority);
176
111
- uv_mutex_lock(&wq->mutex);
177
+ WQ_MUTEX_lock(&wq->mutex);
178
WAITERS_SET(wq, &wt);
179
180
// Wait for our turn
@@ -116,31 +182,32 @@ usec_t waiting_queue_wait(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority) {
182
if (WAITERS_FIRST(wq) == &wt && spinlock_trylock(&wq->spinlock))
183
break;
184
else
119
- uv_cond_wait(&wt.cond, &wq->mutex);
185
+ WQ_COND_wait(&wt.cond, &wq->mutex);
186
} while(true);
187
188
WAITERS_DEL(wq, &wt);
123
- uv_mutex_unlock(&wq->mutex);
189
+ WQ_MUTEX_unlock(&wq->mutex);
190
WAITING_THREAD_cleanup(wq, &wt);
191
192
return now_monotonic_usec() - wt.waiting_since_ut;
193
}
194
129
-void waiting_queue_done(WAITING_QUEUE *wq) {
130
- spinlock_unlock(&wq->spinlock);
131
-
195
+void waiting_queue_release(WAITING_QUEUE *wq) {
196
// Fast path if we're alone
133
- if(__atomic_sub_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 0)
197
+ if(__atomic_sub_fetch(&wq->running, 1, __ATOMIC_RELAXED) == 0) {
198
+ spinlock_unlock(&wq->spinlock);
199
return;
200
+ }
201
202
// Slow path - need to signal next in line
137
- uv_mutex_lock(&wq->mutex);
203
+ WQ_MUTEX_lock(&wq->mutex);
204
205
// Wake up next in line if any
206
if(wq->list)
141
- uv_cond_signal(&wq->list->cond);
207
+ WQ_COND_signal(&wq->list->cond);
208
143
- uv_mutex_unlock(&wq->mutex);
209
+ spinlock_unlock(&wq->spinlock);
210
+ WQ_MUTEX_unlock(&wq->mutex);
211
}
212
213
size_t waiting_queue_waiting(WAITING_QUEUE *wq) {
@@ -183,8 +250,8 @@ static int unittest_functional(void) {
250
251
// Test 1: Fast path should work with no contention
252
fprintf(stderr, " Test 1: Fast path - no contention: ");
186
- usec_t wait_time = waiting_queue_wait(wq, WAITING_QUEUE_PRIO_NORMAL);
187
- waiting_queue_done(wq);
253
+ usec_t wait_time = waiting_queue_acquire(wq, WAITING_QUEUE_PRIO_NORMAL);
254
+ waiting_queue_release(wq);
255
if(wait_time != 0) {
256
fprintf(stderr, "FAILED (waited %"PRIu64" usec)\n", wait_time);
257
errors++;
@@ -210,8 +277,8 @@ static int unittest_functional(void) {
277
WAITERS_DEL(wq, wt);
278
__atomic_sub_fetch(&wq->running, 1, __ATOMIC_RELAXED);
279
213
- WAITING_QUEUE_PRIORITY prio = key_get_priority(wt->order);
214
- Word_t seqno = key_get_seqno(wt->order);
280
+ WAITING_QUEUE_PRIORITY prio = key_get_priority(wt->priority);
281
+ Word_t seqno = key_get_seqno(wt->priority);
282
283
prio_counts[prio]++;
284
if(prio < last_prio) {
@@ -265,7 +332,7 @@ static void *stress_thread(void *arg) {
332
bool *stop_flag = args->stop_flag;
333
334
while(!__atomic_load_n(stop_flag, __ATOMIC_ACQUIRE)) {
268
- usec_t wait_time = waiting_queue_wait(wq, stats->priority);
335
+ usec_t wait_time = waiting_queue_acquire(wq, stats->priority);
336
stats->executions++;
337
stats->total_wait_time += wait_time;
338
if(wait_time > stats->max_wait_time)
@@ -274,7 +341,7 @@ static void *stress_thread(void *arg) {
341
if(with_sleep)
342
tinysleep();
343
277
- waiting_queue_done(wq);
344
+ waiting_queue_release(wq);
345
}
346
347
return NULL;
src/libnetdata/waiting-queue/waiting-queue.h
+6
-4
@@ -4,7 +4,6 @@
4
#define NETDATA_WAITING_QUEUE_H
5
6
#include "libnetdata/libnetdata.h"
7
-#include <uv.h>
7
8
/*
9
* WAITING QUEUE
@@ -43,16 +42,19 @@ WAITING_QUEUE *waiting_queue_create(void);
42
// Destroy a waiting queue - must be empty
43
void waiting_queue_destroy(WAITING_QUEUE *wq);
44
45
+// Returns true when the queue is acquired
46
+bool waiting_queue_try_acquire(WAITING_QUEUE *wq);
47
+
48
// Returns when it is our turn to run
49
// Returns time spent waiting in microseconds
48
-usec_t waiting_queue_wait(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority);
50
+usec_t waiting_queue_acquire(WAITING_QUEUE *wq, WAITING_QUEUE_PRIORITY priority);
51
52
// Mark that we are done - wakes up the next in line
51
-void waiting_queue_done(WAITING_QUEUE *wq);
53
+void waiting_queue_release(WAITING_QUEUE *wq);
54
55
// Return the number of threads currently waiting
56
size_t waiting_queue_waiting(WAITING_QUEUE *wq);
57
58
int unittest_waiting_queue(void);
59
58
-#endif // NETDATA_WAITING_QUEUE_H
\ No newline at end of file
60
+#endif // NETDATA_WAITING_QUEUE_H
src/streaming/stream-sender-commit.c
+9
-7
@@ -68,9 +68,11 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
68
if (unlikely(!src || !src_len))
69
return;
70
71
- waiting_queue_wait(s->wait_queue, (s->host->stream.rcv.status.tid == gettid_cached()
72
- || s->host->stream.snd.status.tid == gettid_cached()) ?
73
- WAITING_QUEUE_PRIO_HIGH : WAITING_QUEUE_PRIO_NORMAL);
71
+ waiting_queue_acquire(
72
+ s->wait_queue,
73
+ (s->host->stream.rcv.status.tid == gettid_cached() || s->host->stream.snd.status.tid == gettid_cached()) ?
74
+ WAITING_QUEUE_PRIO_HIGH :
75
+ WAITING_QUEUE_PRIO_NORMAL);
76
stream_sender_lock(s);
77
78
// copy the sequence number of sender buffer recreates, while having our lock
@@ -85,7 +87,7 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
87
sender_buffer_destroy(commit);
88
89
stream_sender_unlock(s);
88
- waiting_queue_done(s->wait_queue);
90
+ waiting_queue_release(s->wait_queue);
91
return;
92
}
93
@@ -181,7 +183,7 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
183
msg = s->thread.msg;
184
185
stream_sender_unlock(s);
184
- waiting_queue_done(s->wait_queue);
186
+ waiting_queue_release(s->wait_queue);
187
188
if (enable_sending) {
189
msg.opcode = STREAM_OPCODE_SENDER_POLLOUT;
@@ -193,7 +195,7 @@ void sender_buffer_commit(struct sender_state *s, BUFFER *wb, struct sender_buff
195
overflow_with_lock: {
196
msg = s->thread.msg;
197
stream_sender_unlock(s);
196
- waiting_queue_done(s->wait_queue);
198
+ waiting_queue_release(s->wait_queue);
199
msg.opcode = STREAM_OPCODE_SENDER_BUFFER_OVERFLOW;
200
stream_sender_send_opcode(s, msg);
201
nd_log_limit_static_global_var(erl, 1, 0);
@@ -209,7 +211,7 @@ compression_failed_with_lock: {
211
stream_compression_deactivate(s);
212
msg = s->thread.msg;
213
stream_sender_unlock(s);
212
- waiting_queue_done(s->wait_queue);
214
+ waiting_queue_release(s->wait_queue);
215
msg.opcode = STREAM_OPCODE_SENDER_RECONNECT_WITHOUT_COMPRESSION;
216
stream_sender_send_opcode(s, msg);
217
nd_log_limit_static_global_var(erl, 1, 0);
src/streaming/stream-sender.c
+1
-1
@@ -371,7 +371,7 @@ static void stream_sender_log_disconnection(struct stream_thread *sth, struct se
371
ND_LOG_STACK_PUSH(lgs);
372
373
nd_log(NDLS_DAEMON, NDLP_NOTICE,
374
- "STREAM SND[%zu] '%s' [to %s]: sender disconnected from parent, reason: %s (replication in: %u, out: %u, pending: %u)",
374
+ "STREAM SND[%zu] '%s' [to %s]: sender disconnected from parent, reason: %s (replication in: %u, out: %u, pending: %zu)",
375
sth->id, rrdhost_hostname(s->host), s->remote_ip, stream_handshake_error_to_string(reason),
376
s->host->stream.snd.status.replication.counter_in, s->host->stream.snd.status.replication.counter_out,
377
dictionary_entries(s->replication.requests));
src/web/server/web_client_cache.c
+5
-1
@@ -17,6 +17,8 @@
17
// the number of currently connected clients.
18
19
static struct clients_cache {
20
+ unsigned long long client_id;
21
+
22
struct {
23
SPINLOCK spinlock;
24
struct web_client *head; // the structures of the currently connected clients
@@ -103,10 +105,12 @@ struct web_client *web_client_get_from_cache(void) {
105
w = web_client_create(&netdata_buffers_statistics.buffers_web);
106
spinlock_lock(&web_clients_cache.used.spinlock);
107
106
- w->id = pulse_web_client_connected();
108
+ w->id = __atomic_add_fetch(&web_clients_cache.client_id, 1, __ATOMIC_RELAXED);
109
web_clients_cache.used.allocated++;
110
}
111
112
+ pulse_web_client_connected();
113
+
114
// link it to used web clients
115
DOUBLE_LINKED_LIST_PREPEND_ITEM_UNSAFE(web_clients_cache.used.head, w, cache.prev, cache.next);
116
web_clients_cache.used.count++;