forked from zkcrypto/bellman
-
Notifications
You must be signed in to change notification settings - Fork 120
/
locks.rs
153 lines (141 loc) · 4.46 KB
/
locks.rs
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
use fs2::FileExt;
use log::{debug, info, warn};
use std::fs::File;
use std::path::PathBuf;
const GPU_LOCK_NAME: &str = "bellman.gpu.lock";
const PRIORITY_LOCK_NAME: &str = "bellman.priority.lock";
fn tmp_path(filename: &str) -> PathBuf {
let mut p = std::env::temp_dir();
p.push(filename);
p
}
/// `GPULock` prevents two kernel objects to be instantiated simultaneously.
#[derive(Debug)]
pub struct GPULock(File);
impl GPULock {
pub fn lock() -> GPULock {
debug!("Acquiring GPU lock...");
let f = File::create(tmp_path(GPU_LOCK_NAME)).unwrap();
f.lock_exclusive().unwrap();
debug!("GPU lock acquired!");
GPULock(f)
}
}
impl Drop for GPULock {
fn drop(&mut self) {
debug!("GPU lock released!");
}
}
/// `PrioriyLock` is like a flag. When acquired, it means a high-priority process
/// needs to acquire the GPU really soon. Acquiring the `PriorityLock` is like
/// signaling all other processes to release their `GPULock`s.
/// Only one process can have the `PriorityLock` at a time.
#[derive(Debug)]
pub struct PriorityLock(File);
impl PriorityLock {
pub fn lock() -> PriorityLock {
debug!("Acquiring priority lock...");
let f = File::create(tmp_path(PRIORITY_LOCK_NAME)).unwrap();
f.lock_exclusive().unwrap();
debug!("Priority lock acquired!");
PriorityLock(f)
}
pub fn wait(priority: bool) {
if !priority {
File::create(tmp_path(PRIORITY_LOCK_NAME))
.unwrap()
.lock_exclusive()
.unwrap();
}
}
pub fn should_break(priority: bool) -> bool {
!priority
&& File::create(tmp_path(PRIORITY_LOCK_NAME))
.unwrap()
.try_lock_exclusive()
.is_err()
}
}
impl Drop for PriorityLock {
fn drop(&mut self) {
debug!("Priority lock released!");
}
}
use super::error::{GPUError, GPUResult};
use super::fft::FFTKernel;
use super::multiexp::MultiexpKernel;
use crate::bls::Engine;
use crate::domain::create_fft_kernel;
use crate::multiexp::create_multiexp_kernel;
macro_rules! locked_kernel {
($class:ident, $kern:ident, $func:ident, $name:expr) => {
pub struct $class<E>
where
E: Engine,
{
log_d: usize,
priority: bool,
kernel: Option<$kern<E>>,
}
impl<E> $class<E>
where
E: Engine,
{
pub fn new(log_d: usize, priority: bool) -> $class<E> {
$class::<E> {
log_d,
priority,
kernel: None,
}
}
fn init(&mut self) {
if self.kernel.is_none() {
PriorityLock::wait(self.priority);
info!("GPU is available for {}!", $name);
self.kernel = $func::<E>(self.log_d, self.priority);
}
}
fn free(&mut self) {
if let Some(_kernel) = self.kernel.take() {
warn!(
"GPU acquired by a high priority process! Freeing up {} kernels...",
$name
);
}
}
pub fn with<F, R>(&mut self, mut f: F) -> GPUResult<R>
where
F: FnMut(&mut $kern<E>) -> GPUResult<R>,
{
if std::env::var("BELLMAN_NO_GPU").is_ok() {
return Err(GPUError::GPUDisabled);
}
self.init();
loop {
if let Some(ref mut k) = self.kernel {
match f(k) {
Err(GPUError::GPUTaken) => {
self.free();
self.init();
}
Err(e) => {
warn!("GPU {} failed! Falling back to CPU... Error: {}", $name, e);
return Err(e);
}
Ok(v) => return Ok(v),
}
} else {
return Err(GPUError::KernelUninitialized);
}
}
}
}
};
}
locked_kernel!(LockedFFTKernel, FFTKernel, create_fft_kernel, "FFT");
locked_kernel!(
LockedMultiexpKernel,
MultiexpKernel,
create_multiexp_kernel,
"Multiexp"
);