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