1
1
// Licensed to the .NET Foundation under one or more agreements.
2
2
// The .NET Foundation licenses this file to you under the MIT license.
3
3
4
+ using System . Collections . Generic ;
4
5
using System . Threading ;
6
+ using System . Threading . Tasks ;
5
7
using Xunit ;
6
8
7
9
namespace System . IO . Tests
@@ -32,26 +34,32 @@ public class InternalBufferSizeTests : FileSystemWatcherTest
32
34
// it's internal buffer (up to some limit). Our docs say that limit is 64KB but testing on Win8.1
33
35
// indicates that it is much higher than this: I could grow the buffer up to 128 MB and still see
34
36
// that it had an effect. The size needed per operation is determined by the struct layout of
35
- // FILE_NOTIFY_INFORMATION. This works out to 16 + 2 * (Path.GetFileName(file.Path).Length + 1) bytes, where filePath
37
+ // FILE_NOTIFY_INFORMATION. This works out to 12 + 2 * (Path.GetFileName(file.Path).Length + 1) bytes, where filePath
36
38
// is the path to changed file relative to the path passed into ReadDirectoryChanges.
37
39
38
40
// At some point we might decide to improve how FSW handles this at which point we'll need
39
41
// a better test for Error (perhaps just a mock), but for now there is some value in forcing this limit.
42
+ private const int ExcessEventsMultiplier = 200 ; // 100 does not reliably trigger the error
43
+
40
44
[ Theory ]
41
45
[ InlineData ( true ) ]
42
46
[ InlineData ( false ) ]
43
47
[ PlatformSpecific ( TestPlatforms . Windows ) ] // Uses P/Invokes
48
+ [ OuterLoop ( "A little slow" ) ]
44
49
public void FileSystemWatcher_InternalBufferSize ( bool setToHigherCapacity )
45
50
{
46
51
ManualResetEvent unblockHandler = new ManualResetEvent ( false ) ;
47
52
string file = CreateTestFile ( TestDirectory , "file" ) ;
48
53
using ( FileSystemWatcher watcher = CreateWatcher ( TestDirectory , file , unblockHandler ) )
49
54
{
50
- int internalBufferOperationCapacity = CalculateInternalBufferOperationCapacity ( watcher . InternalBufferSize , file ) ;
55
+ watcher . InternalBufferSize = 4096 ; // Minimum
56
+ int internalBufferOperationCapacity = watcher . InternalBufferSize / ( 12 + 2 * ( Path . GetFileName ( file ) . Length + 1 ) ) ;
51
57
52
- // Set the capacity high to ensure no error events arise.
53
58
if ( setToHigherCapacity )
54
- watcher . InternalBufferSize = watcher . InternalBufferSize * 12 ;
59
+ {
60
+ // Set the capacity high to ensure no error events arise.
61
+ watcher . InternalBufferSize = watcher . InternalBufferSize * ExcessEventsMultiplier * 2 ;
62
+ }
55
63
56
64
Action action = GetAction ( unblockHandler , internalBufferOperationCapacity , file ) ;
57
65
Action cleanup = GetCleanup ( unblockHandler ) ;
@@ -65,6 +73,7 @@ public void FileSystemWatcher_InternalBufferSize(bool setToHigherCapacity)
65
73
66
74
[ Fact ]
67
75
[ PlatformSpecific ( TestPlatforms . Windows ) ]
76
+ [ OuterLoop ( "A little slow" ) ]
68
77
public void FileSystemWatcher_InternalBufferSize_SynchronizingObject ( )
69
78
{
70
79
ManualResetEvent unblockHandler = new ManualResetEvent ( false ) ;
@@ -74,7 +83,8 @@ public void FileSystemWatcher_InternalBufferSize_SynchronizingObject()
74
83
TestISynchronizeInvoke invoker = new TestISynchronizeInvoke ( ) ;
75
84
watcher . SynchronizingObject = invoker ;
76
85
77
- int internalBufferOperationCapacity = CalculateInternalBufferOperationCapacity ( watcher . InternalBufferSize , file ) ;
86
+ watcher . InternalBufferSize = 4096 ; // Minimum
87
+ int internalBufferOperationCapacity = watcher . InternalBufferSize / ( 12 + 2 * ( Path . GetFileName ( file ) . Length + 1 ) ) ;
78
88
79
89
Action action = GetAction ( unblockHandler , internalBufferOperationCapacity , file ) ;
80
90
Action cleanup = GetCleanup ( unblockHandler ) ;
@@ -96,23 +106,30 @@ private FileSystemWatcher CreateWatcher(string testDirectoryPath, string filePat
96
106
return watcher ;
97
107
}
98
108
99
- private int CalculateInternalBufferOperationCapacity ( int internalBufferSize , string filePath ) =>
100
- internalBufferSize / ( 17 + Path . GetFileName ( filePath ) . Length ) ;
101
-
102
109
private Action GetAction ( ManualResetEvent unblockHandler , int internalBufferOperationCapacity , string filePath )
103
110
{
104
111
return ( ) =>
105
112
{
106
- // generate enough file change events to overflow the default buffer
107
- for ( int i = 1 ; i < internalBufferOperationCapacity * 10 ; i ++ )
113
+ List < Task > tasks = new ( ) ;
114
+
115
+ // Generate enough file change events to overflow the default buffer
116
+ // For speed, do this on multiple threads
117
+ for ( int i = 1 ; i < internalBufferOperationCapacity * ExcessEventsMultiplier ; i ++ )
108
118
{
109
- File . SetLastWriteTime ( filePath , DateTime . Now + TimeSpan . FromSeconds ( i ) ) ;
119
+ tasks . Add ( Task . Run ( ( ) =>
120
+ {
121
+ // Each sets to a different time to ensure it triggers an event
122
+ File . SetLastWriteTime ( filePath , DateTime . Now + TimeSpan . FromSeconds ( i ) ) ;
123
+ } ) ) ;
110
124
}
111
125
126
+ Task . WaitAll ( tasks ) ;
127
+
112
128
unblockHandler . Set ( ) ;
113
129
} ;
114
130
}
115
131
132
+
116
133
private Action GetCleanup ( ManualResetEvent unblockHandler ) => ( ) => unblockHandler . Reset ( ) ;
117
134
118
135
#endregion
0 commit comments