33const times = require ( 'async/times' )
44const crypto = require ( 'libp2p-crypto' )
55const waterfall = require ( 'async/waterfall' )
6- const timeout = require ( 'async/timeout' )
76const multihashing = require ( 'multihashing-async' )
87const PeerId = require ( 'peer-id' )
98const assert = require ( 'assert' )
109const c = require ( './constants' )
1110const { logger } = require ( './utils' )
11+ const AbortController = require ( 'abort-controller' )
1212
1313const errcode = require ( 'err-code' )
1414
@@ -25,9 +25,8 @@ class RandomWalk {
2525 * @param {DHT } options.dht
2626 */
2727 constructor ( dht , options ) {
28- this . _options = { ...c . defaultRandomWalk , ...options }
2928 assert ( dht , 'Random Walk needs an instance of the Kademlia DHT' )
30- this . _runningHandle = null
29+ this . _options = { ... c . defaultRandomWalk , ... options }
3130 this . _kadDHT = dht
3231 this . log = logger ( dht . peerInfo . id , 'random-walk' )
3332 }
@@ -41,64 +40,44 @@ class RandomWalk {
4140 */
4241 start ( ) {
4342 // Don't run twice
44- if ( this . _running || ! this . _options . enabled ) { return }
45-
46- // Create running handle
47- const runningHandle = {
48- _onCancel : null ,
49- _timeoutId : null ,
50- runPeriodically : ( walk , period ) => {
51- runningHandle . _timeoutId = setTimeout ( ( ) => {
52- runningHandle . _timeoutId = null
53-
54- walk ( ( nextPeriod ) => {
55- // Was walk cancelled while fn was being called?
56- if ( runningHandle . _onCancel ) {
57- return runningHandle . _onCancel ( )
58- }
59- // Schedule next
60- runningHandle . runPeriodically ( walk , nextPeriod )
61- } )
62- } , period )
63- } ,
64- cancel : ( cb ) => {
65- // Not currently running, can callback immediately
66- if ( runningHandle . _timeoutId ) {
67- clearTimeout ( runningHandle . _timeoutId )
68- return cb ( )
69- }
70- // Wait to finish and then call callback
71- runningHandle . _onCancel = cb
72- }
73- }
43+ if ( this . _timeoutId || ! this . _options . enabled ) { return }
7444
7545 // Start doing random walks after `this._options.delay`
76- runningHandle . _timeoutId = setTimeout ( ( ) => {
46+ this . _timeoutId = setTimeout ( ( ) => {
7747 // Start runner immediately
78- runningHandle . runPeriodically ( ( done ) => {
48+ this . _runPeriodically ( ( done ) => {
7949 // Each subsequent walk should run on a `this._options.interval` interval
8050 this . _walk ( this . _options . queriesPerPeriod , this . _options . timeout , ( ) => done ( this . _options . interval ) )
8151 } , 0 )
8252 } , this . _options . delay )
83-
84- this . _runningHandle = runningHandle
8553 }
8654
8755 /**
88- * Stop the random-walk process.
89- * @param { function(Error) } callback
56+ * Stop the random-walk process. Any active
57+ * queries will be aborted.
9058 *
9159 * @returns {void }
9260 */
93- stop ( callback ) {
94- const runningHandle = this . _runningHandle
95-
96- if ( ! runningHandle ) {
97- return callback ( )
98- }
61+ stop ( ) {
62+ clearTimeout ( this . _timeoutId )
63+ this . _timeoutId = null
64+ this . _controller && this . _controller . abort ( )
65+ }
9966
100- this . _runningHandle = null
101- runningHandle . cancel ( callback )
67+ /**
68+ * Run function `walk` on every `interval` ms
69+ * @param {function(callback) } walk The function to execute on `interval`
70+ * @param {number } interval The interval to run on in ms
71+ *
72+ * @private
73+ */
74+ _runPeriodically ( walk , interval ) {
75+ this . _timeoutId = setTimeout ( ( ) => {
76+ walk ( ( nextInterval ) => {
77+ // Schedule next
78+ this . _runPeriodically ( walk , nextInterval )
79+ } )
80+ } , interval )
10281 }
10382
10483 /**
@@ -113,39 +92,63 @@ class RandomWalk {
11392 */
11493 _walk ( queries , walkTimeout , callback ) {
11594 this . log ( 'start' )
95+ this . _controller = new AbortController ( )
11696
117- times ( queries , ( i , cb ) => {
97+ times ( queries , ( i , next ) => {
98+ this . log ( 'running query %d' , i )
99+
100+ // Perform the walk
118101 waterfall ( [
119102 ( cb ) => this . _randomPeerId ( cb ) ,
120- ( id , cb ) => timeout ( ( cb ) => {
121- this . _query ( id , cb )
122- } , walkTimeout ) ( cb )
103+ ( id , cb ) => {
104+ // Check if we've happened to already abort
105+ if ( ! this . _controller ) return cb ( )
106+
107+ this . _query ( id , {
108+ timeout : walkTimeout ,
109+ signal : this . _controller . signal
110+ } , cb )
111+ }
123112 ] , ( err ) => {
124- if ( err ) {
125- this . log . error ( 'query finished with error' , err )
126- return callback ( err )
113+ if ( err && err . code !== 'ETIMEDOUT' ) {
114+ this . log . error ( 'query %d finished with error' , i , err )
115+ return next ( err )
127116 }
128117
129- this . log ( 'done' )
130- callback ( null )
118+ this . log ( 'finished query %d' , i )
119+ next ( null )
131120 } )
121+ } , ( err ) => {
122+ this . _controller = null
123+ this . log ( 'finished queries' )
124+ callback ( err )
132125 } )
133126 }
134127
135128 /**
136129 * The query run during a random walk request.
137130 *
131+ * TODO: While query currently supports an abort controller, it is not
132+ * yet supported by `DHT.findPeer`. Once https://github.com/libp2p/js-libp2p-kad-dht/pull/82
133+ * is complete, and AbortController support has been added to the
134+ * DHT query functions, the abort here will just work, provided the
135+ * functions support `options.signal`. Once done, this todo should be
136+ * removed.
137+ *
138138 * @param {PeerId } id
139+ * @param {object } options
140+ * @param {number } options.timeout
141+ * @param {AbortControllerSignal } options.signal
139142 * @param {function(Error) } callback
140143 * @returns {void }
141144 *
142145 * @private
143146 */
144- _query ( id , callback ) {
147+ _query ( id , options , callback ) {
145148 this . log ( 'query:%s' , id . toB58String ( ) )
146149
147- this . _kadDHT . findPeer ( id , ( err , peer ) => {
148- if ( err . code === 'ERR_NOT_FOUND' ) {
150+ this . _kadDHT . findPeer ( id , options , ( err , peer ) => {
151+ if ( err && err . code === 'ERR_NOT_FOUND' ) {
149152 // expected case, we asked for random stuff after all
150153 return callback ( )
151154 }
0 commit comments