11import { clearTimeout , setTimeout } from 'timers' ;
22
3- import { MongoInvalidArgumentError , MongoOperationTimeoutError , MongoRuntimeError } from './error' ;
4- import { csotMin , noop } from './utils' ;
3+ import { MongoOperationTimeoutError , MongoRuntimeError } from './error' ;
4+ import { noop } from './utils' ;
55
66/** @internal */
77export class TimeoutError extends Error {
@@ -39,6 +39,7 @@ export class Timeout extends Promise<never> {
3939 public ended : number | null = null ;
4040 public duration : number ;
4141 public timedOut = false ;
42+ public cleared = false ;
4243
4344 get remainingTime ( ) : number {
4445 if ( this . timedOut ) return 0 ;
@@ -54,10 +55,6 @@ export class Timeout extends Promise<never> {
5455 private constructor ( executor : Executor = ( ) => null , duration : number , unref = true ) {
5556 let reject ! : Reject ;
5657
57- if ( duration < 0 ) {
58- throw new MongoInvalidArgumentError ( 'Cannot create a Timeout with a negative duration' ) ;
59- }
60-
6158 super ( ( _ , promiseReject ) => {
6259 reject = promiseReject ;
6360
@@ -77,6 +74,12 @@ export class Timeout extends Promise<never> {
7774 // Ensure we do not keep the Node.js event loop running
7875 this . id . unref ( ) ;
7976 }
77+ } else if ( duration < 0 ) {
78+ process . nextTick ( ( ) => {
79+ this . ended = Math . trunc ( performance . now ( ) ) ;
80+ this . timedOut = true ;
81+ reject ( new TimeoutError ( 'Timed out immediately due to negative duration' ) ) ;
82+ } ) ;
8083 }
8184 }
8285
@@ -86,12 +89,18 @@ export class Timeout extends Promise<never> {
8689 clear ( ) : void {
8790 clearTimeout ( this . id ) ;
8891 this . id = undefined ;
92+ this . cleared = true ;
8993 }
9094
9195 throwIfExpired ( ) : void {
9296 if ( this . timedOut ) throw new TimeoutError ( 'Timed out' ) ;
9397 }
9498
99+ /**
100+ * Creates a `Timeout` instance that expires after `durationMS`. Note that when `durationMS` is
101+ * 0, this creates a `Timeout` that never expires. Note also that a negative `durationMS` will
102+ * expire on the next tick of the event loop
103+ * */
95104 public static expires ( durationMS : number , unref ?: boolean ) : Timeout {
96105 return new Timeout ( undefined , durationMS , unref ) ;
97106 }
@@ -214,27 +223,28 @@ export class CSOTTimeoutContext extends TimeoutContext {
214223
215224 get serverSelectionTimeout ( ) : Timeout | null {
216225 // check for undefined
217- if ( typeof this . _serverSelectionTimeout !== 'object' ) {
226+ if ( typeof this . _serverSelectionTimeout !== 'object' || this . _serverSelectionTimeout ?. cleared ) {
227+ const { remainingTimeMS, serverSelectionTimeoutMS } = this ;
218228 const usingServerSelectionTimeoutMS =
219- this . serverSelectionTimeoutMS !== 0 &&
220- csotMin ( this . timeoutMS , this . serverSelectionTimeoutMS ) === this . serverSelectionTimeoutMS ;
229+ serverSelectionTimeoutMS !== 0 &&
230+ ! Number . isFinite ( remainingTimeMS ) &&
231+ remainingTimeMS > serverSelectionTimeoutMS ;
221232
222233 if ( usingServerSelectionTimeoutMS ) {
223- this . _serverSelectionTimeout = Timeout . expires ( this . serverSelectionTimeoutMS ) ;
234+ this . _serverSelectionTimeout = Timeout . expires ( serverSelectionTimeoutMS ) ;
224235 } else {
225- if ( this . timeoutMS > 0 ) {
226- this . _serverSelectionTimeout = Timeout . expires ( this . timeoutMS ) ;
227- } else {
228- this . _serverSelectionTimeout = null ;
229- }
236+ this . _serverSelectionTimeout = Timeout . expires ( remainingTimeMS ) ;
230237 }
231238 }
232239
233240 return this . _serverSelectionTimeout ;
234241 }
235242
236243 get connectionCheckoutTimeout ( ) : Timeout | null {
237- if ( typeof this . _connectionCheckoutTimeout !== 'object' ) {
244+ if (
245+ typeof this . _connectionCheckoutTimeout !== 'object' ||
246+ this . _connectionCheckoutTimeout ?. cleared
247+ ) {
238248 if ( typeof this . _serverSelectionTimeout === 'object' ) {
239249 // null or Timeout
240250 this . _connectionCheckoutTimeout = this . _serverSelectionTimeout ;
0 commit comments