UNPKG

7.09 kBJavaScriptView Raw
1"use strict";
2/*! *****************************************************************************
3Copyright (c) Microsoft Corporation.
4Licensed under the Apache License, Version 2.0.
5
6See LICENSE file in the project root for details.
7***************************************************************************** */
8Object.defineProperty(exports, "__esModule", { value: true });
9const list_1 = require("./list");
10const cancellation_1 = require("./cancellation");
11const utils_1 = require("./utils");
12const adapter_1 = require("./adapter");
13/**
14 * Enables multiple tasks to cooperatively work on an algorithm through
15 * multiple phases.
16 */
17class Barrier {
18 /**
19 * Initializes a new instance of the Barrier class.
20 *
21 * @param participantCount The initial number of participants for the barrier.
22 * @param postPhaseAction An action to execute between each phase.
23 */
24 constructor(participantCount, postPhaseAction) {
25 this._isExecutingPostPhaseAction = false;
26 this._phaseNumber = 0;
27 this._waiters = new list_1.LinkedList();
28 if (!utils_1.isNumber(participantCount))
29 throw new TypeError("Number expected: participantCount.");
30 if ((participantCount |= 0) < 0)
31 throw new RangeError("Argument out of range: participantCount.");
32 if (!utils_1.isFunction(postPhaseAction, /*optional*/ true))
33 throw new TypeError("Function expected: postPhaseAction.");
34 this._participantCount = participantCount;
35 this._remainingParticipants = participantCount;
36 this._postPhaseAction = postPhaseAction;
37 }
38 /**
39 * Gets the number of the Barrier's current phase.
40 */
41 get currentPhaseNumber() {
42 return this._phaseNumber;
43 }
44 /**
45 * Gets the total number of participants in the barrier.
46 */
47 get participantCount() {
48 return this._participantCount;
49 }
50 /**
51 * Gets the number of participants in the barrier that haven't yet signaled in the current phase.
52 */
53 get remainingParticipants() {
54 return this._remainingParticipants;
55 }
56 /**
57 * Notifies the Barrier there will be additional participants.
58 *
59 * @param participantCount The number of additional participants.
60 */
61 add(participantCount) {
62 if (utils_1.isMissing(participantCount))
63 participantCount = 1;
64 if (!utils_1.isNumber(participantCount))
65 throw new TypeError("Number expected: participantCount.");
66 if ((participantCount |= 0) <= 0)
67 throw new RangeError("Argument out of range: participantCount.");
68 if (this._isExecutingPostPhaseAction)
69 throw new Error("This method may not be called from within the postPhaseAction.");
70 this._participantCount += participantCount;
71 this._remainingParticipants += participantCount;
72 }
73 /**
74 * Notifies the Barrier there will be fewer participants.
75 *
76 * @param participantCount The number of participants to remove.
77 */
78 remove(participantCount) {
79 if (utils_1.isMissing(participantCount))
80 participantCount = 1;
81 if (!utils_1.isNumber(participantCount))
82 throw new TypeError("Number expected: participantCount.");
83 if ((participantCount |= 0) <= 0)
84 throw new RangeError("Argument out of range: participantCount.");
85 if (this._participantCount < participantCount)
86 throw new RangeError("Argument out of range: participantCount.");
87 if (this._isExecutingPostPhaseAction)
88 throw new Error("This method may not be called from within the postPhaseAction.");
89 this._participantCount -= participantCount;
90 this._remainingParticipants -= participantCount;
91 if (this._participantCount === 0) {
92 this._finishPhase();
93 }
94 }
95 /**
96 * Signals that a participant has reached the barrier and waits for all other participants
97 * to reach the barrier.
98 *
99 * @param token An optional CancellationToken used to cancel the request.
100 */
101 signalAndWait(token) {
102 return new Promise((resolve, reject) => {
103 const _token = adapter_1.getToken(token);
104 _token.throwIfCancellationRequested();
105 if (this._isExecutingPostPhaseAction)
106 throw new Error("This method may not be called from within the postPhaseAction.");
107 if (this._participantCount === 0)
108 throw new Error("The barrier has no registered participants.");
109 if (this._remainingParticipants === 0)
110 throw new Error("The number of operations using the barrier exceeded the number of registered participants.");
111 const node = this._waiters.push({
112 resolve: () => {
113 registration.unregister();
114 if (_token.cancellationRequested) {
115 reject(new cancellation_1.CancelError());
116 }
117 else {
118 resolve();
119 }
120 },
121 reject: reason => {
122 registration.unregister();
123 if (_token.cancellationRequested) {
124 reject(new cancellation_1.CancelError());
125 }
126 else {
127 reject(reason);
128 }
129 }
130 });
131 const registration = _token.register(() => {
132 if (node.list) {
133 node.list.deleteNode(node);
134 reject(new cancellation_1.CancelError());
135 }
136 });
137 this._remainingParticipants--;
138 if (this._remainingParticipants === 0) {
139 this._finishPhase();
140 }
141 });
142 }
143 _finishPhase() {
144 const postPhaseAction = this._postPhaseAction;
145 if (postPhaseAction) {
146 this._isExecutingPostPhaseAction = true;
147 Promise
148 .resolve()
149 .then(() => postPhaseAction(this))
150 .then(() => this._resolveNextPhase(), error => this._rejectNextPhase(error));
151 }
152 else {
153 Promise
154 .resolve()
155 .then(() => this._resolveNextPhase());
156 }
157 }
158 _nextPhase() {
159 this._isExecutingPostPhaseAction = false;
160 this._remainingParticipants = this._participantCount;
161 this._phaseNumber++;
162 }
163 _resolveNextPhase() {
164 this._nextPhase();
165 for (const deferred of this._waiters.drain()) {
166 if (deferred)
167 deferred.resolve();
168 }
169 }
170 _rejectNextPhase(error) {
171 this._nextPhase();
172 for (const deferred of this._waiters.drain()) {
173 if (deferred)
174 deferred.reject(error);
175 }
176 }
177}
178exports.Barrier = Barrier;