]> git.lizzy.rs Git - rust.git/blob - src/shims/windows/sync.rs
implement Windows SRWLock shims
[rust.git] / src / shims / windows / sync.rs
1 use rustc_target::abi::Size;
2
3 use crate::*;
4
5 // Locks are pointer-sized pieces of data, initialized to 0.
6 // We use them to count readers, with usize::MAX representing the write-locked state.
7
8 fn deref_lock<'mir, 'tcx: 'mir>(
9     ecx: &mut MiriEvalContext<'mir, 'tcx>,
10     lock_op: OpTy<'tcx, Tag>,
11 ) -> InterpResult<'tcx, MPlaceTy<'tcx, Tag>> {
12     // `lock` is a pointer to `void*`; cast it to a pointer to `usize`.
13     let lock = ecx.deref_operand(lock_op)?;
14     let usize = ecx.machine.layouts.usize;
15     assert_eq!(lock.layout.size, usize.size);
16     Ok(lock.offset(Size::ZERO, MemPlaceMeta::None, usize, ecx)?)
17 }
18
19 impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriEvalContext<'mir, 'tcx> {}
20 pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx> {
21     #[allow(non_snake_case)]
22     fn AcquireSRWLockExclusive(
23         &mut self,
24         lock_op: OpTy<'tcx, Tag>,
25     ) -> InterpResult<'tcx> {
26         let this = self.eval_context_mut();
27         assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
28
29         let lock = deref_lock(this, lock_op)?;
30         let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
31         if lock_val == 0 {
32             // Currently not locked. Lock it.
33             let new_val = Scalar::from_machine_usize(this.machine_usize_max(), this);
34             this.write_scalar(new_val, lock.into())?;
35         } else {
36             // Lock is already held. This is a deadlock.
37             throw_machine_stop!(TerminationInfo::Deadlock);
38         }
39
40         Ok(())
41     }
42
43     #[allow(non_snake_case)]
44     fn TryAcquireSRWLockExclusive(
45         &mut self,
46         lock_op: OpTy<'tcx, Tag>,
47     ) -> InterpResult<'tcx, u8> {
48         let this = self.eval_context_mut();
49         assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
50
51         let lock = deref_lock(this, lock_op)?;
52         let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
53         if lock_val == 0 {
54             // Currently not locked. Lock it.
55             let new_val = this.machine_usize_max();
56             this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
57             Ok(1)
58         } else {
59             // Lock is already held.
60             Ok(0)
61         }
62     }
63
64     #[allow(non_snake_case)]
65     fn ReleaseSRWLockExclusive(
66         &mut self,
67         lock_op: OpTy<'tcx, Tag>,
68     ) -> InterpResult<'tcx> {
69         let this = self.eval_context_mut();
70         assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
71
72         let lock = deref_lock(this, lock_op)?;
73         let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
74         if lock_val == this.machine_usize_max() {
75             // Currently locked. Unlock it.
76             let new_val = 0;
77             this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
78         } else {
79             // Lock is not locked.
80             throw_ub_format!("calling ReleaseSRWLockExclusive on an SRWLock that is not exclusively locked");
81         }
82
83         Ok(())
84     }
85
86     #[allow(non_snake_case)]
87     fn AcquireSRWLockShared(
88         &mut self,
89         lock_op: OpTy<'tcx, Tag>,
90     ) -> InterpResult<'tcx> {
91         let this = self.eval_context_mut();
92         assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
93
94         let lock = deref_lock(this, lock_op)?;
95         let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
96         if lock_val == this.machine_usize_max() {
97             // Currently write locked. This is a deadlock.
98             throw_machine_stop!(TerminationInfo::Deadlock);
99         } else {
100             // Bump up read counter (cannot overflow as we just checkd against usize::MAX);
101             let new_val = lock_val+1;
102             // Make sure this does not reach the "write locked" flag.
103             if new_val == this.machine_usize_max() {
104                 throw_unsup_format!("SRWLock read-acquired too many times");
105             }
106             this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
107         }
108
109         Ok(())
110     }
111
112     #[allow(non_snake_case)]
113     fn TryAcquireSRWLockShared(
114         &mut self,
115         lock_op: OpTy<'tcx, Tag>,
116     ) -> InterpResult<'tcx, u8> {
117         let this = self.eval_context_mut();
118         assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
119
120         let lock = deref_lock(this, lock_op)?;
121         let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
122         if lock_val == this.machine_usize_max() {
123             // Currently write locked.
124             Ok(0)
125         } else {
126             // Bump up read counter (cannot overflow as we just checkd against usize::MAX);
127             let new_val = lock_val+1;
128             // Make sure this does not reach the "write locked" flag.
129             if new_val == this.machine_usize_max() {
130                 throw_unsup_format!("SRWLock read-acquired too many times");
131             }
132             this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
133             Ok(1)
134         }
135     }
136
137     #[allow(non_snake_case)]
138     fn ReleaseSRWLockShared(
139         &mut self,
140         lock_op: OpTy<'tcx, Tag>,
141     ) -> InterpResult<'tcx> {
142         let this = self.eval_context_mut();
143         assert_eq!(this.get_total_thread_count(), 1, "concurrency on Windows is not supported");
144
145         let lock = deref_lock(this, lock_op)?;
146         let lock_val = this.read_scalar(lock.into())?.to_machine_usize(this)?;
147         if lock_val == this.machine_usize_max() {
148             // Currently write locked. This is a UB.
149             throw_ub_format!("calling ReleaseSRWLockShared on write-locked SRWLock");
150         } else if lock_val == 0 {
151             // Currently not locked at all.
152             throw_ub_format!("calling ReleaseSRWLockShared on unlocked SRWLock");
153         } else {
154             // Decrement read counter (cannot overflow as we just checkd against 0);
155             let new_val = lock_val-1;
156             this.write_scalar(Scalar::from_machine_usize(new_val, this), lock.into())?;
157         }
158
159         Ok(())
160     }
161 }