aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/hdr_histogram/src/hdr_writer_reader_phaser.c
blob: 21d3adeeaa2ec8876ada25e9fe4070db64c7245b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/**
 * hdr_writer_reader_phaser.h
 * Written by Michael Barker and released to the public domain,
 * as explained at http://creativecommons.org/publicdomain/zero/1.0/
 */

#include <stdint.h>
#include <stdbool.h>
#include <errno.h>

#include "hdr_atomic.h"
#include "hdr_thread.h"

#include "hdr_writer_reader_phaser.h"

static int64_t _hdr_phaser_get_epoch(int64_t* field)
{
    return hdr_atomic_load_64(field);
}

static void _hdr_phaser_set_epoch(int64_t* field, int64_t val)
{
    hdr_atomic_store_64(field, val);
}

static int64_t _hdr_phaser_reset_epoch(int64_t* field, int64_t initial_value)
{
    return hdr_atomic_exchange_64(field, initial_value);
}

int hdr_writer_reader_phaser_init(struct hdr_writer_reader_phaser* p)
{
    if (NULL == p)
    {
        return EINVAL;
    }

    p->start_epoch = 0;
    p->even_end_epoch = 0;
    p->odd_end_epoch = INT64_MIN;
    p->reader_mutex = hdr_mutex_alloc();

    if (!p->reader_mutex)
    {
        return ENOMEM;
    }

    int rc = hdr_mutex_init(p->reader_mutex);
    if (0 != rc)
    {
        return rc;
    }

    // TODO: Should I fence here.

    return 0;
}

void hdr_writer_reader_phaser_destory(struct hdr_writer_reader_phaser* p)
{
    hdr_mutex_destroy(p->reader_mutex);
}

int64_t hdr_phaser_writer_enter(struct hdr_writer_reader_phaser* p)
{
    return hdr_atomic_add_fetch_64(&p->start_epoch, 1);
}

void hdr_phaser_writer_exit(
    struct hdr_writer_reader_phaser* p, int64_t critical_value_at_enter)
{
    int64_t* end_epoch = 
        (critical_value_at_enter < 0) ? &p->odd_end_epoch : &p->even_end_epoch;
    hdr_atomic_add_fetch_64(end_epoch, 1);
}

void hdr_phaser_reader_lock(struct hdr_writer_reader_phaser* p)
{
    hdr_mutex_lock(p->reader_mutex);
}

void hdr_phaser_reader_unlock(struct hdr_writer_reader_phaser* p)
{
    hdr_mutex_unlock(p->reader_mutex);
}

void hdr_phaser_flip_phase(
    struct hdr_writer_reader_phaser* p, int64_t sleep_time_ns)
{
    // TODO: is_held_by_current_thread
    unsigned int sleep_time_us = sleep_time_ns < 1000000000 ? (unsigned int) (sleep_time_ns / 1000) : 1000000;

    int64_t start_epoch = _hdr_phaser_get_epoch(&p->start_epoch);

    bool next_phase_is_even = (start_epoch < 0);

    // Clear currently used phase end epoch.
    int64_t initial_start_value;
    if (next_phase_is_even)
    {
        initial_start_value = 0;
        _hdr_phaser_set_epoch(&p->even_end_epoch, initial_start_value);
    }
    else
    {
        initial_start_value = INT64_MIN;
        _hdr_phaser_set_epoch(&p->odd_end_epoch, initial_start_value);
    }

    // Reset start value, indicating new phase.
    int64_t start_value_at_flip = 
        _hdr_phaser_reset_epoch(&p->start_epoch, initial_start_value);

    bool caught_up = false;
    do
    {
        int64_t* end_epoch =
            next_phase_is_even ? &p->odd_end_epoch : &p->even_end_epoch;

        caught_up = _hdr_phaser_get_epoch(end_epoch) == start_value_at_flip;

        if (!caught_up)
        {
            if (sleep_time_us <= 0)
            {
                hdr_yield();
            }
            else
            {
                hdr_usleep(sleep_time_us);
            }
        }
    }
    while (!caught_up);
}