13
13
* limitations under the License.
14
14
*/
15
15
16
+ using System ;
16
17
using System . Collections . Generic ;
18
+ using System . Threading ;
17
19
using FluentAssertions ;
18
20
using FluentAssertions . Common ;
19
21
using MongoDB . Bson ;
@@ -78,17 +80,69 @@ public void KillCursor_should_actually_work()
78
80
cursor = collection . FindSync ( "{ } ") ;
79
81
cursor . MoveNext ( ) ;
80
82
81
- var cursorId = ( ( AsyncCursor < BsonDocument > ) cursor) . _cursorId( ) ;
83
+ var cursorId = ( ( AsyncCursor < BsonDocument > ) cursor) . _cursorId( ) ;
82
84
cursorId . Should ( ) . NotBe ( 0 ) ;
83
85
cursor . Dispose ( ) ;
84
86
85
87
var desiredResult = BsonDocument . Parse ( $"{ { \" cursorsKilled \" : [ { cursorId} ] , \" cursorsNotFound \" : [ ] , " +
86
88
$"\" cursorsAlive \" : [ ] , \" cursorsUnknown \" : [ ] , \" ok \" : 1.0 } } ") ;
87
- var result = ( ( CommandSucceededEvent ) eventCapturer . Events [ 0 ] ) . Reply ;
89
+ var result = ( ( CommandSucceededEvent ) eventCapturer . Events [ 0 ] ) . Reply ;
88
90
result. IsSameOrEqualTo ( desiredResult ) ;
89
91
}
90
92
}
91
93
94
+ [ SkippableFact ]
95
+ public void Tailable_cursor_should_be_able_to_be_cancelled_from_a_different_thread_with_expected_result( )
96
+ {
97
+ RequireServer. Check ( ) . Supports ( Feature . TailableCursor ) ;
98
+
99
+ string testCollectionName = "test";
100
+ string testDatabaseName = "test";
101
+
102
+ var client = DriverTestConfiguration. Client ;
103
+ var database = client . GetDatabase ( testDatabaseName ) ;
104
+ var collection = database . GetCollection < BsonDocument > ( testCollectionName ) ;
105
+
106
+ DropCollection( client , testDatabaseName , testCollectionName ) ;
107
+ var createCollectionOptions = new CreateCollectionOptions ( )
108
+ {
109
+ Capped = true ,
110
+ MaxSize = 1000
111
+ } ;
112
+ database . CreateCollection ( testCollectionName , createCollectionOptions ) ;
113
+ collection . InsertOne ( new BsonDocument ( ) ) ; // tailable cursors don't work on an empty collection
114
+
115
+ using ( var cancellationTokenSource = new CancellationTokenSource ( ) )
116
+ {
117
+ var findOptions = new FindOptions < BsonDocument > ( )
118
+ {
119
+ BatchSize = 1 ,
120
+ CursorType = CursorType . TailableAwait
121
+ } ;
122
+ var cursor = collection. FindSync( FilterDefinition < BsonDocument > . Empty , findOptions ) ;
123
+ var enumerator = cursor . ToEnumerable ( cancellationTokenSource . Token ) . GetEnumerator ( ) ;
124
+
125
+ var semaphore = new SemaphoreSlim ( 0 ) ;
126
+ var thread = new Thread ( ( ) =>
127
+ {
128
+ semaphore . Wait ( ) ;
129
+ cancellationTokenSource . Cancel ( ) ;
130
+ } ) ;
131
+ thread . Start ( ) ;
132
+
133
+ var exception = Record. Exception( ( Action ) ( ( ) =>
134
+ {
135
+ while ( true )
136
+ {
137
+ _ = enumerator . MoveNext ( ) ;
138
+ semaphore . Release ( 1 ) ;
139
+ }
140
+ } ) ) ;
141
+
142
+ exception. Should ( ) . BeAssignableTo < OperationCanceledException > ( ) ;
143
+ }
144
+ }
145
+
92
146
//private methods
93
147
private IMongoClient CreateClient( )
94
148
{
@@ -105,6 +159,6 @@ private void DropCollection(IMongoClient client, string databaseName, string col
105
159
public static class AsyncCursorReflector
106
160
{
107
161
public static long _cursorId( this AsyncCursor< BsonDocument > obj ) =>
108
- ( long ) Reflector . GetFieldValue ( obj , nameof ( _cursorId ) ) ;
162
+ ( long ) Reflector . GetFieldValue ( obj , nameof ( _cursorId ) ) ;
109
163
}
110
164
}
0 commit comments