blob: 21d3adeeaa2ec8876ada25e9fe4070db64c7245b (
plain) (
tree)
|
|
/**
* 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);
}
|