浏览代码

fix(JingleSessionPC): prevent from queueing tasks after close

Also do not execute any peerconnection callbacks like
'oniceconnectionstatechange', 'onicecandidate' etc. after close.
release-8443
paweldomas 6 年前
父节点
当前提交
036662c653
共有 2 个文件被更改,包括 81 次插入72 次删除
  1. 55
    0
      modules/util/AsyncQueue.js
  2. 26
    72
      modules/xmpp/JingleSessionPC.js

+ 55
- 0
modules/util/AsyncQueue.js 查看文件

1
+import async from 'async';
2
+
3
+/**
4
+ * A queue for async task execution.
5
+ */
6
+export default class AsyncQueue {
7
+    /**
8
+     * Creates new instance.
9
+     */
10
+    constructor() {
11
+        this.modificationQueue = async.queue(this._processQueueTasks.bind(this), 1);
12
+        this.stopped = false;
13
+    }
14
+
15
+    /**
16
+     * Internal task processing implementation which makes things work.
17
+     */
18
+    _processQueueTasks(task, finishedCallback) {
19
+        task(finishedCallback);
20
+    }
21
+
22
+    /**
23
+     * The 'workFunction' function will be given a callback it MUST call with either:
24
+     *  1) No arguments if it was successful or
25
+     *  2) An error argument if there was an error
26
+     * If the task wants to process the success or failure of the task, it
27
+     * should pass the {@code callback} to the push function, e.g.:
28
+     * queue.push(task, (err) => {
29
+     *     if (err) {
30
+     *         // error handling
31
+     *     } else {
32
+     *         // success handling
33
+     *     }
34
+     * });
35
+     *
36
+     * @param {function} workFunction - The task to be execute. See the description above.
37
+     * @param {function} [callback] - Optional callback to be called after the task has been executed.
38
+     */
39
+    push(workFunction, callback) {
40
+        if (this.stopped) {
41
+            callback && callback('The queue has been stopped');
42
+
43
+            return;
44
+        }
45
+        this.modificationQueue.push(workFunction, callback);
46
+    }
47
+
48
+    /**
49
+     * Shutdowns the queue. All already queued tasks will execute, but no future tasks can be added. If a task is added
50
+     * after the queue has been shutdown then the callback will be called with an error.
51
+     */
52
+    shutdown() {
53
+        this.stopped = true;
54
+    }
55
+}

+ 26
- 72
modules/xmpp/JingleSessionPC.js 查看文件

4
     ICE_DURATION,
4
     ICE_DURATION,
5
     ICE_STATE_CHANGED
5
     ICE_STATE_CHANGED
6
 } from '../../service/statistics/AnalyticsEvents';
6
 } from '../../service/statistics/AnalyticsEvents';
7
-import async from 'async';
8
 import { getLogger } from 'jitsi-meet-logger';
7
 import { getLogger } from 'jitsi-meet-logger';
9
 import { $iq, Strophe } from 'strophe.js';
8
 import { $iq, Strophe } from 'strophe.js';
10
 import { integerHash } from '../util/StringUtils';
9
 import { integerHash } from '../util/StringUtils';
19
 import RTCEvents from '../../service/RTC/RTCEvents';
18
 import RTCEvents from '../../service/RTC/RTCEvents';
20
 import Statistics from '../statistics/statistics';
19
 import Statistics from '../statistics/statistics';
21
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
20
 import XMPPEvents from '../../service/xmpp/XMPPEvents';
21
+import AsyncQueue from '../util/AsyncQueue';
22
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
22
 import GlobalOnErrorHandler from '../util/GlobalOnErrorHandler';
23
 
23
 
24
 const logger = getLogger(__filename);
24
 const logger = getLogger(__filename);
212
          */
212
          */
213
         this.signalingLayer = new SignalingLayerImpl();
213
         this.signalingLayer = new SignalingLayerImpl();
214
 
214
 
215
-        this.modificationQueue
216
-            = async.queue(this._processQueueTasks.bind(this), 1);
215
+        /**
216
+         * The queue used to serialize operations done on the peerconnection.
217
+         *
218
+         * @type {AsyncQueue}
219
+         */
220
+        this.modificationQueue = new AsyncQueue();
217
 
221
 
218
         /**
222
         /**
219
          * Flag used to guarantee that the connection established event is
223
          * Flag used to guarantee that the connection established event is
234
     /* eslint-enable max-params */
238
     /* eslint-enable max-params */
235
 
239
 
236
     /**
240
     /**
237
-     * Checks whether or not this session instance has been ended and eventually
238
-     * logs a message which mentions that given <tt>actionName</tt> was
239
-     * cancelled.
240
-     * @param {string} actionName
241
-     * @return {boolean} <tt>true</tt> if this {@link JingleSessionPC} has
242
-     * entered {@link JingleSessionState.ENDED} or <tt>false</tt> otherwise.
241
+     * Checks whether or not this session instance is still operational.
243
      * @private
242
      * @private
243
+     * @returns {boolean} {@code true} if operation or {@code false} otherwise.
244
      */
244
      */
245
-    _assertNotEnded(actionName) {
246
-        if (this.state === JingleSessionState.ENDED) {
247
-            logger.log(
248
-                `The session has ended - cancelling action: ${actionName}`);
249
-
250
-            return false;
251
-        }
252
-
253
-        return true;
245
+    _assertNotEnded() {
246
+        return this.state !== JingleSessionState.ENDED;
254
     }
247
     }
255
 
248
 
256
     /**
249
     /**
316
                     pcOptions);
309
                     pcOptions);
317
 
310
 
318
         this.peerconnection.onicecandidate = ev => {
311
         this.peerconnection.onicecandidate = ev => {
319
-            if (!ev) {
312
+            if (!ev || !this._assertNotEnded()) {
320
                 // There was an incomplete check for ev before which left
313
                 // There was an incomplete check for ev before which left
321
                 // the last line of the function unprotected from a potential
314
                 // the last line of the function unprotected from a potential
322
                 // throw of an exception. Consequently, it may be argued that
315
                 // throw of an exception. Consequently, it may be argued that
372
         // "closed" instead.
365
         // "closed" instead.
373
         // I suppose at some point this will be moved to onconnectionstatechange
366
         // I suppose at some point this will be moved to onconnectionstatechange
374
         this.peerconnection.onsignalingstatechange = () => {
367
         this.peerconnection.onsignalingstatechange = () => {
375
-            if (!this.peerconnection) {
368
+            if (!this.peerconnection || !this._assertNotEnded()) {
376
                 return;
369
                 return;
377
             }
370
             }
378
             if (this.peerconnection.signalingState === 'stable') {
371
             if (this.peerconnection.signalingState === 'stable') {
379
                 this.wasstable = true;
372
                 this.wasstable = true;
380
-            } else if (
381
-                (this.peerconnection.signalingState === 'closed'
382
-                || this.peerconnection.connectionState === 'closed')
383
-                && !this.closed) {
373
+            } else if (this.peerconnection.signalingState === 'closed'
374
+                || this.peerconnection.connectionState === 'closed') {
384
                 this.room.eventEmitter.emit(XMPPEvents.SUSPEND_DETECTED, this);
375
                 this.room.eventEmitter.emit(XMPPEvents.SUSPEND_DETECTED, this);
385
             }
376
             }
386
         };
377
         };
392
          * the value of RTCPeerConnection.iceConnectionState changes.
383
          * the value of RTCPeerConnection.iceConnectionState changes.
393
          */
384
          */
394
         this.peerconnection.oniceconnectionstatechange = () => {
385
         this.peerconnection.oniceconnectionstatechange = () => {
395
-            if (!this.peerconnection
396
-                    || !this._assertNotEnded('oniceconnectionstatechange')) {
386
+            if (!this.peerconnection || !this._assertNotEnded()) {
397
                 return;
387
                 return;
398
             }
388
             }
399
             const now = window.performance.now();
389
             const now = window.performance.now();
473
                 this.isReconnect = false;
463
                 this.isReconnect = false;
474
                 break;
464
                 break;
475
             case 'disconnected':
465
             case 'disconnected':
476
-                if (this.closed) {
477
-                    break;
478
-                }
479
                 this.isReconnect = true;
466
                 this.isReconnect = true;
480
 
467
 
481
                 // Informs interested parties that the connection has been
468
                 // Informs interested parties that the connection has been
1320
      * @param reasonText
1307
      * @param reasonText
1321
      */
1308
      */
1322
     onTerminated(reasonCondition, reasonText) {
1309
     onTerminated(reasonCondition, reasonText) {
1323
-        this.state = JingleSessionState.ENDED;
1324
-        this.establishmentDuration = undefined;
1325
-
1326
         // Do something with reason and reasonCondition when we start to care
1310
         // Do something with reason and reasonCondition when we start to care
1327
         // this.reasonCondition = reasonCondition;
1311
         // this.reasonCondition = reasonCondition;
1328
         // this.reasonText = reasonText;
1312
         // this.reasonText = reasonText;
1488
         this.modificationQueue.push(workFunction);
1472
         this.modificationQueue.push(workFunction);
1489
     }
1473
     }
1490
 
1474
 
1491
-    /**
1492
-     * The 'task' function will be given a callback it MUST call with either:
1493
-     *  1) No arguments if it was successful or
1494
-     *  2) An error argument if there was an error
1495
-     * If the task wants to process the success or failure of the task, it
1496
-     * should pass a handler to the .push function, e.g.:
1497
-     * queue.push(task, (err) => {
1498
-     *     if (err) {
1499
-     *         // error handling
1500
-     *     } else {
1501
-     *         // success handling
1502
-     *     }
1503
-     * });
1504
-     */
1505
-    _processQueueTasks(task, finishedCallback) {
1506
-        task(finishedCallback);
1507
-    }
1508
-
1509
     /**
1475
     /**
1510
      * Takes in a jingle offer iq, returns the new sdp offer
1476
      * Takes in a jingle offer iq, returns the new sdp offer
1511
      * @param {jquery xml element} offerIq the incoming offer
1477
      * @param {jquery xml element} offerIq the incoming offer
1695
      */
1661
      */
1696
     replaceTrack(oldTrack, newTrack) {
1662
     replaceTrack(oldTrack, newTrack) {
1697
         const workFunction = finishedCallback => {
1663
         const workFunction = finishedCallback => {
1698
-            // Check if the connection was closed and pretend everything is OK.
1699
-            // This can happen if a track removal is scheduled but takes place
1700
-            // after the connection is closed.
1701
-            if (this.peerconnection.signalingState === 'closed'
1702
-                || this.peerconnection.connectionState === 'closed'
1703
-                || this.closed) {
1704
-
1705
-                finishedCallback();
1706
-
1707
-                return;
1708
-            }
1709
-
1710
             const oldLocalSdp = this.peerconnection.localDescription.sdp;
1664
             const oldLocalSdp = this.peerconnection.localDescription.sdp;
1711
 
1665
 
1712
             // NOTE the code below assumes that no more than 1 video track
1666
             // NOTE the code below assumes that no more than 1 video track
2251
      * Closes the peerconnection.
2205
      * Closes the peerconnection.
2252
      */
2206
      */
2253
     close() {
2207
     close() {
2254
-        this.closed = true;
2208
+        this.state = JingleSessionState.ENDED;
2209
+        this.establishmentDuration = undefined;
2255
 
2210
 
2256
-        // The signaling layer will remove it's listeners
2257
-        this.signalingLayer.setChatRoom(null);
2211
+        this.modificationQueue.push(finishCallback => {
2212
+            // The signaling layer will remove it's listeners
2213
+            this.signalingLayer.setChatRoom(null);
2258
 
2214
 
2259
-        // do not try to close if already closed.
2260
-        this.peerconnection
2261
-            && ((this.peerconnection.signalingState
2262
-                    && this.peerconnection.signalingState !== 'closed')
2263
-                || (this.peerconnection.connectionState
2264
-                    && this.peerconnection.connectionState !== 'closed'))
2265
-            && this.peerconnection.close();
2215
+            // do not try to close if already closed.
2216
+            this.peerconnection && this.peerconnection.close();
2217
+            finishCallback();
2218
+        });
2219
+        this.modificationQueue.shutdown();
2266
     }
2220
     }
2267
 
2221
 
2268
     /**
2222
     /**

正在加载...
取消
保存