Skip to content
Closed
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
diagnostics_channel: make tracing channels safer and support symbol n…
…ames
  • Loading branch information
Stephen Belanger
Stephen Belanger committed Nov 9, 2022
commit f8605a62b49003cc0bbe703361b848aa1ea4ddae
82 changes: 50 additions & 32 deletions lib/diagnostics_channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,15 @@ class ActiveChannel {
subscribe(subscription) {
validateFunction(subscription, 'subscription');
ArrayPrototypePush(this._subscribers, subscription);
this._weak.incRef();
}

unsubscribe(subscription) {
const index = ArrayPrototypeIndexOf(this._subscribers, subscription);
if (index === -1) return false;

ArrayPrototypeSplice(this._subscribers, index, 1);
this._weak.decRef();

// When there are no more active subscribers, restore to fast prototype.
if (!this._subscribers.length) {
Expand Down Expand Up @@ -68,6 +70,7 @@ class ActiveChannel {
class Channel {
constructor(name) {
this._subscribers = undefined;
this._weak = undefined;
this.name = name;
}

Expand Down Expand Up @@ -107,24 +110,17 @@ function channel(name) {
}

channel = new Channel(name);
channels[name] = new WeakReference(channel);
channel._weak = new WeakReference(channel);
channels[name] = channel._weak;
return channel;
}

function subscribe(name, subscription) {
const chan = channel(name);
channels[name].incRef();
chan.subscribe(subscription);
return channel(name).subscribe(subscription);
}

function unsubscribe(name, subscription) {
const chan = channel(name);
if (!chan.unsubscribe(subscription)) {
return false;
}

channels[name].decRef();
return true;
return channel(name).unsubscribe(subscription);
}

function hasSubscribers(name) {
Expand All @@ -139,46 +135,54 @@ function hasSubscribers(name) {
}

class TracingChannel {
#channels;

constructor(name) {
this.name = name;
this.channels = {
start: channel(`${name}.start`),
end: channel(`${name}.end`),
asyncEnd: channel(`${name}.asyncEnd`),
error: channel(`${name}.error`)
this.#channels = {
start: new Channel(Symbol(`tracing.start`)),
end: new Channel(Symbol(`tracing.end`)),
asyncEnd: new Channel(Symbol(`tracing.asyncEnd`)),
error: new Channel(Symbol(`tracing.error`))
};
}

// Attach WeakReference to all the sub-channels so the liveness management
// in subscribe/unsubscribe keeps the TracingChannel the sub-channels are
// attached to alive.
set _weak(weak) {
for (const key in this.#channels) {
this.#channels[key]._weak = weak;
}
}

get hasSubscribers() {
const { channels } = this;
for (const key in channels) {
if (channels[key].hasSubscribers) return true;
for (const key in this.#channels) {
if (this.#channels[key].hasSubscribers) {
return true;
}
}
return false;
}

subscribe(handlers) {
let subscribed = true;
for (const key in handlers) {
Comment thread
mcollina marked this conversation as resolved.
Outdated
if (!subscribe(`${this.name}.${key}`, handlers[key])) {
subscribed = false;
}
this.#channels[key]?.subscribe(handlers[key]);
}
return subscribed;
}

unsubscribe(handlers) {
let unsubscribed = true;
for (const key in handlers) {
if (!unsubscribe(`${this.name}.${key}`, handlers[key])) {
unsubscribed = false;
const channel = this.#channels[key];
if (!channel || !channel.unsubscribe(handlers[key])) {
return false;

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why end the loop just because there is maybe an extra key in the object?

}
}
return unsubscribed;
return true;
}

traceSync(fn, ctx = {}, thisArg, ...args) {
const { start, end, error } = this.channels;
const { start, end, error } = this.#channels;
start.publish(ctx);
try {
const result = ReflectApply(fn, thisArg, args);
Expand All @@ -194,7 +198,7 @@ class TracingChannel {
}

tracePromise(fn, ctx = {}, thisArg, ...args) {
Comment thread
mcollina marked this conversation as resolved.
Outdated
const { asyncEnd, start, end, error } = this.channels;
const { asyncEnd, start, end, error } = this.#channels;
start.publish(ctx);

const reject = (err) => {
Expand Down Expand Up @@ -223,7 +227,7 @@ class TracingChannel {
}

traceCallback(fn, position = 0, ctx = {}, thisArg, ...args) {
const { start, end, asyncEnd, error } = this.channels;
const { start, end, asyncEnd, error } = this.#channels;
start.publish(ctx);

function wrap(fn) {
Expand Down Expand Up @@ -258,8 +262,22 @@ class TracingChannel {
}
}

const tracingChannels = ObjectCreate(null);

function tracingChannel(name) {
return new TracingChannel(name);
let channel;
const ref = tracingChannels[name];
if (ref) channel = ref.get();
if (channel) return channel;

if (typeof name !== 'string' && typeof name !== 'symbol') {
throw new ERR_INVALID_ARG_TYPE('tracingChannel', ['string', 'symbol'], name);
}

channel = new TracingChannel(name);
channel._weak = new WeakReference(channel);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
channel._weak = new WeakReference(channel);
channel._weak = tracingChannels[name] = new WeakReference(channel);

TracingChannel#_weak is a setter, there is no getter otherwise this is always undefined

tracingChannels[name] = channel._weak;
return channel;
}

module.exports = {
Expand Down