Source

bosdyn-client/token_manager_update.js

'use strict';

const { setTimeout: sleep } = require('node:timers/promises');
const { Worker, isMainThread, workerData, parentPort } = require('node:worker_threads');

const { InvalidTokenError } = require('./auth');
const { ResponseError, RpcError } = require('./exceptions');
const { WriteFailedError } = require('./token_cache');

class TokenManager {
  /**
   * Create an instance of TokenManager's class.
   * @param {Object} robot Robot object.
   * @param {number} [timestamp=null] The date in timestamp to use.
   */
  constructor(robot, timestamp = null) {
    this.robot = robot;
    this._last_timestamp = timestamp || Date.now();

    this._loopStart = true;
    // This._loop = setImmediate(this.update.bind(this)).unref();
  }

  is_alive() {
    return this._loopStart;
  }

  stop() {
    // ClearImmediate(this._loop);
    this._loopStart = false;
  }

  /**
   * Refresh the user token as needed.
   */
  async update() {
    const USER_TOKEN_REFRESH_TIME_DELTA = 3_600;
    const USER_TOKEN_RETRY_INTERVAL_START = 1_000;

    let retry_interval = USER_TOKEN_RETRY_INTERVAL_START;
    /* eslint-disable no-await-in-loop */
    while (this._loopStart) {
      let elapsed_time = Date.now() - this._last_timestamp;
      if (elapsed_time >= USER_TOKEN_REFRESH_TIME_DELTA) {
        try {
          await this.robot.authenticate_with_token(this.robot.user_token);
        } catch (e) {
          if (e instanceof WriteFailedError) {
            console.error('[TokenManager] Failed to save the token to the cache. Continuing without caching.');
          } else if (e instanceof InvalidTokenError || e instanceof ResponseError || e instanceof RpcError) {
            console.error(`[TokenManager] Error refreshing the token. Retry in ${retry_interval}`);
            await sleep(retry_interval);
            retry_interval = Math.min(2 * retry_interval, USER_TOKEN_REFRESH_TIME_DELTA);
            continue;
          }
        }

        retry_interval = USER_TOKEN_RETRY_INTERVAL_START;
        this._last_timestamp = Date.now();
        elapsed_time = USER_TOKEN_REFRESH_TIME_DELTA;
      }
      await sleep(elapsed_time);
    }
    /* eslint-enable no-await-in-loop */
    console.debug(`[TokenManager] Shutting down monitoring of token belonging to robot ${this.robot.address}`);
  }
}

if (isMainThread) {
  class TokenManagerRunner {
    constructor(robot, timestamp) {
      this.runner = new TokenManager(robot, timestamp);
      console.log(this.runner);
      this.worker = new Worker(__filename, { workerData: { runner: this.runner } });
      this._stop = false;
    }

    is_alive() {
      return !this._stop;
    }

    stop() {
      this._stop = true;
      this.worker.postMessage({ t: 'stopUpdate' });
      return this.worker.terminate();
    }

    update() {
      this.worker.postMessage({ t: 'startUpdate' });
    }
  }

  module.exports = { TokenManager: TokenManagerRunner };

  console.log('Outside Worker !', isMainThread);
} else {
  console.log('Inside Worker !', isMainThread);

  const { runner } = workerData;
  Object.setPrototypeOf(runner, TokenManager.prototype);

  parentPort.on('message', e => {
    if (e.t === 'startUpdate') {
      runner.update();
    } else if (e.t === 'stopUpdate') {
      runner.stop();
    }
  });
}