samchon / tgrid

TypeScript RPC (Remote Procedure Call) for WebSocket and Worker protocols
https://tgrid.com/
MIT License
141 stars 19 forks source link

Protocol - Workers #6

Closed samchon closed 5 years ago

samchon commented 5 years ago

Outline

TGrid will consider the worker as a remote system.

JavaScript does not support the thread. JavaScript only provides worker, which is almost same with the process. In the worker environment, sharing memory variable is not possible. Only IPC (Inter-Process Communication) is allowed.

In such restriction, I will provide RFC (Remote Function Call) for help the worker. With the RFC, it's not possible to sharing memory, however, sharing objects and functions may be possible. That's the reason why I'm planning to consider workers as remote systems.

Components

1. Dedicated Worker

1.1. WorkerServer

namespace tgrid.protocols.workers
{
    export class WorkerServer<Provider extends object>
        extends basic.CommunicatorBase<Provider>
    {
        /* ----------------------------------------------------------------
            CONSTRUCTORS
        ---------------------------------------------------------------- */
        /**
         * Initializer Constructor.
         * 
         * @param provider An object providing functions for the connector.
         */
        public constructor(provider: Provider);

        /**
         * Close connection.
         */
        public close(): Promise<void>;

        /* ----------------------------------------------------------------
            ACCESSORS
        ---------------------------------------------------------------- */
        /**
         * Get driver for remote controller.
         *
         * @return A driver for the remote Controller.
         */
        public getDriver<Controller extends object>(): components.Driver<Controller>;

        /**
         * Join connection.
         */
        public join(): Promise<void>;
    }
}

1.2. WorkerConnector

namespace tgrid.protocols.workers
{
    export class WorkerConnector<Provider extends object>
        extends basic.CommunicatorBase<Provider>
    {
        /* ----------------------------------------------------------------
            CONSTRUCTORS
        ---------------------------------------------------------------- */
        /**
         * Initializer Constructor.
         * 
         * @param provider An object providing functions for the server.
         */
        public constructor(provider: Provider);

        /**
         * Connect to worker server with compilation.
         * 
         * @param source JS Source Code to be server with compilation. 
         *               It would better make the source code to be bundled.
         */
        public compile(source: string): Promise<void>;

        /**
         * Connect to worker server.
         * 
         * @param jsFile JS File to be worker server.
         */
        public connect(jsFile: string): Promise<void>;

        /**
         * Close connection.
         */
        public close(): Promise<void>;

        /* ----------------------------------------------------------------
            ACCESSORS
        ---------------------------------------------------------------- */
        /**
         * Get driver for remote controller.
         *
         * @return A driver for the remote Controller.
         */
        public getDriver<Controller extends object>(): components.Driver<Controller>;

        /**
         * Join connection.
         */
        public join(): Promise<void>;
    }
}

2. Shared Worker

2.1. SharedWorkerServer

namespace tgrid.protocols.workers
{
    export class SharedWorkerServer
    {
        /**
         * Default Constructor.
         */
        public constructor();

        /**
         * Open Server.
         * 
         * @param cb Callback function called whenever client connects.
         */
        public open(cb: (acceptor: SharedWorkerAcceptor) => any): Promise<void>;

        /**
         * Close Server.
         */
        public close(): Promise<void>;
    }
}

2.2. SharedWorkerAcceptor

namespace tgrid.protocols.workers
{
    export class SharedWorkerAcceptor
        extends basic.CommunicatorBase
    {
        /* ----------------------------------------------------------------
            CONSTRUCTORS
        ---------------------------------------------------------------- */
        /**
         * Hidden Constructor.
         * 
         * You can't create WebAcceptor by yourself. It would be created only by the WebServer.
         */
        private constructor(server, port);

        /**
         * Close connection.
         */
        public close(): Promise<void>;

        /* ----------------------------------------------------------------
            HANDSHAKES
        ---------------------------------------------------------------- */
        /**
         * Accept connection.
         */
        public accept(): Promise<void>;

        /**
         * Reject connection.
         */
        public reject(): Promise<void>;

        /**
         * Start listening.
         * 
         * Start listening data (function requests) from the remote client.
         * 
         * @param provider An object to provide functions for the remote client.
         */
        public listen<Provider extends object>
            (provider: Provider): Promise<void>;

        /* ----------------------------------------------------------------
            ACCCSSORS
        ---------------------------------------------------------------- */
        /**
         * Get driver for remote controller.
         *
         * @return A driver for the remote Controller.
         */
        public getDriver<Controller extends object>(): components.Driver<Controller>;

        /**
         * Wait server to provide.
         * 
         * Wait server to specify its `Provider`.
         */
        public wait(): Promise<void>;

        /**
         * Join connection.
         */
        public join(): Promise<void>;
    }
}

2.3. SharedWorkerConnector

namespace tgrid.protocols.workers
{
    export class SharedWorkerConnector<Provider extends object>
        extends basic.CommunicatorBase<Provider>
    {
        /**
         * Initializer Constructor.
         * 
         * @param provider A provider for server.
         */
        public constructor(provider: Provider);

        /**
         * Connect to shared worker server.
         * 
         * @param jsFile JS File to be worker server.
         */
        public connect(jsFile: string): Promise<void>;

        /**
         * Close connection.
         */
        public close(): Promise<void>;

        /* ----------------------------------------------------------------
            ACCCSSORS
        ---------------------------------------------------------------- */
        /**
         * Get driver for remote controller.
         *
         * @return A driver for the remote Controller.
         */
        public getDriver<Controller extends object>(): components.Driver<Controller>;

        /**
         * Wait server to provide.
         * 
         * Wait server to specify its `Provider`.
         */
        public wait(): Promise<void>;

        /**
         * Join connection.
         */
        public join(): Promise<void>;

        public get state(): SharedWorkerConnector.State;
    }

    export namespace SharedWorkerConnector
    {
        export const enum State { ... }

        /**
         * Compile source to JS File.
         * 
         * @return Path of compiled JS File.
         */
        export function compile(source: string): string;
    }
}

3. Service Worker

I'm studying the Service Worker. If the Service Worker is enough good to consider as remote system, then it will also be implemented.

Sample Code

server.ts

import { WorkerServer } from "tgrid/protocols/workers";
import { Driver } from "tgrid/basic";

import { Mutex } from "tstl/thread";
import { randint } from "tstl/algorithm";

interface IController
{
    mutex: Mutex;
    print(str: string): void;
}

async function main(str: string): Promise<void>
{
    // PREPARE SERVER & DRIVER
    let server: WorkerServer = new WorkerServer();
    let driver: Driver<IController> = server.getDriver<IController>();

    // REMOTE FUNCTION CALLS
    await driver.mutex.lock();
    {
        for (let i: number = 0; i < 20; ++i)
            await driver.print(str);
        await driver.print("\n");
    }
    await driver.mutex.unlock();

    // CLOSE THE SERVER (WORKER)
    await server.close();
}
main(randint(0, 9) + "");

client.ts

import { WorkerConnector } from "tgrid/protocols/workers";

import { Vector } from "tstl/container";
import { Mutex } from "tstl/thread";

// FEATURES TO PROVIDE
namespace provider
{
    export var mutex = new Mutex();
    export function print(str: string): void
    {
        process.stdout.write(str);
    }
}

async function main(): Promise<void>
{
    let workers: Vector<WorkerConnector<typeof provider>> = new Vector();

    // CREATE WORKERS
    for (let i: number = 0; i < 4; ++i)
    {
        workers.push_back(new WorkerConnector(provider));
        workers.back().connect(__dirname + "/server.js");
    }

    // WAIT THEM TO BE CLOSED
    for (let w of workers)
        await w.join();
}
main();