@@ -42,20 +42,54 @@ macro_rules! async_file_ext {
42
42
/// locked exclusively.
43
43
fn lock_shared( & self ) ->Result <( ) >;
44
44
45
+ /// Locks the file for exclusive usage, blocking if the file is currently
46
+ /// locked exclusively.
47
+ ///
48
+ /// **Note:** This method is not really "async", the underlying system call is still blocking.
49
+ /// Having this method as async is just for convenience when using it in async runtime.
50
+ fn lock_shared_async( & self ) ->impl core:: future:: Future <Output =Result <( ) >>;
51
+
45
52
/// Locks the file for exclusive usage, blocking if the file is currently
46
53
/// locked.
47
54
fn lock_exclusive( & self ) ->Result <( ) >;
48
55
56
+ /// Locks the file for exclusive usage, blocking if the file is currently
57
+ /// locked.
58
+ ///
59
+ /// **Note:** This method is not really "async", the underlying system call is still blocking.
60
+ /// Having this method as async is just for convenience when using it in async runtime.
61
+ fn lock_exclusive_async( & self ) ->impl core:: future:: Future <Output =Result <( ) >>;
62
+
49
63
/// Locks the file for shared usage, or returns an error if the file is
50
64
/// currently locked (see `lock_contended_error`).
51
65
fn try_lock_shared( & self ) ->Result <( ) >;
52
66
67
+ /// Locks the file for shared usage, or returns an error if the file is
68
+ /// currently locked (see `lock_contended_error`).
69
+ ///
70
+ /// **Note:** This method is not really "async", the underlying system call is still blocking.
71
+ /// Having this method as async is just for convenience when using it in async runtime.
72
+ fn try_lock_shared_async( & self ) ->impl core:: future:: Future <Output =Result <( ) >>;
73
+
53
74
/// Locks the file for exclusive usage, or returns an error if the file is
54
75
/// currently locked (see `lock_contended_error`).
55
76
fn try_lock_exclusive( & self ) ->Result <( ) >;
56
77
78
+ /// Locks the file for exclusive usage, or returns an error if the file is
79
+ /// currently locked (see `lock_contended_error`).
80
+ ///
81
+ /// **Note:** This method is not really "async", the underlying system call is still blocking.
82
+ /// Having this method as async is just for convenience when using it in async runtime.
83
+ fn try_lock_exclusive_async( & self ) ->impl core:: future:: Future <Output =Result <( ) >>;
84
+
57
85
/// Unlocks the file.
58
86
fn unlock( & self ) ->Result <( ) >;
87
+
88
+ /// Unlocks the file.
89
+ ///
90
+ /// **Note:** This method is not really "async", the underlying system call is still blocking.
91
+ /// Having this method as async is just for convenience when using it in async runtime.
92
+ fn unlock_async( & self ) ->impl core:: future:: Future <Output =Result <( ) >>;
59
93
}
60
94
61
95
impl AsyncFileExt for $file{
@@ -68,18 +102,42 @@ macro_rules! async_file_ext {
68
102
fn lock_shared( & self ) ->Result <( ) >{
69
103
sys:: lock_shared( self )
70
104
}
105
+
106
+ async fn lock_shared_async( & self ) ->Result <( ) >{
107
+ sys:: lock_shared( self )
108
+ }
109
+
71
110
fn lock_exclusive( & self ) ->Result <( ) >{
72
111
sys:: lock_exclusive( self )
73
112
}
113
+
114
+ async fn lock_exclusive_async( & self ) ->Result <( ) >{
115
+ sys:: lock_exclusive( self )
116
+ }
117
+
74
118
fn try_lock_shared( & self ) ->Result <( ) >{
75
119
sys:: try_lock_shared( self )
76
120
}
121
+
122
+ async fn try_lock_shared_async( & self ) ->Result <( ) >{
123
+ sys:: try_lock_shared( self )
124
+ }
125
+
77
126
fn try_lock_exclusive( & self ) ->Result <( ) >{
78
127
sys:: try_lock_exclusive( self )
79
128
}
129
+
130
+ async fn try_lock_exclusive_async( & self ) ->Result <( ) >{
131
+ sys:: try_lock_exclusive( self )
132
+ }
133
+
80
134
fn unlock( & self ) ->Result <( ) >{
81
135
sys:: unlock( self )
82
136
}
137
+
138
+ async fn unlock_async( & self ) ->Result <( ) >{
139
+ sys:: unlock( self )
140
+ }
83
141
}
84
142
}
85
143
}
@@ -144,6 +202,51 @@ macro_rules! test_mod {
144
202
file3. lock_exclusive( ) . unwrap( ) ;
145
203
}
146
204
205
+ /// Tests shared file lock operations.
206
+ #[ $annotation]
207
+ async fn lock_shared_async( ) {
208
+ let tempdir = tempdir:: TempDir :: new( "fs4" ) . unwrap( ) ;
209
+ let path = tempdir. path( ) . join( "fs4" ) ;
210
+ let file1 = fs:: OpenOptions :: new( )
211
+ . read( true )
212
+ . write( true )
213
+ . create( true )
214
+ . open( & path)
215
+ . await
216
+ . unwrap( ) ;
217
+ let file2 = fs:: OpenOptions :: new( )
218
+ . read( true )
219
+ . write( true )
220
+ . create( true )
221
+ . open( & path)
222
+ . await
223
+ . unwrap( ) ;
224
+ let file3 = fs:: OpenOptions :: new( )
225
+ . read( true )
226
+ . write( true )
227
+ . create( true )
228
+ . open( & path)
229
+ . await
230
+ . unwrap( ) ;
231
+
232
+ // Concurrent shared access is OK, but not shared and exclusive.
233
+ file1. lock_shared_async( ) . await . unwrap( ) ;
234
+ file2. lock_shared_async( ) . await . unwrap( ) ;
235
+ assert_eq!(
236
+ file3. try_lock_exclusive_async( ) . await . unwrap_err( ) . kind( ) ,
237
+ lock_contended_error( ) . kind( )
238
+ ) ;
239
+ file1. unlock_async( ) . await . unwrap( ) ;
240
+ assert_eq!(
241
+ file3. try_lock_exclusive_async( ) . await . unwrap_err( ) . kind( ) ,
242
+ lock_contended_error( ) . kind( )
243
+ ) ;
244
+
245
+ // Once all shared file locks are dropped, an exclusive lock may be created;
246
+ file2. unlock_async( ) . await . unwrap( ) ;
247
+ file3. lock_exclusive_async( ) . await . unwrap( ) ;
248
+ }
249
+
147
250
/// Tests exclusive file lock operations.
148
251
#[ $annotation]
149
252
async fn lock_exclusive( ) {
@@ -180,6 +283,42 @@ macro_rules! test_mod {
180
283
file2. lock_exclusive( ) . unwrap( ) ;
181
284
}
182
285
286
+ /// Tests exclusive file lock operations.
287
+ #[ $annotation]
288
+ async fn lock_exclusive_async( ) {
289
+ let tempdir = tempdir:: TempDir :: new( "fs4" ) . unwrap( ) ;
290
+ let path = tempdir. path( ) . join( "fs4" ) ;
291
+ let file1 = fs:: OpenOptions :: new( )
292
+ . read( true )
293
+ . write( true )
294
+ . create( true )
295
+ . open( & path)
296
+ . await
297
+ . unwrap( ) ;
298
+ let file2 = fs:: OpenOptions :: new( )
299
+ . read( true )
300
+ . write( true )
301
+ . create( true )
302
+ . open( & path)
303
+ . await
304
+ . unwrap( ) ;
305
+
306
+ // No other access is possible once an exclusive lock is created.
307
+ file1. lock_exclusive_async( ) . await . unwrap( ) ;
308
+ assert_eq!(
309
+ file2. try_lock_exclusive_async( ) . await . unwrap_err( ) . kind( ) ,
310
+ lock_contended_error( ) . kind( )
311
+ ) ;
312
+ assert_eq!(
313
+ file2. try_lock_shared_async( ) . await . unwrap_err( ) . kind( ) ,
314
+ lock_contended_error( ) . kind( )
315
+ ) ;
316
+
317
+ // Once the exclusive lock is dropped, the second file is able to create a lock.
318
+ file1. unlock_async( ) . await . unwrap( ) ;
319
+ file2. lock_exclusive_async( ) . await . unwrap( ) ;
320
+ }
321
+
183
322
/// Tests that a lock is released after the file that owns it is dropped.
184
323
#[ $annotation]
185
324
async fn lock_cleanup( ) {
@@ -211,6 +350,37 @@ macro_rules! test_mod {
211
350
file2. lock_shared( ) . unwrap( ) ;
212
351
}
213
352
353
+ /// Tests that a lock is released after the file that owns it is dropped.
354
+ #[ $annotation]
355
+ async fn lock_cleanup_async( ) {
356
+ let tempdir = tempdir:: TempDir :: new( "fs4" ) . unwrap( ) ;
357
+ let path = tempdir. path( ) . join( "fs4" ) ;
358
+ let file1 = fs:: OpenOptions :: new( )
359
+ . read( true )
360
+ . write( true )
361
+ . create( true )
362
+ . open( & path)
363
+ . await
364
+ . unwrap( ) ;
365
+ let file2 = fs:: OpenOptions :: new( )
366
+ . read( true )
367
+ . write( true )
368
+ . create( true )
369
+ . open( & path)
370
+ . await
371
+ . unwrap( ) ;
372
+
373
+ file1. lock_exclusive_async( ) . await . unwrap( ) ;
374
+ assert_eq!(
375
+ file2. try_lock_shared_async( ) . await . unwrap_err( ) . kind( ) ,
376
+ lock_contended_error( ) . kind( )
377
+ ) ;
378
+
379
+ // Drop file1; the lock should be released.
380
+ drop( file1) ;
381
+ file2. lock_shared_async( ) . await . unwrap( ) ;
382
+ }
383
+
214
384
/// Tests file allocation.
215
385
#[ $annotation]
216
386
async fn allocate( ) {