instance_copy_on_write/
lib.rs

1/*-
2 * instance-copy-on-write - a synchronization primitive based on copy-on-write.
3 * 
4 * Copyright (C) 2025 Aleksandr Morozov alex@
5 * 
6 * The scram-rs crate can be redistributed and/or modified
7 * under the terms of either of the following licenses:
8 *
9 *   1. the Mozilla Public License Version 2.0 (the “MPL”) OR
10 *                     
11 *   2. EUROPEAN UNION PUBLIC LICENCE v. 1.2 EUPL © the European Union 2007, 2016
12 */
13
14 /// Type of the sync code.
15 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
16 pub enum ICoWLockTypes
17 {
18    /// Based on atomic and backoff.
19    Atomic,
20
21    /// based on the OS RwLock
22    RwLock,
23 }
24
25impl fmt::Display for ICoWLockTypes
26{
27    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
28    {
29        match self
30        {
31            Self::Atomic => 
32                write!(f, "atomic"),
33            Self::RwLock => 
34                write!(f, "rwlock"),
35        }
36    }
37}
38
39/// Errors which may be returned.
40#[derive(Copy, Clone, Debug, PartialEq, Eq)]
41pub enum ICoWError
42{
43    /// An attempt to write to the instance using non exclusive copy-on-write operation
44    /// while the exclusive is still going.
45    ExclusiveLockPending,
46
47    /// Is issued if "exponential backoff has completed and blocking the thread is advised".
48    WouldBlock,
49
50    /// Duplicate write operation prevented.
51    AlreadyUpdated,
52}
53
54impl fmt::Display for ICoWError
55{
56    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
57    {
58        match self
59        {
60            Self::ExclusiveLockPending => 
61                write!(f, "already exlcusivly locked"),
62            Self::WouldBlock => 
63                write!(f, "cannot perform try operation due to blocking"),
64            Self::AlreadyUpdated => 
65                write!(f, "the value has been updated before the current thread"),
66        }
67    }
68}
69
70extern crate crossbeam_utils;
71extern crate crossbeam_deque;
72
73/// A lightweight CoW implementation.
74#[cfg(all(target_has_atomic = "ptr", feature = "prefer_atomic"))]
75pub mod cow;
76
77/// A RwLock based copy-on-write.
78#[cfg(any(not(target_has_atomic = "ptr"), not(feature = "prefer_atomic")))]
79pub mod cow_mutex;
80
81use std::fmt;
82
83#[cfg(all(target_has_atomic = "ptr", feature = "prefer_atomic"))]
84pub use cow::{ICoW, ICoWRead, ICoWCopy, ICoWLock};
85
86#[cfg(any(not(target_has_atomic = "ptr"), not(feature = "prefer_atomic")))]
87pub use cow_mutex::{ICoW, ICoWRead, ICoWCopy, ICoWLock};
88
89
90#[cfg(test)]
91mod test
92{
93    use std::{sync::{mpsc, Arc, LazyLock}, time::{Duration, Instant}};
94
95    use super::*;
96
97
98
99
100    #[test]
101    fn test_3()
102    {
103        #[derive(Debug, Clone)]
104        struct Test { s: String };
105
106        let icow = ICoW::new(Test{ s: "test".into() });
107
108        for _ in 0..10
109        {
110            let s = Instant::now();
111
112            let read0 = icow.read();
113
114            let e = s.elapsed();
115
116            println!("{:?}", e);
117        }
118
119    }
120
121    #[test]
122    fn test_33()
123    {
124        #[derive(Debug, Clone)]
125        struct Test { s: String };
126
127        let icow = ICoW::new(Test{ s: "test".into() });
128
129        let write_ex = icow.try_clone_exclusivly().unwrap();
130
131        {
132        let write_ex_err = icow.try_clone_exclusivly();
133        assert_eq!(write_ex_err.is_none(), true);
134        }
135
136        let write_ex_err = icow.try_clone();
137        assert_eq!(write_ex_err.is_none(), true);
138
139        let read1 = icow.try_read();
140        assert_eq!(read1.is_none(), true);
141
142        drop(write_ex);
143
144        drop(icow);
145
146    }
147
148    #[test]
149    fn test_4()
150    {
151        #[derive(Debug, Clone)]
152        struct Test { s: u32 }
153
154        let icow = ICoW::new(Test{ s: 1 });
155
156        let read0 = icow.read();
157        let read1 = icow.read();
158
159        let mut excl_write = icow.try_clone_exclusivly().unwrap();
160
161        excl_write.item.s = 5;
162
163
164        excl_write.commit();
165
166        assert_eq!(read0.item.s, 1);
167
168        let read3 = icow.read();
169        assert_eq!(read3.item.s, 5);
170
171        let mut writing = icow.try_clone().unwrap();
172        writing.s = 4;
173
174        writing.commit().unwrap();
175
176        assert_eq!(read0.item.s, 1);
177        assert_eq!(read3.item.s, 5);
178
179        let read4 = icow.read();
180        assert_eq!(read4.s, 4);
181    }
182
183    #[test]
184    fn test_5()
185    {
186        #[derive(Debug, Clone)]
187        struct Test { s: u32 }
188
189        let icow = Arc::new(ICoW::new(Test{ s: 1 }));
190
191        let read0 = icow.read();
192        let read2 = icow.read();
193        
194        let c_icow = icow.clone();
195
196        let (se, rc) = mpsc::channel::<()>();
197        let handler0 = 
198            std::thread::spawn(move || 
199                {
200                    let mut lock0 = c_icow.try_clone_exclusivly().unwrap();
201
202                    se.send(()).unwrap();
203
204                    lock0.item.s = 5;
205
206                    std::thread::sleep(Duration::from_micros(2));
207
208                    lock0.commit();
209                }
210            );
211
212        rc.recv().unwrap();
213
214        let s = Instant::now();
215
216        let read1 = icow.read();
217        
218        let e = s.elapsed();
219
220        println!("{:?}", e);
221
222        
223        assert_eq!(read1.item.s, 5);
224        assert_eq!(read0.item.s, 1);
225
226        handler0.join().unwrap();
227
228        let weak0 = read0.weak();
229        let weak1 = read1.weak();
230
231        drop(read0);
232        drop(read1);
233
234        assert_eq!(weak0.upgrade().is_some(), true);
235        assert_eq!(weak1.upgrade().is_some(), true);
236
237        drop(read2);
238        assert_eq!(weak0.upgrade().is_none(), true);
239        assert_eq!(weak1.upgrade().is_some(), true);
240    }
241
242
243    #[test]
244    fn test_6()
245    {
246        #[derive(Debug, Clone)]
247        struct Test { s: u32 }
248
249        let icow = Arc::new(ICoW::new(Test{ s: 1 }));
250
251        let read0 = icow.read();
252        let read2 = icow.read();
253        
254        let c_icow = icow.clone();
255
256        let (se, rc) = mpsc::channel::<()>();
257        let handler0 = 
258            std::thread::spawn(move || 
259                {
260                    let read2 = c_icow.read();
261
262                    let mut lock0 = c_icow.try_clone_exclusivly().unwrap();
263
264                    se.send(()).unwrap();
265
266                    lock0.item.s = 5;
267
268                    std::thread::sleep(Duration::from_nanos(50));
269                    lock0.commit();
270
271                    let read3 = c_icow.read();
272
273                    assert_eq!(read2.item.s, 1);
274                    assert_eq!(read3.item.s, 5);
275                }
276            );
277
278        rc.recv().unwrap();
279
280        for _ in 0..100000000
281        {
282            let read1 = icow.read();
283
284            if read1.item.s == 1
285            {
286                continue;
287            }
288            else
289            {
290                break;
291            }
292        }
293
294        let read1 = icow.read();
295        assert_eq!(read1.item.s, 5);
296
297        handler0.join().unwrap();
298
299        return;
300    }
301
302
303    #[test]
304    fn test_7()
305    {
306        #[derive(Debug, Clone)]
307        struct TestStruct { s: u32 }
308
309        impl TestStruct
310        {
311            fn new(s: u32) -> Self
312            {
313                return Self{ s: s };
314            }
315        }
316
317        static VAL: LazyLock<ICoW<TestStruct>> = 
318            LazyLock::new(|| ICoW::new(TestStruct::new(1))); 
319        
320
321        
322        
323            let borrow1 = VAL.read();
324            assert_eq!(borrow1.item.s, 1);
325
326            let (mpsc_send, mpsc_rcv) = mpsc::channel::<u64>();
327            let (mpsc_send2, mpsc_rcv2) = mpsc::channel::<u64>();
328
329            let thread1 = 
330                std::thread::spawn(move ||
331                    {
332                        for _ in 0..1000
333                        {
334                            let _ = mpsc_rcv2.recv();
335                            let borrow1 = VAL.read();
336
337                            let mut transaction = VAL.try_clone_exclusivly().unwrap();
338
339                            transaction.item.s = 5;
340
341                            
342
343                            std::thread::sleep(Duration::from_nanos(1001));
344                            transaction.commit();
345                            let borrow2 = VAL.read();
346                            
347
348                            assert_eq!(borrow1.item.s, 1);
349
350                            assert_eq!(borrow2.item.s, 5);
351
352                            let _ = mpsc_send.send(1);
353                        }
354                    }
355                );
356            
357            
358
359            for x in 0..1000
360            {
361                println!("{}", x);
362                mpsc_send2.send(1).unwrap();
363                let _ = mpsc_rcv.recv();
364
365                let borrow1 = VAL.read();
366                assert_eq!(borrow1.item.s, 5);
367
368                let mut transaction = VAL.try_clone_exclusivly().unwrap();
369                transaction.item.s = 1;
370                transaction.commit();
371
372                let borrow1 = VAL.read();
373                assert_eq!(borrow1.item.s, 1);
374                
375            }
376            
377            
378
379            thread1.join().unwrap();
380
381            
382    }
383
384
385    #[test]
386    fn test_8()
387    {
388        #[derive(Debug, Clone)]
389        struct Test { s: u32 }
390
391        let icow = Arc::new(ICoW::new(Test{ s: 1 }));
392
393        for _ in 0..20
394        {
395            let read0 = icow.read();
396            let read2 = icow.read();
397            
398            let c_icow = icow.clone();
399
400            //let (se, rc) = mpsc::channel::<()>();
401            let handler0 = 
402                std::thread::spawn(move || 
403                    {
404                        let read2 = c_icow.read();
405
406                        let mut lock0 = c_icow.try_clone().unwrap();
407
408                        //se.send(()).unwrap();
409
410                        lock0.s = 5;
411
412                        std::thread::sleep(Duration::from_micros(1));
413                        lock0.commit_blocking().unwrap();
414
415                        let read3 = c_icow.read();
416
417                        assert_eq!(read2.item.s, 1);
418                        assert_eq!(read3.item.s, 5);
419                    }
420                );
421
422            //rc.recv().unwrap();
423
424            for i in 0..1000000000
425            {
426                let read1 = icow.read();
427
428                if read1.item.s == 1
429                {
430                    continue;
431                }
432                else
433                {
434                    println!("{}", i);
435                    break;
436                }
437            }
438
439            let read1 = icow.read();
440            assert_eq!(read1.item.s, 5);
441
442            handler0.join().unwrap();
443
444            let mut lock0 = icow.try_clone().unwrap();
445            lock0.s = 1;
446            lock0.commit_blocking().unwrap();
447        }
448
449        return;
450    }
451}