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@4neko.org
5 * 
6 * The instance-copy-on-write crate can be redistributed and/or modified
7 * under the terms of either of the following licenses:
8 *
9 *   1. The MIT License (MIT)
10 */
11
12/*!
13 * An experimental thread access synchronization primitive 
14 * which is based on CoW Copy-on-Write and also exclusive lock.
15 * 
16 * By default an RwLock based implementation is used, because the 
17 * atomics based implementation is still not ready. It can be activated 
18 * by enabling the feature prefer_atomic.
19 * 
20 * The `ICoWRead<DATA>` instance with inner value `DATA` obtained from the 
21 * `ICoW<DATA>` is valid even after the inner value was copied and copy was 
22 * commited back. All `readers` which will will read the `ICoW` instance after 
23 * the `commit` will receive a `ICoWRead<DATA>` with updated `DATA`.
24 *
25 * Exclusive copying prevents `readers` from reading while simple `non-exclusive` 
26 * copy allows to access the inner data for reading and further copying. But, the 
27 * `non-exclusive` copies are not in sync, so each copy is uniq. The `sequential` 
28 * commit of copied data is not yet supported.
29 *
30 *  ```ignore
31
32    let val = 
33        ICoW::new(TestStruct::new(1, 2));
34
35    // read only 
36    let read1 = val.read();
37    //..
38    drop(read1);
39
40    // ...
41
42    // copy on write NON-exclusively, read is possible
43    let mut transaction = val.clone_copy();
44
45    transaction.start = 5;
46    transaction.stop = 6;
47
48    // update, after calling this function, all reades who 
49    // read before will still see old version.
50    // all reades after, new
51    transaction.commit();
52    //or
53    drop(transaction); // to drop changes
54
55
56    // exclusive lock, read is also not possible
57
58    let res = val.clone_exclusivly();
59    // ..
60    // commit changes releasing lock
61    commit(val); //or
62    drop(val); // to drop changes and release lock
63
64    ```
65 */
66
67 /// Type of the sync code.
68 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
69 pub enum ICoWLockTypes
70 {
71    /// Based on atomic and backoff.
72    Atomic,
73
74    /// based on the OS RwLock
75    RwLock,
76 }
77
78impl fmt::Display for ICoWLockTypes
79{
80    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
81    {
82        match self
83        {
84            Self::Atomic => 
85                write!(f, "atomic"),
86            Self::RwLock => 
87                write!(f, "rwlock"),
88        }
89    }
90}
91
92/// Errors which may be returned.
93#[derive(Copy, Clone, Debug, PartialEq, Eq)]
94pub enum ICoWError
95{
96    /// An attempt to write to the instance using non exclusive copy-on-write operation
97    /// while the exclusive is still going.
98    ExclusiveLockPending,
99
100    /// Is issued if "exponential backoff has completed and blocking the thread is advised".
101    WouldBlock,
102
103    /// Duplicate write operation prevented.
104    AlreadyUpdated,
105}
106
107impl fmt::Display for ICoWError
108{
109    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result 
110    {
111        match self
112        {
113            Self::ExclusiveLockPending => 
114                write!(f, "already exlcusivly locked"),
115            Self::WouldBlock => 
116                write!(f, "cannot perform try operation due to blocking"),
117            Self::AlreadyUpdated => 
118                write!(f, "the value has been updated before the current thread"),
119        }
120    }
121}
122
123extern crate crossbeam_utils;
124extern crate crossbeam_deque;
125
126/// A lightweight CoW implementation.
127#[cfg(all(target_has_atomic = "ptr", feature = "prefer_atomic"))]
128pub mod cow;
129
130/// A RwLock based copy-on-write.
131#[cfg(any(not(target_has_atomic = "ptr"), not(feature = "prefer_atomic")))]
132pub mod cow_mutex;
133
134use std::fmt;
135
136#[cfg(all(target_has_atomic = "ptr", feature = "prefer_atomic"))]
137pub use cow::{ICoW, ICoWRead, ICoWCopy, ICoWLock};
138
139#[cfg(any(not(target_has_atomic = "ptr"), not(feature = "prefer_atomic")))]
140pub use cow_mutex::{ICoW, ICoWRead, ICoWCopy, ICoWLock};
141
142pub trait ICowType
143{
144    fn get_lock_type() -> ICoWLockTypes;
145}
146
147#[cfg(test)]
148mod test
149{
150    use std::{sync::{mpsc, Arc, LazyLock}, time::{Duration, Instant}};
151
152    use super::*;
153
154
155
156
157    #[test]
158    fn test_3()
159    {
160        #[derive(Debug, Clone)]
161        struct Test { s: String }
162
163        let icow = ICoW::new(Test{ s: "test".into() });
164
165        for _ in 0..10
166        {
167            let s = Instant::now();
168
169            let read0 = icow.read();
170
171            let e = s.elapsed();
172
173            println!("{:?}", e);
174        }
175
176    }
177
178    #[test]
179    fn test_33()
180    {
181        #[derive(Debug, Clone)]
182        struct Test { s: String };
183
184        let icow = ICoW::new(Test{ s: "test".into() });
185
186        let write_ex = icow.try_clone_copy_exclusivly().unwrap();
187
188        {
189        let write_ex_err = icow.try_clone_copy_exclusivly();
190        assert_eq!(write_ex_err.is_none(), true);
191        }
192
193        let write_ex_err = icow.try_clone_copy();
194        assert_eq!(write_ex_err.is_none(), true);
195
196        let read1 = icow.try_read();
197        assert_eq!(read1.is_none(), true);
198
199        drop(write_ex);
200
201        drop(icow);
202
203    }
204
205    #[test]
206    fn test_4()
207    {
208        #[derive(Debug, Clone)]
209        struct Test { s: u32 }
210
211        let icow = ICoW::new(Test{ s: 1 });
212
213        let read0 = icow.read();
214        let read1 = icow.read();
215
216        let mut excl_write = icow.try_clone_copy_exclusivly().unwrap();
217
218        excl_write.s = 5;
219
220
221        excl_write.commit();
222
223        assert_eq!(read0.s, 1);
224
225        let read3 = icow.read();
226        assert_eq!(read3.s, 5);
227
228        let mut writing = icow.try_clone_copy().unwrap();
229        writing.s = 4;
230
231        writing.commit().unwrap();
232
233        assert_eq!(read0.s, 1);
234        assert_eq!(read3.s, 5);
235
236        let read4 = icow.read();
237        assert_eq!(read4.s, 4);
238    }
239
240    #[test]
241    fn test_5()
242    {
243        #[derive(Debug, Clone)]
244        struct Test { s: u32 }
245
246        let icow = Arc::new(ICoW::new(Test{ s: 1 }));
247
248        let read0 = icow.read();
249        let read2 = icow.read();
250        
251        let c_icow = icow.clone();
252
253        let (se, rc) = mpsc::channel::<()>();
254        let handler0 = 
255            std::thread::spawn(move || 
256                {
257                    let mut lock0 = c_icow.try_clone_copy_exclusivly().unwrap();
258
259                    se.send(()).unwrap();
260
261                    lock0.s = 5;
262
263                    std::thread::sleep(Duration::from_micros(2));
264
265                    lock0.commit();
266                }
267            );
268
269        rc.recv().unwrap();
270
271        let s = Instant::now();
272
273        let read1 = icow.read();
274        
275        let e = s.elapsed();
276
277        println!("{:?}", e);
278
279        
280        assert_eq!(read1.s, 5);
281        assert_eq!(read0.s, 1);
282
283        handler0.join().unwrap();
284
285        let weak0 = read0.weak();
286        let weak1 = read1.weak();
287
288        drop(read0);
289        drop(read1);
290
291        assert_eq!(weak0.upgrade().is_some(), true);
292        assert_eq!(weak1.upgrade().is_some(), true);
293
294        drop(read2);
295        assert_eq!(weak0.upgrade().is_none(), true);
296        assert_eq!(weak1.upgrade().is_some(), true);
297    }
298
299
300    #[test]
301    fn test_6()
302    {
303        #[derive(Debug, Clone)]
304        struct Test { s: u32 }
305
306        let icow = Arc::new(ICoW::new(Test{ s: 1 }));
307
308        let read0 = icow.read();
309        let read2 = icow.read();
310        
311        let c_icow = icow.clone();
312
313        let (se, rc) = mpsc::channel::<()>();
314        let handler0 = 
315            std::thread::spawn(move || 
316                {
317                    let read2 = c_icow.read();
318
319                    let mut lock0 = c_icow.try_clone_copy_exclusivly().unwrap();
320
321                    se.send(()).unwrap();
322
323                    lock0.s = 5;
324
325                    std::thread::sleep(Duration::from_nanos(50));
326                    lock0.commit();
327
328                    let read3 = c_icow.read();
329
330                    assert_eq!(read2.s, 1);
331                    assert_eq!(read3.s, 5);
332                }
333            );
334
335        rc.recv().unwrap();
336
337        for _ in 0..100000000
338        {
339            let read1 = icow.read();
340
341            if read1.s == 1
342            {
343                continue;
344            }
345            else
346            {
347                break;
348            }
349        }
350
351        let read1 = icow.read();
352        assert_eq!(read1.item.s, 5);
353
354        handler0.join().unwrap();
355
356        return;
357    }
358
359
360    #[test]
361    fn test_7()
362    {
363        #[derive(Debug, Clone)]
364        struct TestStruct { s: u32 }
365
366        impl TestStruct
367        {
368            fn new(s: u32) -> Self
369            {
370                return Self{ s: s };
371            }
372        }
373
374        static VAL: LazyLock<ICoW<TestStruct>> = 
375            LazyLock::new(|| ICoW::new(TestStruct::new(1))); 
376        
377
378        
379        
380            let borrow1 = VAL.read();
381            assert_eq!(borrow1.item.s, 1);
382
383            let (mpsc_send, mpsc_rcv) = mpsc::channel::<u64>();
384            let (mpsc_send2, mpsc_rcv2) = mpsc::channel::<u64>();
385
386            let thread1 = 
387                std::thread::spawn(move ||
388                    {
389                        for _ in 0..1000
390                        {
391                            let _ = mpsc_rcv2.recv();
392                            let borrow1 = VAL.read();
393
394                            let mut transaction = VAL.try_clone_copy_exclusivly().unwrap();
395
396                            transaction.s = 5;
397
398                            
399
400                            std::thread::sleep(Duration::from_nanos(1001));
401                            transaction.commit();
402                            let borrow2 = VAL.read();
403                            
404
405                            assert_eq!(borrow1.item.s, 1);
406
407                            assert_eq!(borrow2.item.s, 5);
408
409                            let _ = mpsc_send.send(1);
410                        }
411                    }
412                );
413            
414            
415
416            for x in 0..1000
417            {
418                println!("{}", x);
419                mpsc_send2.send(1).unwrap();
420                let _ = mpsc_rcv.recv();
421
422                let borrow1 = VAL.read();
423                assert_eq!(borrow1.s, 5);
424
425                let mut transaction = VAL.try_clone_copy_exclusivly().unwrap();
426                transaction.s = 1;
427                transaction.commit();
428
429                let borrow1 = VAL.read();
430                assert_eq!(borrow1.s, 1);
431                
432            }
433            
434            
435
436            thread1.join().unwrap();
437
438            
439    }
440
441
442    #[test]
443    fn test_8()
444    {
445        #[derive(Debug, Clone)]
446        struct Test { s: u32 }
447
448        let icow = Arc::new(ICoW::new(Test{ s: 1 }));
449
450        for _ in 0..20
451        {
452            let read0 = icow.read();
453            let read2 = icow.read();
454            
455            let c_icow = icow.clone();
456
457            //let (se, rc) = mpsc::channel::<()>();
458            let handler0 = 
459                std::thread::spawn(move || 
460                    {
461                        let read2 = c_icow.read();
462
463                        let mut lock0 = c_icow.as_ref().clone_copy();
464
465                        //se.send(()).unwrap();
466
467                        lock0.s = 5;
468
469                        std::thread::sleep(Duration::from_micros(1));
470                        lock0.commit_blocking().unwrap();
471
472                        let read3 = c_icow.read();
473
474                        assert_eq!(read2.s, 1);
475                        assert_eq!(read3.s, 5);
476                    }
477                );
478
479            //rc.recv().unwrap();
480
481            for i in 0..1000000000
482            {
483                let read1 = icow.read();
484
485                if read1.s == 1
486                {
487                    continue;
488                }
489                else
490                {
491                    println!("{}", i);
492                    break;
493                }
494            }
495
496            let read1 = icow.read();
497            assert_eq!(read1.s, 5);
498
499            handler0.join().unwrap();
500
501            let mut lock0 = icow.try_clone_copy().unwrap();
502            lock0.s = 1;
503            lock0.commit_blocking().unwrap();
504        }
505
506        return;
507    }
508}