1+ using System ;
2+ using System . Collections ;
3+ using System . Collections . Generic ;
4+ using System . Collections . Specialized ;
5+ using System . ComponentModel ;
6+ using System . Linq ;
7+ using System . Threading ;
8+
9+ namespace Files . Helpers
10+ {
11+ public class BulkConcurrentObservableCollection < T > : INotifyCollectionChanged , INotifyPropertyChanged , ICollection < T > , IList < T > , ICollection , IList
12+ {
13+ private volatile bool isBulkOperationStarted ;
14+ private volatile int readerCount ;
15+ private volatile bool needSnapshot ;
16+ private readonly SemaphoreSlim writerLock = new SemaphoreSlim ( 1 , 1 ) ;
17+ private readonly SemaphoreSlim mutex = new SemaphoreSlim ( 1 , 1 ) ;
18+ private readonly SemaphoreSlim snapshotLock = new SemaphoreSlim ( 1 , 1 ) ;
19+ private readonly List < T > collection = new List < T > ( ) ;
20+ private List < T > snapshot = new List < T > ( ) ;
21+
22+ private void Write ( Action writeFunc )
23+ {
24+ writerLock . Wait ( ) ;
25+ try
26+ {
27+ writeFunc ( ) ;
28+ }
29+ finally
30+ {
31+ writerLock . Release ( ) ;
32+ snapshotLock . Wait ( ) ;
33+ needSnapshot = true ;
34+ snapshotLock . Release ( ) ;
35+ }
36+ }
37+
38+ private U Write < U > ( Func < U > writeFunc )
39+ {
40+ writerLock . Wait ( ) ;
41+ try
42+ {
43+ return writeFunc ( ) ;
44+ }
45+ finally
46+ {
47+ writerLock . Release ( ) ;
48+ snapshotLock . Wait ( ) ;
49+ needSnapshot = true ;
50+ snapshotLock . Release ( ) ;
51+ }
52+ }
53+
54+ private void Read ( Action readFunc )
55+ {
56+ mutex . Wait ( ) ;
57+ readerCount ++ ;
58+ if ( readerCount == 1 )
59+ {
60+ writerLock . Wait ( ) ;
61+ }
62+ mutex . Release ( ) ;
63+ try
64+ {
65+ readFunc ( ) ;
66+ }
67+ finally
68+ {
69+ mutex . Wait ( ) ;
70+ readerCount -- ;
71+ if ( readerCount == 0 )
72+ {
73+ writerLock . Release ( ) ;
74+ }
75+ mutex . Release ( ) ;
76+ }
77+ }
78+
79+ private U Read < U > ( Func < U > readFunc )
80+ {
81+ mutex . Wait ( ) ;
82+ readerCount ++ ;
83+ if ( readerCount == 1 )
84+ {
85+ writerLock . Wait ( ) ;
86+ }
87+ mutex . Release ( ) ;
88+ try
89+ {
90+ return readFunc ( ) ;
91+ }
92+ finally
93+ {
94+ mutex . Wait ( ) ;
95+ readerCount -- ;
96+ if ( readerCount == 0 )
97+ {
98+ writerLock . Release ( ) ;
99+ }
100+ mutex . Release ( ) ;
101+ }
102+ }
103+
104+ public int Count => Read ( ( ) => collection . Count ) ;
105+
106+ public bool IsReadOnly => false ;
107+
108+ public bool IsFixedSize => false ;
109+
110+ public bool IsSynchronized => false ;
111+
112+ public object SyncRoot => throw new NotImplementedException ( ) ;
113+
114+ object IList . this [ int index ]
115+ {
116+ get
117+ {
118+ return this [ index ] ;
119+ }
120+ set
121+ {
122+ this [ index ] = ( T ) value ;
123+ }
124+ }
125+
126+ public T this [ int index ]
127+ {
128+ get => Read ( ( ) => collection [ index ] ) ;
129+ set
130+ {
131+ var item = Write ( ( ) =>
132+ {
133+ var item = collection [ index ] ;
134+ collection [ index ] = value ;
135+ return item ;
136+ } ) ;
137+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Replace , value , item ) ) ;
138+ }
139+ }
140+
141+ public event NotifyCollectionChangedEventHandler CollectionChanged ;
142+ public event PropertyChangedEventHandler PropertyChanged ;
143+
144+ public void BeginBulkOperation ( )
145+ {
146+ isBulkOperationStarted = true ;
147+ }
148+
149+ protected void OnCollectionChanged ( NotifyCollectionChangedEventArgs e )
150+ {
151+ if ( ! isBulkOperationStarted )
152+ {
153+ CollectionChanged ? . Invoke ( this , e ) ;
154+ PropertyChanged ? . Invoke ( this , new PropertyChangedEventArgs ( nameof ( Count ) ) ) ;
155+ }
156+ }
157+
158+ public void EndBulkOperation ( )
159+ {
160+ isBulkOperationStarted = false ;
161+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Reset ) ) ;
162+ }
163+
164+ public void Add ( T item )
165+ {
166+ Write ( ( ) => collection . Add ( item ) ) ;
167+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Add , item ) ) ;
168+ }
169+
170+ public void Clear ( )
171+ {
172+ Write ( ( ) => collection . Clear ( ) ) ;
173+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Reset ) ) ;
174+ }
175+
176+ public bool Contains ( T item )
177+ {
178+ return Read ( ( ) => collection . Contains ( item ) ) ;
179+ }
180+
181+ public void CopyTo ( T [ ] array , int arrayIndex )
182+ {
183+ Read ( ( ) => collection . CopyTo ( array , arrayIndex ) ) ;
184+ }
185+
186+ public bool Remove ( T item )
187+ {
188+ var result = Write ( ( ) => collection . Remove ( item ) ) ;
189+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Remove , item ) ) ;
190+ return result ;
191+ }
192+
193+ public IEnumerator < T > GetEnumerator ( )
194+ {
195+ snapshotLock . Wait ( ) ;
196+ if ( needSnapshot )
197+ {
198+ snapshot = Read ( ( ) => collection . ToList ( ) ) ;
199+ needSnapshot = false ;
200+ }
201+ snapshotLock . Release ( ) ;
202+ return snapshot . GetEnumerator ( ) ;
203+ }
204+
205+ IEnumerator IEnumerable . GetEnumerator ( )
206+ {
207+ return GetEnumerator ( ) ;
208+ }
209+
210+ public int IndexOf ( T item )
211+ {
212+ return Read ( ( ) => collection . IndexOf ( item ) ) ;
213+ }
214+
215+ public void Insert ( int index , T item )
216+ {
217+ Write ( ( ) => collection . Insert ( index , item ) ) ;
218+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Add , item ) ) ;
219+ }
220+
221+ public void RemoveAt ( int index )
222+ {
223+ var item = Write ( ( ) =>
224+ {
225+ var item = collection [ index ] ;
226+ collection . RemoveAt ( index ) ;
227+ return item ;
228+ } ) ;
229+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Remove , item ) ) ;
230+ }
231+
232+ public void AddRange ( IEnumerable < T > items )
233+ {
234+ if ( items . Count ( ) == 0 )
235+ {
236+ return ;
237+ }
238+ Write ( ( ) => collection . AddRange ( items ) ) ;
239+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Add , items . ToList ( ) ) ) ;
240+ }
241+
242+ public void InsertRange ( int index , IEnumerable < T > items )
243+ {
244+ if ( items . Count ( ) == 0 )
245+ {
246+ return ;
247+ }
248+ Write ( ( ) => collection . InsertRange ( index , items ) ) ;
249+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Add , items . ToList ( ) , index ) ) ;
250+ }
251+
252+ public void RemoveRange ( int index , int count )
253+ {
254+ if ( count == 0 )
255+ {
256+ return ;
257+ }
258+ var items = Write ( ( ) =>
259+ {
260+ var items = collection . Skip ( index ) . Take ( count ) . ToList ( ) ;
261+ collection . RemoveRange ( index , count ) ;
262+ return items ;
263+ } ) ;
264+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Remove , items ) ) ;
265+ }
266+
267+ public void ReplaceRange ( int index , IEnumerable < T > items )
268+ {
269+ var count = items . Count ( ) ;
270+ if ( count == 0 )
271+ {
272+ return ;
273+ }
274+ var ( newItems , oldItems ) = Write ( ( ) =>
275+ {
276+ var oldItems = collection . Skip ( index ) . Take ( count ) . ToList ( ) ;
277+ var newItems = items . ToList ( ) ;
278+ collection . InsertRange ( index , newItems ) ;
279+ collection . RemoveRange ( index + count , count ) ;
280+ return ( newItems , oldItems ) ;
281+ } ) ;
282+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Replace , newItems , oldItems ) ) ;
283+ }
284+
285+ int IList . Add ( object value )
286+ {
287+ var index = Write ( ( ) =>
288+ {
289+ collection . Add ( ( T ) value ) ;
290+ return collection . Count ;
291+ } ) ;
292+ OnCollectionChanged ( new NotifyCollectionChangedEventArgs ( NotifyCollectionChangedAction . Add , value ) ) ;
293+ return index ;
294+ }
295+
296+ bool IList . Contains ( object value ) => Contains ( ( T ) value ) ;
297+ int IList . IndexOf ( object value ) => IndexOf ( ( T ) value ) ;
298+ void IList . Insert ( int index , object value ) => Insert ( index , ( T ) value ) ;
299+ void IList . Remove ( object value ) => Remove ( ( T ) value ) ;
300+ void ICollection . CopyTo ( Array array , int index ) => CopyTo ( ( T [ ] ) array , index ) ;
301+ }
302+ }
0 commit comments