Skip to content

Commit df8c153

Browse files
committed
Thread safety for error handler function pointer
1 parent af483c4 commit df8c153

File tree

4 files changed

+85
-24
lines changed

4 files changed

+85
-24
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ build = "build.rs"
1515
libc = "0.1.10"
1616
num = "0.1.27"
1717
time = "0.1.32"
18+
lazy_static = "0.2.1"
1819

1920
[build-dependencies]
2021
rustc-serialize = "0.3.16"

src/error.rs

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,44 @@
1+
use std::ops::{Deref, DerefMut};
12
use defines::AfError;
23
use std::error::Error;
4+
use std::marker::{Send, Sync};
5+
use std::sync::RwLock;
6+
37

48
pub type ErrorCallback = Fn(AfError);
59

6-
pub static mut HANDLE_ERROR: &'static ErrorCallback = &handle_error_general;
10+
11+
/// Wrap ErrorCallback function pointer inside a structure
12+
/// to enable implementing Send, Sync traits on it.
13+
pub struct Callback<'cblifetime> {
14+
pub cb: &'cblifetime ErrorCallback,
15+
}
16+
17+
18+
// Implement Send, Sync traits for Callback structure to
19+
// enable the user of Callback function pointer in conjunction
20+
// with threads using a mutex.
21+
unsafe impl<'cblifetime> Send for Callback<'cblifetime> {}
22+
unsafe impl<'cblifetime> Sync for Callback<'cblifetime> {}
23+
24+
25+
pub static DEFAULT_HANDLE_ERROR: &'static ErrorCallback = &handle_error_general;
26+
27+
28+
lazy_static! {
29+
static ref ERROR_HANDLER_LOCK: RwLock< Callback<'static> > =
30+
RwLock::new(Callback{cb: DEFAULT_HANDLE_ERROR});
31+
}
32+
733

834
#[allow(unused_must_use)]
9-
pub fn register_error_handler(callback: &'static ErrorCallback) {
10-
unsafe {
11-
HANDLE_ERROR = callback;
12-
}
35+
pub fn register_error_handler(cb_value: &'static ErrorCallback) {
36+
let mut gaurd = match ERROR_HANDLER_LOCK.write() {
37+
Ok(g) => g,
38+
Err(_)=> panic!("Failed to acquire lock to register error handler"),
39+
};
40+
41+
*gaurd.deref_mut() = Callback{cb:cb_value};
1342
}
1443

1544
pub fn handle_error_general(error_code: AfError) {
@@ -18,3 +47,15 @@ pub fn handle_error_general(error_code: AfError) {
1847
_ => panic!("Error message: {}", error_code.description()),
1948
}
2049
}
50+
51+
#[allow(non_snake_case)]
52+
pub fn HANDLE_ERROR(error_code: AfError) {
53+
let gaurd = match ERROR_HANDLER_LOCK.read() {
54+
Ok(g) => g,
55+
Err(_)=> panic!("Failed to acquire lock while handling FFI return value"),
56+
};
57+
58+
let func = gaurd.deref().cb;
59+
60+
func(error_code);
61+
}

src/lib.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22
html_favicon_url = "http://www.rust-lang.org/favicon.ico",
33
html_root_url = "http://arrayfire.com/docs/rust")]
44

5+
#[macro_use]
6+
extern crate lazy_static;
7+
58
pub use array::Array;
69
pub use array::{print};
710
mod array;
@@ -50,7 +53,7 @@ mod defines;
5053
pub use dim4::Dim4;
5154
mod dim4;
5255

53-
pub use error::{ErrorCallback, register_error_handler,handle_error_general};
56+
pub use error::{ErrorCallback, register_error_handler, handle_error_general};
5457
mod error;
5558

5659
pub use index::{Indexer, index, row, rows, col, cols, slice, slices,

tests/lib.rs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,58 @@
1+
#[macro_use]
12
extern crate arrayfire as af;
23

34
use std::error::Error;
45
use std::thread;
56
use std::time::Duration;
67
use af::*;
78

8-
pub fn handler_sample1(error_code: AfError) {
9-
println!("Error handler sample1");
10-
match error_code {
11-
AfError::SUCCESS => {}, /* No-op */
12-
_ => panic!("Error message: {}", error_code.description()),
13-
}
14-
}
9+
macro_rules! implement_handler {
10+
($fn_name:ident, $msg: expr) => (
1511

16-
pub fn handler_sample2(error_code: AfError) {
17-
println!("Error handler sample2");
18-
match error_code {
19-
AfError::SUCCESS => {}, /* No-op */
20-
_ => panic!("Error message: {}", error_code.description()),
21-
}
12+
pub fn $fn_name(error_code: AfError) {
13+
println!("{:?}", $msg);
14+
match error_code {
15+
AfError::SUCCESS => {}, /* No-op */
16+
_ => panic!("Error message: {}", error_code.description()),
17+
}
18+
}
19+
20+
)
2221
}
2322

23+
implement_handler!(handler_sample1, "Error Handler Sample1");
24+
implement_handler!(handler_sample2, "Error Handler Sample2");
25+
implement_handler!(handler_sample3, "Error Handler Sample3");
26+
implement_handler!(handler_sample4, "Error Handler Sample4");
27+
2428
pub static HANDLE1: &'static ErrorCallback = &handler_sample1;
2529
pub static HANDLE2: &'static ErrorCallback = &handler_sample2;
30+
pub static HANDLE3: &'static ErrorCallback = &handler_sample3;
31+
pub static HANDLE4: &'static ErrorCallback = &handler_sample4;
2632

33+
#[allow(unused_must_use)]
2734
#[test]
2835
fn check_error_handler_mutation() {
2936

3037
for i in 0..4 {
31-
thread::spawn(move || {
32-
if i%2==0 {
33-
register_error_handler(HANDLE2);
34-
} else {
35-
register_error_handler(HANDLE1);
38+
thread::Builder::new().name(format!("child {}",i+1).to_string()).spawn(move || {
39+
println!("{:?}", thread::current());
40+
match i {
41+
0 => register_error_handler(HANDLE1.clone()),
42+
1 => register_error_handler(HANDLE2.clone()),
43+
2 => register_error_handler(HANDLE3.clone()),
44+
3 => register_error_handler(HANDLE4.clone()),
45+
_ => panic!("Impossible scenario"),
3646
}
47+
//match i {
48+
// 1 => thread::sleep(Duration::from_millis(20)),
49+
// 2 => thread::sleep(Duration::from_millis(10)),
50+
// _ => (),
51+
//}
3752
});
3853
}
3954

55+
af::info();
4056
thread::sleep(Duration::from_millis(50));
4157

4258
}

0 commit comments

Comments
 (0)