1use crate::error::{result_from_raw, Error};
2use std::{ops::Deref, ptr::NonNull, sync::Arc};
3
4#[derive(Debug)]
8pub struct UniqueContext {
9 raw: NonNull<sys::ca_client_context>,
10}
11
12unsafe impl Send for UniqueContext {}
13
14impl UniqueContext {
15 pub fn new() -> Result<Self, Error> {
17 let prev = Self::current();
18 if !prev.is_null() {
19 Self::detach();
20 }
21 let ret = result_from_raw(unsafe {
22 sys::ca_context_create(
23 sys::ca_preemptive_callback_select::ca_enable_preemptive_callback,
24 )
25 })
26 .map(|()| {
27 let raw = Self::current();
28 Self::detach();
29 Self {
30 raw: NonNull::new(raw).unwrap(),
31 }
32 });
33 if let Some(prev) = NonNull::new(prev) {
34 Self::attach(prev);
35 }
36 ret
37 }
38 pub(crate) fn current() -> *mut sys::ca_client_context {
39 unsafe { sys::ca_current_context() }
40 }
41 fn attach(raw: NonNull<sys::ca_client_context>) {
42 unsafe { sys::ca_attach_context(raw.as_ptr()) };
43 }
44 fn detach() {
45 unsafe { sys::ca_detach_context() };
46 }
47
48 pub fn with<F: FnOnce() -> R, R>(&self, f: F) -> R {
52 let prev = Self::current();
53 if prev != self.raw.as_ptr() {
54 if !prev.is_null() {
55 Self::detach();
56 }
57 Self::attach(self.raw);
58 }
59 let ret = f();
60 if prev != self.raw.as_ptr() {
61 Self::detach();
62 if let Some(prev) = NonNull::new(prev) {
63 Self::attach(prev);
64 }
65 }
66 ret
67 }
68
69 pub(crate) fn flush_io(&self) {
73 self.with(|| result_from_raw(unsafe { sys::ca_flush_io() }))
74 .unwrap()
75 }
76}
77
78impl Drop for UniqueContext {
79 fn drop(&mut self) {
80 let prev = Self::current();
81 if !prev.is_null() {
82 Self::detach();
83 }
84 Self::attach(self.raw);
85 unsafe { sys::ca_context_destroy() };
86 if let Some(prev) = NonNull::new(prev) {
87 Self::attach(prev);
88 }
89 }
90}
91
92#[derive(Clone, Debug)]
94pub struct Context {
95 arc: Arc<UniqueContext>,
96}
97
98unsafe impl Send for Context {}
99
100impl Deref for Context {
101 type Target = UniqueContext;
102 fn deref(&self) -> &Self::Target {
103 &self.arc
104 }
105}
106
107impl Context {
108 pub fn new() -> Result<Self, Error> {
110 UniqueContext::new().map(|uniq| Self {
111 arc: Arc::new(uniq),
112 })
113 }
114}
115
116#[cfg(test)]
117mod tests {
118 use super::UniqueContext as Context;
119
120 #[test]
121 fn new() {
122 Context::new().unwrap();
123 }
124
125 #[test]
126 fn attach() {
127 assert!(Context::current().is_null());
128 let ctx = Context::new().unwrap();
129 assert!(Context::current().is_null());
130 ctx.with(|| {
131 assert_eq!(Context::current(), ctx.raw.as_ptr());
132 });
133 assert!(Context::current().is_null());
134 }
135
136 #[test]
137 fn reattach_same() {
138 assert!(Context::current().is_null());
139 let ctx = Context::new().unwrap();
140 assert!(Context::current().is_null());
141 ctx.with(|| {
142 assert_eq!(Context::current(), ctx.raw.as_ptr());
143 ctx.with(|| {
144 assert_eq!(Context::current(), ctx.raw.as_ptr());
145 });
146 assert_eq!(Context::current(), ctx.raw.as_ptr());
147 });
148 assert!(Context::current().is_null());
149 }
150
151 #[test]
152 fn reattach_different() {
153 assert!(Context::current().is_null());
154 let ctx = Context::new().unwrap();
155 assert!(Context::current().is_null());
156 ctx.with(|| {
157 assert_eq!(Context::current(), ctx.raw.as_ptr());
158 let other_ctx = Context::new().unwrap();
159 assert_eq!(Context::current(), ctx.raw.as_ptr());
160 other_ctx.with(|| {
161 assert_eq!(Context::current(), other_ctx.raw.as_ptr());
162 ctx.with(|| {
163 assert_eq!(Context::current(), ctx.raw.as_ptr());
164 });
165 assert_eq!(Context::current(), other_ctx.raw.as_ptr());
166 });
167 assert_eq!(Context::current(), ctx.raw.as_ptr());
168 drop(other_ctx);
169 assert_eq!(Context::current(), ctx.raw.as_ptr());
170 });
171 assert!(Context::current().is_null());
172 }
173}