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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
// Copyright © 2019-2020 VMware, Inc. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR MIT

//! Concurrent Node Replication (CNR) is a library which can be used to implement a
//! NUMA-aware version of any concurrent data structure. It takes in a
//! concurrent implementation of said data structure, and scales it out to
//! multiple cores and NUMA nodes by combining three techniques:
//! commutativity based work partitioning, operation logging, and flat combining..
//!
//! # How does it work
//! To replicate a concurrent data structure, one needs to implement the
//! [Dispatch](trait.Dispatch.html) trait for it. To map the operation to a log,
//! each operation ([ReadOperation](trait.Dispatch.html#associatedtype.ReadOperation)
//! and [WriteOperation](trait.Dispatch.html#associatedtype.WriteOperation))
//! needs to implement [LogMapper](trait.LogMapper.html) trait.
//! The following snippet implements [Dispatch](trait.Dispatch.html) for concurrent
//! [HashMap](https://docs.rs/chashmap/2.2.2/chashmap/struct.CHashMap.html)
//! as an example. A complete example (using [Replica](struct.Replica.html)
//! and [Log](struct.Log.html)) can be found in the
//! [examples](https://github.com/vmware/node-replication/tree/master/cnr/examples/hashmap.rs)
//! folder.
//!
//! ```
//! use cnr::Dispatch;
//! use cnr::LogMapper;
//! use chashmap::CHashMap;
//!
//! /// The replicated hashmap uses a concurrent hashmap internally.
//! pub struct CNRHashMap {
//!    storage: CHashMap<usize, usize>,
//! }
//!
//! /// We support a mutable put operation on the hashmap.
//! #[derive(Debug, PartialEq, Clone)]
//! pub enum Modify {
//!    Put(usize, usize),
//! }
//!
//! /// Application developer implements LogMapper for each mutable operation.
//! /// It is used to map the operation to one of the many logs. Commutative
//! /// operations can map to same or different log and conflicting operations
//! /// must map to same log.
//! impl LogMapper for Modify {
//!    fn hash(&self, nlogs: usize, logs: &mut Vec<usize>) {
//!       logs.clear();
//!       match self {
//!          Modify::Put(key, _val) => logs.push(*key % nlogs),
//!       }
//!    }
//! }
//!
//! /// We support an immutable read operation to lookup a key from the hashmap.
//! #[derive(Debug, PartialEq, Clone)]
//! pub enum Access {
//!    Get(usize),
//! }
//!
//! /// Application developer implements LogMapper for each immutable operation. It
//! /// is used to map the operation to one of the many log. Commutative operations
//! /// can go to same or different log and conflicts operations must map to same log.
//! impl LogMapper for Access {
//!    fn hash(&self, nlogs: usize, logs: &mut Vec<usize>) {
//!       logs.clear();
//!       match self {
//!          Access::Get(key) => logs.push(*key % nlogs),
//!       }
//!    }
//! }
//!
//! /// The Dispatch traits executes `ReadOperation` (our Access enum)
//! /// and `WriteOperation` (our Modify enum) against the replicated
//! /// data-structure.
//! impl Dispatch for CNRHashMap {
//!    type ReadOperation = Access;
//!    type WriteOperation = Modify;
//!    type Response = Option<usize>;
//!
//!    /// The `dispatch` function applies the immutable operations.
//!    fn dispatch(&self, op: Self::ReadOperation) -> Self::Response {
//!        match op {
//!            Access::Get(key) => self.storage.get(&key).map(|v| *v),
//!        }
//!    }
//!
//!    /// The `dispatch_mut` function applies the mutable operations.
//!    fn dispatch_mut(
//!        &self,
//!        op: Self::WriteOperation,
//!    ) -> Self::Response {
//!        match op {
//!            Modify::Put(key, value) => self.storage.insert(key, value),
//!        }
//!    }
//! }
//! ```
#![no_std]
#![feature(new_uninit)]
#![feature(get_mut_unchecked)]
#![feature(negative_impls)]
#![feature(try_reserve)]

#[cfg(test)]
extern crate std;
#[macro_use]
extern crate alloc;
extern crate core;

extern crate crossbeam_utils;

#[macro_use]
extern crate log as logging;

#[macro_use]
extern crate static_assertions;

mod context;
mod log;
mod replica;

pub use crate::log::{Log, MAX_REPLICAS_PER_LOG};
pub use replica::{Replica, ReplicaToken, MAX_THREADS_PER_REPLICA};

use alloc::vec::Vec;
use core::fmt::Debug;

/// Every data structure must implement [LogMapper](trait.LogMapper.html) trait
/// for [ReadOperation](trait.Dispatch.html#associatedtype.ReadOperation) and
/// [WriteOperation](trait.Dispatch.html#associatedtype.WriteOperation).
///
/// Data structure implement `hash` that is used to map each operation to a log.
/// All the conflicting operations must map to a single log and the commutative
/// operations can map to same or different logs based on the operation argument.
///
/// [Replica](struct.Replica.html) internally performs a modulo operation on `hash`
/// return value with the total number of logs. The data structure can implement
/// trait to return a value between 0 and (#logs-1) to avoid the modulo operation.
///
/// When the replica calls `hash`, the implementor can assume that the capacity
/// of `logs` >= `nlogs` and that `logs` is empty.
pub trait LogMapper {
    /// Method to convert the operation and it's arguments to a log number.
    fn hash(&self, nlogs: usize, logs: &mut Vec<usize>);
}

/// Trait that a data structure must implement to be usable with this library.
///
/// When this library executes a read-only operation against the data structure,
/// it invokes the `dispatch()` method with the operation as an argument.
///
/// When this library executes a write operation against the data structure, it
/// invokes the `dispatch_mut()` method with the operation as an argument.
pub trait Dispatch {
    /// A read-only operation. When executed against the data structure, an operation
    /// of this type must not mutate the data structure in anyway. Otherwise, the
    /// assumptions made by this library no longer hold.
    type ReadOperation: Sized + Clone + PartialEq + Debug + LogMapper;

    /// A write operation. When executed against the data structure, an operation of
    /// this type is allowed to mutate state. The library ensures that this is done so
    /// in a thread-safe manner.
    type WriteOperation: Sized + Clone + PartialEq + Debug + Send + LogMapper;

    /// The type on the value returned by the data structure when a `ReadOperation` or a
    /// `WriteOperation` successfully executes against it.
    type Response: Sized + Clone;

    /// Method on the data structure that allows a read-only operation to be
    /// executed against it.
    fn dispatch(&self, op: Self::ReadOperation) -> Self::Response;

    /// Method on the data structure that allows a write operation to be
    /// executed against it.
    fn dispatch_mut(&self, op: Self::WriteOperation) -> Self::Response;
}