From 9fd3d60bff19149b016a844728e8f82be556f51b Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Wed, 13 Mar 2024 16:12:36 +0900 Subject: [PATCH 1/2] Adjust refactoring of samchon/tgrid#58 --- .gitignore | 1 + .npmignore | 15 - .vscode/launch.json | 51 +- .vscode/settings.json | 10 + package.json | 33 +- prettier.config.js | 14 + src/MutexAcceptor.ts | 403 +++++----- src/MutexConnector.ts | 534 +++++++------ src/MutexServer.ts | 231 +++--- src/client/RemoteBarrier.ts | 245 +++--- src/client/RemoteConditionVariable.ts | 274 ++++--- src/client/RemoteLatch.ts | 271 ++++--- src/client/RemoteMutex.ts | 515 +++++++------ src/client/RemoteSemaphore.ts | 364 +++++---- src/client/index.ts | 11 +- src/index.ts | 2 +- src/module.ts | 2 +- src/server/GlobalGroup.ts | 30 +- src/server/ProviderCapsule.ts | 14 +- src/server/ProviderGroup.ts | 101 +-- src/server/components/IComponent.ts | 16 +- src/server/components/ServerBarrier.ts | 121 ++- .../components/ServerConditionVariable.ts | 226 +++--- src/server/components/ServerLatch.ts | 122 ++- src/server/components/ServerMutex.ts | 700 +++++++++--------- src/server/components/ServerSemaphore.ts | 414 +++++------ src/server/components/SolidComponent.ts | 207 +++--- src/server/components/internal/Disolver.ts | 9 +- src/server/components/internal/Joiner.ts | 7 +- src/server/global/GlobalBarriers.ts | 19 +- src/server/global/GlobalBase.ts | 59 +- src/server/global/GlobalConditionVariables.ts | 23 +- src/server/global/GlobalLatches.ts | 19 +- src/server/global/GlobalMutexes.ts | 19 +- src/server/global/GlobalSemaphores.ts | 28 +- src/server/providers/BarriersProvider.ts | 52 +- .../providers/ConditionVariablesProvider.ts | 42 +- src/server/providers/LatchesProvider.ts | 48 +- src/server/providers/MutexesProvider.ts | 93 +-- src/server/providers/ProviderBase.ts | 107 +-- src/server/providers/SemaphoresProvider.ts | 46 +- .../test_condition_variable_disconnections.ts | 33 - .../test_condition_variable_waits.ts | 66 -- src/test/index.ts | 130 ---- src/test/internal/ConnectionFactory.ts | 7 - src/test/internal/IActivation.ts | 5 - src/test/internal/Validator.ts | 198 ----- src/test/manual/remote-mutex.ts | 57 -- .../mutextes/test_mutex_disconnections.ts | 36 - src/test/mutextes/test_mutex_locks.ts | 113 --- .../semaphores/test_semaphore_acquires.ts | 104 --- .../test_semaphore_disconnections.ts | 55 -- src/test/semaphores/test_semaphore_simple.ts | 22 - src/test/test_destructors.ts | 32 - .../test_condition_variable_disconnections.ts | 43 ++ .../test_condition_variable_waits.ts | 66 ++ test/index.ts | 130 ++++ test/internal/ConnectionFactory.ts | 7 + test/internal/IActivation.ts | 4 + test/internal/Validator.ts | 224 ++++++ test/manual/remote-mutex.ts | 52 ++ test/mutextes/test_mutex_disconnections.ts | 38 + test/mutextes/test_mutex_locks.ts | 103 +++ test/semaphores/test_semaphore_acquires.ts | 111 +++ .../test_semaphore_disconnections.ts | 72 ++ test/semaphores/test_semaphore_simple.ts | 25 + test/test_destructors.ts | 29 + test/tsconfig.json | 17 + tsconfig.json | 3 +- 69 files changed, 3580 insertions(+), 3700 deletions(-) delete mode 100644 .npmignore create mode 100644 .vscode/settings.json create mode 100644 prettier.config.js delete mode 100644 src/test/condition_variables/test_condition_variable_disconnections.ts delete mode 100644 src/test/condition_variables/test_condition_variable_waits.ts delete mode 100644 src/test/index.ts delete mode 100644 src/test/internal/ConnectionFactory.ts delete mode 100644 src/test/internal/IActivation.ts delete mode 100644 src/test/internal/Validator.ts delete mode 100644 src/test/manual/remote-mutex.ts delete mode 100644 src/test/mutextes/test_mutex_disconnections.ts delete mode 100644 src/test/mutextes/test_mutex_locks.ts delete mode 100644 src/test/semaphores/test_semaphore_acquires.ts delete mode 100644 src/test/semaphores/test_semaphore_disconnections.ts delete mode 100644 src/test/semaphores/test_semaphore_simple.ts delete mode 100644 src/test/test_destructors.ts create mode 100644 test/condition_variables/test_condition_variable_disconnections.ts create mode 100644 test/condition_variables/test_condition_variable_waits.ts create mode 100644 test/index.ts create mode 100644 test/internal/ConnectionFactory.ts create mode 100644 test/internal/IActivation.ts create mode 100644 test/internal/Validator.ts create mode 100644 test/manual/remote-mutex.ts create mode 100644 test/mutextes/test_mutex_disconnections.ts create mode 100644 test/mutextes/test_mutex_locks.ts create mode 100644 test/semaphores/test_semaphore_acquires.ts create mode 100644 test/semaphores/test_semaphore_disconnections.ts create mode 100644 test/semaphores/test_semaphore_simple.ts create mode 100644 test/test_destructors.ts create mode 100644 test/tsconfig.json diff --git a/.gitignore b/.gitignore index 6c68c38..4160da5 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,5 @@ lib/ node_modules/ package-lock.json +pnpm-lock.yaml *.log \ No newline at end of file diff --git a/.npmignore b/.npmignore deleted file mode 100644 index 0fc45e0..0000000 --- a/.npmignore +++ /dev/null @@ -1,15 +0,0 @@ -# PROJECT FILES -/.git/ -/.github/ -/.vscode/ -/node_modules/ -/src/ - -# CONFIGURATIONS -package-lock.json -tsconfig.json -*.*ignore - -# SPECIAL FILES -/lib/test/ -*.log \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 30f8601..d7686c9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,31 +1,24 @@ { - // Use IntelliSense to learn about possible Node.js debug attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "node", - "request": "launch", - "name": "TypeScript Test using TS-NODE", - "runtimeArgs": [ - "-r", - "ts-node/register" - ], - "args": [ - "${workspaceRoot}/src/test/index.ts" - ] - }, - { - "type": "node", - "request": "launch", - "name": "JavaScript Test using SourceMap", - "program": "${workspaceRoot}/lib/test/index.js", - "cwd": "${workspaceRoot}", - - // TypeScript - "sourceMaps": true, - "outFiles": ["${workspaceRot}/lib/**.js"] - } - ] + // Use IntelliSense to learn about possible Node.js debug attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "node", + "request": "launch", + "name": "Launch Program", + "program": "${workspaceRoot}/bin/test/main.js", + "cwd": "${workspaceRoot}", + // TypeScript + "sourceMaps": true + }, + { + "type": "node", + "request": "attach", + "name": "Attach to Process", + "port": 5858, + "outFiles": [] + } + ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..a3553df --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,10 @@ +{ + "editor.tabSize": 2, + "editor.formatOnSave": true, + "[typescript][javascript]": { + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + } +} \ No newline at end of file diff --git a/package.json b/package.json index 62b985a..902b707 100644 --- a/package.json +++ b/package.json @@ -1,16 +1,17 @@ { "name": "mutex-server", - "version": "0.3.2", + "version": "0.4.0", "description": "Mutex Server using WebSocket", "main": "lib/index.js", "typings": "lib/index.d.ts", "scripts": { - "api": "typedoc src ../tstl/src --exclude \"**/+(test|benchmark)/**\" --excludeNotDocumented --plugin typedoc-plugin-external-module-name --plugin typedoc-plugin-exclude-references --out ../mutex.dev/api", - "build": "npm run compile", - "compile": "rimraf lib && tsc", - "dev": "rimraf lib && tsc --watch", - "test": "node lib/test", - "test:ts": "ts-node src/test" + "build": "npm run build:main && npm run build:test", + "build:main": "rimraf lib && tsc", + "build:test": "rimraf bin && tsc --project test/tsconfig.json", + "dev": "npm run build:test -- --watch", + "prepare": "ts-patch install", + "prettier": "prettier src --write && prettier test --write", + "test": "node bin/test" }, "repository": { "type": "git", @@ -48,17 +49,27 @@ }, "homepage": "https://github.com/samchon/mutex", "devDependencies": { + "@trivago/prettier-plugin-sort-imports": "^4.3.0", "@types/cli": "^0.11.19", "@types/node": "^13.11.1", "cli": "^1.0.1", + "prettier": "^3.2.5", "rimraf": "^3.0.2", "source-map-support": "^0.5.16", "ts-node": "^8.8.2", - "typedoc": "^0.22.10", - "typescript": "^4.5.2" + "ts-patch": "^3.1.2", + "typedoc": "^0.25.12", + "typescript": "^5.4.2", + "typescript-transform-paths": "^3.4.7" }, "dependencies": { - "tgrid": "^0.8.9", + "tgrid": "^0.9.0", "tstl": "^2.5.13" - } + }, + "files": [ + "LICENSE", + "README.md", + "lib", + "src" + ] } diff --git a/prettier.config.js b/prettier.config.js new file mode 100644 index 0000000..c319b6e --- /dev/null +++ b/prettier.config.js @@ -0,0 +1,14 @@ +module.exports = { + parser: "typescript", + printWidth: 80, + semi: true, + tabWidth: 2, + trailingComma: "all", + importOrder: [ + "", + "^[./]", + ], + importOrderSeparation: true, + importOrderSortSpecifiers: true, + importOrderParserPlugins: ["decorators-legacy", "typescript"], +}; diff --git a/src/MutexAcceptor.ts b/src/MutexAcceptor.ts index e797325..a92e97e 100644 --- a/src/MutexAcceptor.ts +++ b/src/MutexAcceptor.ts @@ -1,236 +1,225 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; -import { Driver } from "tgrid/components/Driver"; +import { Driver, WebAcceptor } from "tgrid"; import { ProviderCapsule } from "./server/ProviderCapsule"; import { ProviderGroup } from "./server/ProviderGroup"; /** * Acceptor of the `mutex-server`. - * + * * - available only in NodeJS. - * - * The {@link MutexAcceptor} is a communicator class interacting with the remote client, through - * websocket and [RFC](https://github.com/samchon/tgrid#13-remote-function-call) protocol, in the + * + * The {@link MutexAcceptor} is a communicator class interacting with the remote client, through + * websocket and [RFC](https://github.com/samchon/tgrid#13-remote-function-call) protocol, in the * `mutex-server`. The {@link MutexAcceptor} objects are always created by the {@link MutexServer} * class, whenever a remote client connects to the `mutex-server`. - * + * * You can accept or reject the client's connection by calling {@link accept} or {@link reject} * method. If you {@link accept} the connection, the interaction would be started and the client * can access to critical component sections of this `mutex-server`. - * + * * Also, when declaring this {@link MutexAcceptor} type, you've to define two template arguments, * *Header* and *Provider*. The *Header* type repersents an initial data gotten from the remote - * client after the connection. I hope you and client not to omit it and utilize it as an - * activation tool to enhance security. - * - * The second template argument *Provider* represents the additional features provided for the - * remote client. If you don't have any plan to provide additional feature to the remote client, + * client after the connection. I hope you and client not to omit it and utilize it as an + * activation tool to enhance security. + * + * The second template argument *Provider* represents the additional features provided for the + * remote client. If you don't have any plan to provide additional feature to the remote client, * just declare it as `null` - * + * * @template Header Type of the header containing initial data. * @template Provider Type of additional features provided for the remote client. * @author Jeongho Nam - https://github.com/samchon */ -export class MutexAcceptor -{ - /** - * @hidden - */ - private base_: WebAcceptor>; - - /** - * @hidden - */ - private group_: ProviderGroup; - - /* ---------------------------------------------------------------- +export class MutexAcceptor { + /** + * @hidden + */ + private base_: WebAcceptor>; + + /** + * @hidden + */ + private group_: ProviderGroup; + + /* ---------------------------------------------------------------- CONSTRUCTORS ---------------------------------------------------------------- */ - /** - * @hidden - */ - private constructor(base: WebAcceptor>, group: ProviderGroup) - { - this.base_ = base; - this.group_ = group; - } - - /** - * @internal - */ - public static create - (base: WebAcceptor>, group: ProviderGroup) - { - return new MutexAcceptor(base, group); - } - - /** - * Close connection. - * - * Close connection with the remote client. - * - * When connection with the remote client has been closed, all of the locks the client had - * acquired and tried would be automatically unlocked and cancelled by this `mutex-server`. - * Also, remote critical section components had assigned to the client would be returned, too. - * - * In addition, the disconnection destories all [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s - * between the remote client. Therefore, if the remote client is providing custom features to - * your `mutex-server` and there're uncompleted method calls to the `Provider` through - * `Driver`, {@link RuntimeError} exceptions would be thrown. - * - * @param code Closing code. - * @param reason Reason why. - * @throw DomainError when the connection is not online. - */ - public close(code?: number, reason?: string): Promise - { - return this.base_.close(code, reason); - } - - /** - * Join connection. - * - * Wait until connection with the server to be closed. - */ - public join(): Promise; - - /** - * Join connection until timeout. - * - * Wait until connection with the server to be clsoed until timeout. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeeded to waiting in the given time. - */ - public join(ms: number): Promise; - - /** - * Join connection until time expiration. - * - * Wait until connection with the server to be closed until time expiration. - * - * @param at The maximum time point to wait. - * @return Whether succeeded to waiting in the given time. - */ - public join(at: Date): Promise; - - public join(param?: number | Date): Promise - { - return this.base_.join(param! as Date); - } - - /* ---------------------------------------------------------------- + /** + * @hidden + */ + private constructor( + base: WebAcceptor>, + group: ProviderGroup, + ) { + this.base_ = base; + this.group_ = group; + } + + /** + * @internal + */ + public static create( + base: WebAcceptor>, + group: ProviderGroup, + ) { + return new MutexAcceptor(base, group); + } + + /** + * Close connection. + * + * Close connection with the remote client. + * + * When connection with the remote client has been closed, all of the locks the client had + * acquired and tried would be automatically unlocked and cancelled by this `mutex-server`. + * Also, remote critical section components had assigned to the client would be returned, too. + * + * In addition, the disconnection destories all [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s + * between the remote client. Therefore, if the remote client is providing custom features to + * your `mutex-server` and there're uncompleted method calls to the `Provider` through + * `Driver`, {@link RuntimeError} exceptions would be thrown. + * + * @param code Closing code. + * @param reason Reason why. + * @throw DomainError when the connection is not online. + */ + public close(code?: number, reason?: string): Promise { + return this.base_.close(code, reason); + } + + /** + * Join connection. + * + * Wait until connection with the server to be closed. + */ + public join(): Promise; + + /** + * Join connection until timeout. + * + * Wait until connection with the server to be clsoed until timeout. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeeded to waiting in the given time. + */ + public join(ms: number): Promise; + + /** + * Join connection until time expiration. + * + * Wait until connection with the server to be closed until time expiration. + * + * @param at The maximum time point to wait. + * @return Whether succeeded to waiting in the given time. + */ + public join(at: Date): Promise; + + public join(param?: number | Date): Promise { + return this.base_.join(param! as Date); + } + + /* ---------------------------------------------------------------- ACCESSORS ---------------------------------------------------------------- */ - /** - * IP Address of client. - */ - public get ip(): string - { - return this.base_.ip; - } - - /** - * Path of client has connected. - */ - public get path(): string - { - return this.base_.path; - } - - /** - * Header containing initialization data like activation. - */ - public get header(): Header - { - return this.base_.header; - } - - /** - * Get state. - * - * Get current state of connection state with the remote client. List of values are such like - * below: - * - * - `REJECTING`: The {@link MutexAcceptor.reject} method is on running. - * - `NONE`: The {@link MutexAcceptor} instance is newly created, but did nothing yet. - * - `ACCEPTING`: The {@link MutexAcceptor.accept} method is on running. - * - `OPEN`: The connection is online. - * - `CLOSING`: The {@link MutexAcceptor.close} method is on running. - * - `CLOSED`: The connection is offline. - */ - public get state(): MutexAcceptor.State - { - return this.base_.state; - } - - /** - * Get Driver for [RFC](https://github.com/samchon/tgrid#13-remote-function-call). - * - * If remote client provides custom features for this `mutex-server`, you can utilize - * those features thorugh returned `Driver` object by this method. To use this method, you - * should define the `Controller` template argument, a type of interface who is defining - * additionla features provided from the remote client. - * - * The returned object `Driver` makes to call remote functions, defined in the `Controller` - * and provided by `Provider` in the remote client, possible. In other words, callling a - * function in the `Driver`, it means to call a matched function in the remote - * client's `Provider` object. - * - * - `Controller`: Definition only - * - `Driver`: [Remote Function Call](https://github.com/samchon/tgrid#13-remote-function-call) - * - * @template Controller An interface for provided features (functions & objects) from the remote client (`Provider`). - * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. - * @return A `Driver` for the [RFC](https://github.com/samchon/tgrid#13-remote-function-call). - */ - public getDriver(): Driver - { - return this.base_.getDriver(); - } - - /* ---------------------------------------------------------------- + /** + * IP Address of client. + */ + public get ip(): string { + return this.base_.ip; + } + + /** + * Path of client has connected. + */ + public get path(): string { + return this.base_.path; + } + + /** + * Header containing initialization data like activation. + */ + public get header(): Header { + return this.base_.header; + } + + /** + * Get state. + * + * Get current state of connection state with the remote client. List of values are such like + * below: + * + * - `REJECTING`: The {@link MutexAcceptor.reject} method is on running. + * - `NONE`: The {@link MutexAcceptor} instance is newly created, but did nothing yet. + * - `ACCEPTING`: The {@link MutexAcceptor.accept} method is on running. + * - `OPEN`: The connection is online. + * - `CLOSING`: The {@link MutexAcceptor.close} method is on running. + * - `CLOSED`: The connection is offline. + */ + public get state(): MutexAcceptor.State { + return this.base_.state; + } + + /** + * Get Driver for [RFC](https://github.com/samchon/tgrid#13-remote-function-call). + * + * If remote client provides custom features for this `mutex-server`, you can utilize + * those features thorugh returned `Driver` object by this method. To use this method, you + * should define the `Controller` template argument, a type of interface who is defining + * additionla features provided from the remote client. + * + * The returned object `Driver` makes to call remote functions, defined in the `Controller` + * and provided by `Provider` in the remote client, possible. In other words, callling a + * function in the `Driver`, it means to call a matched function in the remote + * client's `Provider` object. + * + * - `Controller`: Definition only + * - `Driver`: [Remote Function Call](https://github.com/samchon/tgrid#13-remote-function-call) + * + * @template Controller An interface for provided features (functions & objects) from the remote client (`Provider`). + * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. + * @return A `Driver` for the [RFC](https://github.com/samchon/tgrid#13-remote-function-call). + */ + public getDriver< + Controller extends object, + UseParametric extends boolean = false, + >(): Driver { + return this.base_.getDriver(); + } + + /* ---------------------------------------------------------------- HANDSHAKES ---------------------------------------------------------------- */ - /** - * Accept connection. - * - * Accepts, permits the client's connection to this `mutex-server` and starts interaction. - * - * @param provider An object providing additional features for the remote client. If you don't have plan to proivde any additional feature, assign `null`. - */ - public async accept(provider: Provider): Promise - { - await this.base_.accept({ group: this.group_, provider: provider }); - this.base_.join().then(() => this.group_.destructor()); - } - - /** - * Reject connection. - * - * Reject without acceptance, any interaction. The connection would be closed immediately. - * - * @param code Closing code. - * @param reason Reason why. - */ - public reject(status?: number, reason?: string): Promise - { - return this.base_.reject(status, reason); - } + /** + * Accept connection. + * + * Accepts, permits the client's connection to this `mutex-server` and starts interaction. + * + * @param provider An object providing additional features for the remote client. If you don't have plan to proivde any additional feature, assign `null`. + */ + public async accept(provider: Provider): Promise { + await this.base_.accept({ group: this.group_, provider: provider }); + this.base_.join().then(() => this.group_.destructor()); + } + + /** + * Reject connection. + * + * Reject without acceptance, any interaction. The connection would be closed immediately. + * + * @param code Closing code. + * @param reason Reason why. + */ + public reject(status?: number, reason?: string): Promise { + return this.base_.reject(status, reason); + } } /** - * + * */ -export namespace MutexAcceptor -{ - /** - * Current state of the {@link MutexAcceptor} - */ - export import State = WebAcceptor.State; -} \ No newline at end of file +export namespace MutexAcceptor { + /** + * Current state of the {@link MutexAcceptor} + */ + export import State = WebAcceptor.State; +} diff --git a/src/MutexConnector.ts b/src/MutexConnector.ts index 6992d41..a2e0b27 100644 --- a/src/MutexConnector.ts +++ b/src/MutexConnector.ts @@ -1,32 +1,25 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { WebConnector } from "tgrid/protocols/web/WebConnector"; -import { Driver } from "tgrid/components/Driver"; -import { Promisive } from "tgrid/typings/Promisive"; -import { ProviderCapsule } from "./server/ProviderCapsule"; +import { Driver, Promisive, WebConnector } from "tgrid"; import { RemoteBarrier } from "./client/RemoteBarrier"; import { RemoteConditionVariable } from "./client/RemoteConditionVariable"; import { RemoteLatch } from "./client/RemoteLatch"; import { RemoteMutex } from "./client/RemoteMutex"; import { RemoteSemaphore } from "./client/RemoteSemaphore"; +import { ProviderCapsule } from "./server/ProviderCapsule"; /** * Mutex server connector for client. - * + * * The {@link MutexConnector} is a communicator class who can connect to a remote `mutex-server` - * and interact with it through websocket and [RFC](https://github.com/samchon/tgrid#13-remote-function-call) + * and interact with it through websocket and [RFC](https://github.com/samchon/tgrid#13-remote-function-call) * protocol. - * - * You can connect to the websocket `mutex-server` using {@link connect} method. After the - * connection, get network level remote critical section components using one of below methods. + * + * You can connect to the websocket `mutex-server` using {@link connect} method. After the + * connection, get network level remote critical section components using one of below methods. * After utilizing those components, please do not forget to realising the component by calling - * {@link IRemoteComponent.destructor} or closing connection with the `mutex-server` by calling + * {@link IRemoteComponent.destructor} or closing connection with the `mutex-server` by calling * the {@link close} method to prevent waste of resources of the `mutex-server`. - * + * * - Solid Components * - {@link getConditionVariable} * - {@link getMutex} @@ -34,292 +27,291 @@ import { RemoteSemaphore } from "./client/RemoteSemaphore"; * - Adaptor Comopnents * - {@link getBarrier} * - {@link getLatch} - * + * * Also, when declaring this {@link MutexConnector} type, you've to define two template arguments, * *Header* and *Provider*. The *Header* type repersents an initial data sending to the remote - * `mutex-server` after connection. I hope you not to omit it and utilize it as an activation tool - * to enhance security. - * - * The second template argument *Provider* represents the custom features provided for the remote - * `mutex-server`. If you don't have any plan to provide any feature to the `mutex-server`, just + * `mutex-server` after connection. I hope you not to omit it and utilize it as an activation tool + * to enhance security. + * + * The second template argument *Provider* represents the custom features provided for the remote + * `mutex-server`. If you don't have any plan to provide any feature to the `mutex-server`, just * declare it as `null`. - * + * * @template Header Type of the *header* containing initial data. * @template Provider Type of additional features provided for the remote server. If no plan to provide anything, declare `null`. * @author Jeongho Nam - https://github.com/samchon */ -export class MutexConnector -{ - /** - * @hidden - */ - private base_: WebConnector; +export class MutexConnector { + /** + * @hidden + */ + private base_: WebConnector; - /** - * @hidden - */ - private controller_: Driver>; + /** + * @hidden + */ + private controller_: Driver>; - /* ----------------------------------------------------------- + /* ----------------------------------------------------------- CONSTRUCTORS ----------------------------------------------------------- */ - /** - * Initializer Constructor. - * - * @param header Initial data sending to the remote server after connection. Hope to use it as an activation tool. - * @param provider Additional feature provided for the remote server. If don't plan to provide anything to the remote server, assign `null`. - */ - public constructor(header: Header, provider: Provider) - { - this.base_ = new WebConnector(header, provider); - this.controller_ = this.base_.getDriver(); - } + /** + * Initializer Constructor. + * + * @param header Initial data sending to the remote server after connection. Hope to use it as an activation tool. + * @param provider Additional feature provided for the remote server. If don't plan to provide anything to the remote server, assign `null`. + */ + public constructor(header: Header, provider: Provider) { + this.base_ = new WebConnector(header, provider); + this.controller_ = this.base_.getDriver(); + } - /** - * Connect to a remote `mutex-server`. - * - * Try connection to the remote `mutex-server` with its *address* and waiting for the server - * to accept the trial. If the `mutex-server` accepts your connection, the function would be - * returned. Otherwise, the server rejects your connection, an exception would be thrown. - * - * After the connection and your business (using remote critical sections) has been - * completed, please don't forget closing the connection in time to prevent waste of the - * server resource. - * - * @param url URL address to connect. - * @throw DomainError when connection is not offline . - * @throw WebError when target server is not following adequate protocol. - * @throw WebError when server rejects your connection. - * @throw WebError when server does not accept your connection until timeout. - */ - public connect(url: string, timeout?: number): Promise - { - return this.base_.connect(url, { timeout }); - } + /** + * Connect to a remote `mutex-server`. + * + * Try connection to the remote `mutex-server` with its *address* and waiting for the server + * to accept the trial. If the `mutex-server` accepts your connection, the function would be + * returned. Otherwise, the server rejects your connection, an exception would be thrown. + * + * After the connection and your business (using remote critical sections) has been + * completed, please don't forget closing the connection in time to prevent waste of the + * server resource. + * + * @param url URL address to connect. + * @throw DomainError when connection is not offline . + * @throw WebError when target server is not following adequate protocol. + * @throw WebError when server rejects your connection. + * @throw WebError when server does not accept your connection until timeout. + */ + public connect(url: string, timeout?: number): Promise { + return this.base_.connect(url, { timeout }); + } - /** - * Close connection. - * - * Close connection from the remote `mutex-server`. - * - * When connection with the `mutex-server` has been closed, all of the locks you had acquired - * and tried would be automatically unlocked and cancelled by the server. Therefore, if you've - * tried to get locks through the remote critical section components by calling their methods, - * those methods would throw {@link RuntimeError} exceptions. - * - * Also, the disconnection destories all [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s - * between the remote server. Therefore, if you or server has an additional `Provider` and - * there're uncompleted method calls to the `Provideer` through the `Driver`, - * {@link RuntimeError} exceptions would be thrown. - * - * @throw DomainError when the connection is not online. - */ - public async close(): Promise - { - await this.base_.close(); - } + /** + * Close connection. + * + * Close connection from the remote `mutex-server`. + * + * When connection with the `mutex-server` has been closed, all of the locks you had acquired + * and tried would be automatically unlocked and cancelled by the server. Therefore, if you've + * tried to get locks through the remote critical section components by calling their methods, + * those methods would throw {@link RuntimeError} exceptions. + * + * Also, the disconnection destories all [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s + * between the remote server. Therefore, if you or server has an additional `Provider` and + * there're uncompleted method calls to the `Provideer` through the `Driver`, + * {@link RuntimeError} exceptions would be thrown. + * + * @throw DomainError when the connection is not online. + */ + public async close(): Promise { + await this.base_.close(); + } - /** - * Join connection. - * - * Wait until connection with the server to be closed. - */ - public join(): Promise; + /** + * Join connection. + * + * Wait until connection with the server to be closed. + */ + public join(): Promise; - /** - * Join connection until timeout. - * - * Wait until connection with the server to be clsoed until timeout. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeeded to waiting in the given time. - */ - public join(ms: number): Promise; + /** + * Join connection until timeout. + * + * Wait until connection with the server to be clsoed until timeout. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeeded to waiting in the given time. + */ + public join(ms: number): Promise; - /** - * Join connection until time expiration. - * - * Wait until connection with the server to be closed until time expiration. - * - * @param at The maximum time point to wait. - * @return Whether succeeded to waiting in the given time. - */ - public join(at: Date): Promise; + /** + * Join connection until time expiration. + * + * Wait until connection with the server to be closed until time expiration. + * + * @param at The maximum time point to wait. + * @return Whether succeeded to waiting in the given time. + */ + public join(at: Date): Promise; - public join(param?: number | Date): Promise - { - return this.base_.join(param! as Date); - } + public join(param?: number | Date): Promise { + return this.base_.join(param! as Date); + } - /* ----------------------------------------------------------- + /* ----------------------------------------------------------- ACCESSORS ----------------------------------------------------------- */ - /** - * Get connection url. - */ - public get url(): string | undefined - { - return this.base_.url; - } + /** + * Get connection url. + */ + public get url(): string | undefined { + return this.base_.url; + } - /** - * Get state. - * - * Get current state of connection state with the `mutex-server`. List of values are such like - * below: - * - * - `NONE`: The {@link MutexConnector} instance is newly created, but did nothing yet. - * - `CONNECTING`: The {@link MutexConnector.connect} method is on running. - * - `OPEN`: The connection is online. - * - `CLOSING`: The {@link MutexConnector.close} method is on running. - * - `CLOSED`: The connection is offline. - */ - public get state(): MutexConnector.State - { - return this.base_.state; - } + /** + * Get state. + * + * Get current state of connection state with the `mutex-server`. List of values are such like + * below: + * + * - `NONE`: The {@link MutexConnector} instance is newly created, but did nothing yet. + * - `CONNECTING`: The {@link MutexConnector.connect} method is on running. + * - `OPEN`: The connection is online. + * - `CLOSING`: The {@link MutexConnector.close} method is on running. + * - `CLOSED`: The connection is offline. + */ + public get state(): MutexConnector.State { + return this.base_.state; + } - /** - * Get header. - */ - public get header(): Header - { - return this.base_.header; - } + /** + * Get header. + */ + public get header(): Header { + return this.base_.header; + } - /** - * Get Driver for [RFC](https://github.com/samchon/tgrid#13-remote-function-call). - * - * If your target {@link MutexServer} provides additional features, you can utilize those - * features through returned `Driver` object by this method. To use this method, you should - * define the `Controller` template argument, a type of interface who is defining additional - * features provided from the remote server. - * - * The returned object `Driver` makes to call remote functions, defined in the `Controller` - * and provided by `Provider` in the remote server, possible. In other words, callling a - * function in the `Driver`, it means to call a matched function in the remote - * server's `Provider` object. - * - * - `Controller`: Definition only - * - `Driver`: [Remote Function Call](https://github.com/samchon/tgrid#13-remote-function-call) - * - * @template Controller An interface for provided features (functions & objects) from the remote system (`Provider`). - * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. - * @return A `Driver` for the [RFC](https://github.com/samchon/tgrid#13-remote-function-call). - */ - public getDriver(): Promisive - { - return this.controller_.provider as any; - } + /** + * Get Driver for [RFC](https://github.com/samchon/tgrid#13-remote-function-call). + * + * If your target {@link MutexServer} provides additional features, you can utilize those + * features through returned `Driver` object by this method. To use this method, you should + * define the `Controller` template argument, a type of interface who is defining additional + * features provided from the remote server. + * + * The returned object `Driver` makes to call remote functions, defined in the `Controller` + * and provided by `Provider` in the remote server, possible. In other words, callling a + * function in the `Driver`, it means to call a matched function in the remote + * server's `Provider` object. + * + * - `Controller`: Definition only + * - `Driver`: [Remote Function Call](https://github.com/samchon/tgrid#13-remote-function-call) + * + * @template Controller An interface for provided features (functions & objects) from the remote system (`Provider`). + * @template UseParametric Whether to convert type of function parameters to be compatible with their pritimive. + * @return A `Driver` for the [RFC](https://github.com/samchon/tgrid#13-remote-function-call). + */ + public getDriver< + Controller extends object, + UseParametric extends boolean = false, + >(): Promisive { + return this.controller_.provider as any; + } - /* ----------------------------------------------------------- + /* ----------------------------------------------------------- THREAD COMPONENTS ----------------------------------------------------------- */ - /** - * Get remote condition variable. - * - * Get remote condition variable from the `mutex-server`. - * - * If the `mutex-server` doesn't have the *key* named condition variable, the server will - * create a new condition variable instance. Otherwise, the server already has the *key* named - * condition variable, server will return it directly. - * - * @param key An identifier name to be created or search for. - * @return A {@link RemoteConditionVariable} object. - */ - public getConditionVariable(key: string): Promise - { - return RemoteConditionVariable.create(this.controller_.group.condition_variables, key); - } + /** + * Get remote condition variable. + * + * Get remote condition variable from the `mutex-server`. + * + * If the `mutex-server` doesn't have the *key* named condition variable, the server will + * create a new condition variable instance. Otherwise, the server already has the *key* named + * condition variable, server will return it directly. + * + * @param key An identifier name to be created or search for. + * @return A {@link RemoteConditionVariable} object. + */ + public getConditionVariable(key: string): Promise { + return RemoteConditionVariable.create( + this.controller_.group.condition_variables, + key, + ); + } - /** - * Get remote mutex. - * - * Get remote mutex from the `mutex-server`. - * - * If the `mutex-server` doesn't have the *key* named mutex, the server will create a new - * mutex instance. Otherwise, the server already has the *key* named mutex, server will return - * it directly. - * - * @param key An identifier name to be created or search for. - * @return A {@link RemoteMutex} object. - */ - public getMutex(key: string): Promise - { - return RemoteMutex.create(this.controller_.group.mutexes, key); - } + /** + * Get remote mutex. + * + * Get remote mutex from the `mutex-server`. + * + * If the `mutex-server` doesn't have the *key* named mutex, the server will create a new + * mutex instance. Otherwise, the server already has the *key* named mutex, server will return + * it directly. + * + * @param key An identifier name to be created or search for. + * @return A {@link RemoteMutex} object. + */ + public getMutex(key: string): Promise { + return RemoteMutex.create(this.controller_.group.mutexes, key); + } - /** - * Get remote semaphore. - * - * Get remote semaphore from the `mutex-server`. - * - * If the `mutex-server` doesn't have the *key* named semaphore, the server will create a new - * semaphore instance with your *count*. Otherwise, the server already has the *key* named - * semaphore, server will return it directly. - * - * Therefore, if the server already has the *key* named semaphore, its - * {@link RemoteSemaphore.max} can be different with your *count*. - * - * @param key An identifier name to be created or search for. - * @param count Downward counter of the target semaphore, if newly created. - * @return A {@link RemoteSemaphore} object. - */ - public getSemaphore(key: string, count: number): Promise - { - return RemoteSemaphore.create(this.controller_.group.semaphores, key, count); - } + /** + * Get remote semaphore. + * + * Get remote semaphore from the `mutex-server`. + * + * If the `mutex-server` doesn't have the *key* named semaphore, the server will create a new + * semaphore instance with your *count*. Otherwise, the server already has the *key* named + * semaphore, server will return it directly. + * + * Therefore, if the server already has the *key* named semaphore, its + * {@link RemoteSemaphore.max} can be different with your *count*. + * + * @param key An identifier name to be created or search for. + * @param count Downward counter of the target semaphore, if newly created. + * @return A {@link RemoteSemaphore} object. + */ + public getSemaphore(key: string, count: number): Promise { + return RemoteSemaphore.create( + this.controller_.group.semaphores, + key, + count, + ); + } - /** - * Get remote barrier. - * - * Get remote barrier from the `mutex-server`. - * - * If the `mutex-server` doesn't have the *key* named barrier, the server will create a new - * barrier instance with your *count*. Otherwise, the server already has the *key* named - * barrier, server will return it directly. - * - * Therefore, if the server already has the *key* named barrier, its downward counter can be - * different with your *count*. - * - * @param key An identifier name to be created or search for. - * @param count Downward counter of the target barrier, if newly created. - * @return A {@link RemoteBarrier} object. - */ - public getBarrier(key: string, count: number): Promise - { - return RemoteBarrier.create(this.controller_.group.barriers, key, count); - } + /** + * Get remote barrier. + * + * Get remote barrier from the `mutex-server`. + * + * If the `mutex-server` doesn't have the *key* named barrier, the server will create a new + * barrier instance with your *count*. Otherwise, the server already has the *key* named + * barrier, server will return it directly. + * + * Therefore, if the server already has the *key* named barrier, its downward counter can be + * different with your *count*. + * + * @param key An identifier name to be created or search for. + * @param count Downward counter of the target barrier, if newly created. + * @return A {@link RemoteBarrier} object. + */ + public getBarrier(key: string, count: number): Promise { + return RemoteBarrier.create(this.controller_.group.barriers, key, count); + } - /** - * Get remote latch. - * - * Get remote latch from the `mutex-server`. - * - * If the `mutex-server` doesn't have the *key* named latch, the server will create a new - * latch instance with your *count*. Otherwise, the server already has the *key* named latch, - * server will return it directly. - * - * Therefore, if the server already has the *key* named latch, its downward counter can be - * different with your *count*. - * - * @param identifier An identifier name to be created or search for. - * @param count Downward counter of the target latch, if newly created. - * @return A {@link RemoteLatch} object. - */ - public getLatch(identifier: string, count: number): Promise - { - return RemoteLatch.create(this.controller_.group.latches, identifier, count); - } + /** + * Get remote latch. + * + * Get remote latch from the `mutex-server`. + * + * If the `mutex-server` doesn't have the *key* named latch, the server will create a new + * latch instance with your *count*. Otherwise, the server already has the *key* named latch, + * server will return it directly. + * + * Therefore, if the server already has the *key* named latch, its downward counter can be + * different with your *count*. + * + * @param identifier An identifier name to be created or search for. + * @param count Downward counter of the target latch, if newly created. + * @return A {@link RemoteLatch} object. + */ + public getLatch(identifier: string, count: number): Promise { + return RemoteLatch.create( + this.controller_.group.latches, + identifier, + count, + ); + } } /** - * + * */ -export namespace MutexConnector -{ - /** - * Current state of the {@link MutexConnector} - */ - export import State = WebConnector.State; -} \ No newline at end of file +export namespace MutexConnector { + /** + * Current state of the {@link MutexConnector} + */ + export import State = WebConnector.State; +} diff --git a/src/MutexServer.ts b/src/MutexServer.ts index 4a03075..e8ef222 100644 --- a/src/MutexServer.ts +++ b/src/MutexServer.ts @@ -3,7 +3,7 @@ * @module msv */ //----------------------------------------------------------- -import { WebServer } from "tgrid/protocols/web/WebServer"; +import { WebServer } from "tgrid"; import { MutexAcceptor } from "./MutexAcceptor"; import { ProviderCapsule } from "./server/ProviderCapsule"; @@ -12,147 +12,146 @@ import { GlobalGroup } from "./server/GlobalGroup"; /** * The Mutex Server. - * + * * - available only in NodeJS. - * + * * The {@link MutexServer} is a class who can open an `mutex-server`. Clients connecting to the * `mutex-server` would communicate with this server through {@link MutexAcceptor} objects using * websocket and [RFC](https://github.com/samchon/tgrid#13-remote-function-call) protocol. - * - * To open the `mutex-server`, call the {@link open} method with your custom callback function + * + * To open the `mutex-server`, call the {@link open} method with your custom callback function * which would be called whenever a {@link MutexAcceptor} has been newly created by a client's * connection. - * + * * Also, when declaring this {@link MutexServer} type, you've to define two template arguments, * *Header* and *Provider*. The *Header* type repersents an initial data gotten from the remote - * client after the connection. I hope you and client not to omit it and utilize it as an - * activation tool to enhance security. - * - * The second template argument *Provider* represents the additional features provided for the - * remote client. If you don't have any plan to provide additional feature to the remote client, + * client after the connection. I hope you and client not to omit it and utilize it as an + * activation tool to enhance security. + * + * The second template argument *Provider* represents the additional features provided for the + * remote client. If you don't have any plan to provide additional feature to the remote client, * just declare it as `null`. - * + * * @template Header Type of the *header* containing initial data. * @template Provider Type of additional features provided for the remote client. * @author Jeongho Nam - https://github.com/samchon */ -export class MutexServer -{ - /** - * @hidden - */ - private server_: WebServer>; +export class MutexServer { + /** + * @hidden + */ + private server_: WebServer>; - /** - * @hidden - */ - private components_: GlobalGroup; + /** + * @hidden + */ + private components_: GlobalGroup; - /* ----------------------------------------------------------- + /* ----------------------------------------------------------- CONSTRUCTORS ----------------------------------------------------------- */ - /** - * Default Constructor. - * - * Create a web-socket server, without secured option (`ws://`). - */ - public constructor(); + /** + * Default Constructor. + * + * Create a web-socket server, without secured option (`ws://`). + */ + public constructor(); - /** - * Initializer Constructor. - * - * Create a secured web-socket server with *key* and *certification* data (`wss://`). - * - * @param key Key string. - * @param cert Certification string. - */ - public constructor(key: string, cert: string); + /** + * Initializer Constructor. + * + * Create a secured web-socket server with *key* and *certification* data (`wss://`). + * + * @param key Key string. + * @param cert Certification string. + */ + public constructor(key: string, cert: string); - public constructor(key?: string, cert?: string) - { - this.server_ = new WebServer(key!, cert!); - this.components_ = new GlobalGroup(); - } + public constructor(key?: string, cert?: string) { + this.server_ = new WebServer(key!, cert!); + this.components_ = new GlobalGroup(); + } - /** - * Open a `mutex-server`. - * - * Open a `mutex-server` through web-socket protocol, with its *port* number and *handler* - * function determining whether to accept the client's connection or not. After the - * `mutex-server` has been opened, clients can connect to that `mutex-server` by using the - * {@link MutexConnector} class. - * - * When implementing the *handler* function with the {@link MutexAcceptor} instance, calls the - * {@link MutexAcceptor.accept} method if you want to accept the new client's connection. - * Otherwise you dont't want to accept the client and reject its connection, just calls the - * {@link MutexAcceptor.reject} instead. - * - * Note that, this `mutex-server` handles the global critical sections on the entire network - * level. If you define the *predicator* function to accept every client's connection, some - * bad guy can spoil your own `mutex-server` by monopolying those critical sections. - * Therefore, don't implement the *handler* function to call only {@link MutexAcceptor.accept} - * method, but filter their connections by considering their {@link MutexAcceptor.header} - * data. - * - * @param port Port number to listen. - * @param handler A handler function determining whether to accept the client's connection or not. - */ - public open(port: number, handler: (acceptor: MutexAcceptor) => Promise): Promise - { - return this.server_.open(port, async base => - { - let group: ProviderGroup = new ProviderGroup(this.components_, base); - let acceptor: MutexAcceptor = MutexAcceptor.create(base, group); + /** + * Open a `mutex-server`. + * + * Open a `mutex-server` through web-socket protocol, with its *port* number and *handler* + * function determining whether to accept the client's connection or not. After the + * `mutex-server` has been opened, clients can connect to that `mutex-server` by using the + * {@link MutexConnector} class. + * + * When implementing the *handler* function with the {@link MutexAcceptor} instance, calls the + * {@link MutexAcceptor.accept} method if you want to accept the new client's connection. + * Otherwise you dont't want to accept the client and reject its connection, just calls the + * {@link MutexAcceptor.reject} instead. + * + * Note that, this `mutex-server` handles the global critical sections on the entire network + * level. If you define the *predicator* function to accept every client's connection, some + * bad guy can spoil your own `mutex-server` by monopolying those critical sections. + * Therefore, don't implement the *handler* function to call only {@link MutexAcceptor.accept} + * method, but filter their connections by considering their {@link MutexAcceptor.header} + * data. + * + * @param port Port number to listen. + * @param handler A handler function determining whether to accept the client's connection or not. + */ + public open( + port: number, + handler: (acceptor: MutexAcceptor) => Promise, + ): Promise { + return this.server_.open(port, async (base) => { + let group: ProviderGroup = new ProviderGroup(this.components_, base); + let acceptor: MutexAcceptor = MutexAcceptor.create( + base, + group, + ); - await handler(acceptor); - }); - } + await handler(acceptor); + }); + } - /** - * Close the `mutex-server`. - * - * Close the `mutex-server` and disconnect all the remote connections between its clients. - * Therefore, clients who are using the {@link MutexConnector} class, they can't use the - * remote critical section components more. - * - * Also, closing the server means that all of the [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s - * between the server and had connected clients would be destroied. Therefore, all of the [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s - * that are not returned (completed) yet would ge {@link RuntimeError} exception. - */ - public close(): Promise - { - return this.server_.close(); - } + /** + * Close the `mutex-server`. + * + * Close the `mutex-server` and disconnect all the remote connections between its clients. + * Therefore, clients who are using the {@link MutexConnector} class, they can't use the + * remote critical section components more. + * + * Also, closing the server means that all of the [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s + * between the server and had connected clients would be destroied. Therefore, all of the [RFC](https://github.com/samchon/tgrid#13-remote-function-call)s + * that are not returned (completed) yet would ge {@link RuntimeError} exception. + */ + public close(): Promise { + return this.server_.close(); + } - /* ----------------------------------------------------------- + /* ----------------------------------------------------------- ACCESSORS ----------------------------------------------------------- */ - /** - * Get server state. - * - * Get current state of the `mutex-server`. - * - * List of values are such like below: - * - * - `NONE`: The `{@link MutexServer} instance is newly created, but did nothing yet. - * - `OPENING`: The {@link MutexServer.open} method is on running. - * - `OPEN`: The `mutex-server` is online. - * - `CLOSING`: The {@link MutexServer.close} method is on running. - * - `CLOSED`: The `mutex-server` is offline. - */ - public get state(): MutexServer.State - { - return this.server_.state; - } + /** + * Get server state. + * + * Get current state of the `mutex-server`. + * + * List of values are such like below: + * + * - `NONE`: The `{@link MutexServer} instance is newly created, but did nothing yet. + * - `OPENING`: The {@link MutexServer.open} method is on running. + * - `OPEN`: The `mutex-server` is online. + * - `CLOSING`: The {@link MutexServer.close} method is on running. + * - `CLOSED`: The `mutex-server` is offline. + */ + public get state(): MutexServer.State { + return this.server_.state; + } } /** - * + * */ -export namespace MutexServer -{ - /** - * Current state of the {@link MutexServer} - */ - export import State = WebServer.State; -} \ No newline at end of file +export namespace MutexServer { + /** + * Current state of the {@link MutexServer} + */ + export import State = WebServer.State; +} diff --git a/src/client/RemoteBarrier.ts b/src/client/RemoteBarrier.ts index 9c5df6d..59ac17d 100644 --- a/src/client/RemoteBarrier.ts +++ b/src/client/RemoteBarrier.ts @@ -1,148 +1,133 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { Promisive } from "tgrid/typings/Promisive"; +import { Promisive } from "tgrid"; import { BarriersProvider } from "../server/providers/BarriersProvider"; /** * Remote Barrier. - * - * The `RemoteBarrier` class blocks critical sections until the downward counter to be zero. - * Unlike the {@link RemoteLatch} class whose downward counter is disposable, `RemoteBarrier` - * can re-use the downward counter repeatedly, resetting counter to be initial value whenever + * + * The `RemoteBarrier` class blocks critical sections until the downward counter to be zero. + * Unlike the {@link RemoteLatch} class whose downward counter is disposable, `RemoteBarrier` + * can re-use the downward counter repeatedly, resetting counter to be initial value whenever * reach to the zero. - * + * * @author Jeongho Nam - https://github.com/samchon */ -export class RemoteBarrier -{ - /** - * @hidden - */ - private controller_: Promisive; +export class RemoteBarrier { + /** + * @hidden + */ + private controller_: Promisive; - /** - * @hidden - */ - private name_: string; + /** + * @hidden + */ + private name_: string; - /* ----------------------------------------------------------- - CONSTRUCTORS - ----------------------------------------------------------- */ - /** - * @hidden - */ - private constructor(controller: Promisive, name: string) - { - this.controller_ = controller; - this.name_ = name; - } + /* ----------------------------------------------------------- + CONSTRUCTORS + ----------------------------------------------------------- */ + /** + * @hidden + */ + private constructor(controller: Promisive, name: string) { + this.controller_ = controller; + this.name_ = name; + } - /** - * @internal - */ - public static async create - ( - controller: Promisive, - name: string, - count: number - ): Promise - { - await controller.emplace(name, count); - return new RemoteBarrier(controller, name); - } + /** + * @internal + */ + public static async create( + controller: Promisive, + name: string, + count: number, + ): Promise { + await controller.emplace(name, count); + return new RemoteBarrier(controller, name); + } - /* ----------------------------------------------------------- + /* ----------------------------------------------------------- WAIT FUNCTIONS ----------------------------------------------------------- */ - /** - * Waits until the counter to be zero. - * - * Blocks the function calling until internal counter to be reached to the zero. - */ - public wait(): Promise - { - return this.controller_.wait(this.name_); - } + /** + * Waits until the counter to be zero. + * + * Blocks the function calling until internal counter to be reached to the zero. + */ + public wait(): Promise { + return this.controller_.wait(this.name_); + } - /** - * Tries to wait until the counter to be zero in timeout. - * - * Attempts to block the function calling until internal counter to be reached to the zero - * in timeout. If succeeded to waiting the counter to be reached to the zero, it returns - * `true`. Otherwise, the {@link RemoteBarrier} fails to reach to the zero in the given time, - * the function gives up the waiting and returns `false`. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeeded to waiting in the given time. - */ - public wait_for(ms: number): Promise - { - return this.controller_.wait_for(this.name_, ms); - } + /** + * Tries to wait until the counter to be zero in timeout. + * + * Attempts to block the function calling until internal counter to be reached to the zero + * in timeout. If succeeded to waiting the counter to be reached to the zero, it returns + * `true`. Otherwise, the {@link RemoteBarrier} fails to reach to the zero in the given time, + * the function gives up the waiting and returns `false`. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeeded to waiting in the given time. + */ + public wait_for(ms: number): Promise { + return this.controller_.wait_for(this.name_, ms); + } - /** - * Tries to wait until the counter to be zero in time expiration. - * - * Attempts to block the function calling until internal counter to be reached to the zero - * in time expiration. If succeeded to waiting the counter to be reached to the zero, it - * returns `true`. Otherwise, the {@link RemoteBarrier} fails to reach to the zero in the - * given time, the function gives up the waiting and returns `false`. - * - * @param at The maximum time point to wait. - * @return Whether succeeded to waiting in the given time. - */ - public async wait_until(at: Date): Promise - { - let ms: number = at.getTime() - Date.now(); - return await this.wait_for(ms); - } + /** + * Tries to wait until the counter to be zero in time expiration. + * + * Attempts to block the function calling until internal counter to be reached to the zero + * in time expiration. If succeeded to waiting the counter to be reached to the zero, it + * returns `true`. Otherwise, the {@link RemoteBarrier} fails to reach to the zero in the + * given time, the function gives up the waiting and returns `false`. + * + * @param at The maximum time point to wait. + * @return Whether succeeded to waiting in the given time. + */ + public async wait_until(at: Date): Promise { + let ms: number = at.getTime() - Date.now(); + return await this.wait_for(ms); + } - /* ----------------------------------------------------------- - ARRIVAL FUNCTIONS - ----------------------------------------------------------- */ - /** - * Derecements the counter. - * - * Decrements the counter by *n* without blocking. - * - * If the parametric value *n* is equal to or greater than internal counter, so that the - * internal counter be equal to or less than zero, everyone who are {@link wait waiting} for - * the {@link Latch} would continue their executions. - * - * @param n Value of the decrement. Default is 1. - */ - public arrive(n: number = 1): Promise - { - return this.controller_.arrive(this.name_, n); - } + /* ----------------------------------------------------------- + ARRIVAL FUNCTIONS + ----------------------------------------------------------- */ + /** + * Derecements the counter. + * + * Decrements the counter by *n* without blocking. + * + * If the parametric value *n* is equal to or greater than internal counter, so that the + * internal counter be equal to or less than zero, everyone who are {@link wait waiting} for + * the {@link Latch} would continue their executions. + * + * @param n Value of the decrement. Default is 1. + */ + public arrive(n: number = 1): Promise { + return this.controller_.arrive(this.name_, n); + } + + /** + * Decrements the counter and waits until the counter to be zero. + * + * Decrements the counter by one and blocks the section until internal counter to be zero. + * + * If the the remained counter be zero by this decrement, everyone who are + * {@link wait waiting} for the {@link RemoteBarrier} would continue their executions + * including this one. + */ + public arrive_and_drop(): Promise { + return this.controller_.arrive_and_drop(this.name_); + } - /** - * Decrements the counter and waits until the counter to be zero. - * - * Decrements the counter by one and blocks the section until internal counter to be zero. - * - * If the the remained counter be zero by this decrement, everyone who are - * {@link wait waiting} for the {@link RemoteBarrier} would continue their executions - * including this one. - */ - public arrive_and_drop(): Promise - { - return this.controller_.arrive_and_drop(this.name_); - } - - /** - * Decrements the counter and initial size at the same time. - * - * Decrements not only internal counter, but also initialize size of the counter at the same - * time. If the remained counter be zero by the decrement, everyone who are - * {@link wait waiting} for the {@link RemoteBarrier} would continue their executions. - */ - public arrive_and_wait(): Promise - { - return this.controller_.arrive_and_wait(this.name_); - } -} \ No newline at end of file + /** + * Decrements the counter and initial size at the same time. + * + * Decrements not only internal counter, but also initialize size of the counter at the same + * time. If the remained counter be zero by the decrement, everyone who are + * {@link wait waiting} for the {@link RemoteBarrier} would continue their executions. + */ + public arrive_and_wait(): Promise { + return this.controller_.arrive_and_wait(this.name_); + } +} diff --git a/src/client/RemoteConditionVariable.ts b/src/client/RemoteConditionVariable.ts index 799a16a..501262f 100644 --- a/src/client/RemoteConditionVariable.ts +++ b/src/client/RemoteConditionVariable.ts @@ -1,65 +1,59 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { Promisive } from "tgrid/typings/Promisive"; +import { Promisive } from "tgrid"; import { ConditionVariablesProvider } from "../server/providers/ConditionVariablesProvider"; /** * Remote ConditionVariable. - * + * * The `RemoteConditionVariable` class blocks critical sections until be notified. - * + * * @author Jeongho Nam - https://github.com/samchon */ -export class RemoteConditionVariable -{ - /** - * @hidden - */ - private controller_: Promisive; - - /** - * @hidden - */ - private name_: string; - - /* ----------------------------------------------------------- - CONSTRUCTORS - ----------------------------------------------------------- */ - /** - * @hidden - */ - private constructor(controller: Promisive, name: string) - { - this.controller_ = controller; - this.name_ = name; - } - - /** - * @internal - */ - public static async create - ( - controller: Promisive, - name: string - ): Promise - { - await controller.emplace(name, undefined); - return new RemoteConditionVariable(controller, name); - } - - /* ----------------------------------------------------------- +export class RemoteConditionVariable { + /** + * @hidden + */ + private controller_: Promisive; + + /** + * @hidden + */ + private name_: string; + + /* ----------------------------------------------------------- + CONSTRUCTORS + ----------------------------------------------------------- */ + /** + * @hidden + */ + private constructor( + controller: Promisive, + name: string, + ) { + this.controller_ = controller; + this.name_ = name; + } + + /** + * @internal + */ + public static async create( + controller: Promisive, + name: string, + ): Promise { + await controller.emplace(name, undefined); + return new RemoteConditionVariable(controller, name); + } + + /* ----------------------------------------------------------- WAIT FUNCTIONS ----------------------------------------------------------- */ - /** - * Wait until notified. - */ - public wait(): Promise; + /** + * Wait until notified. + */ + public wait(): Promise; - /** + /** * Wait until predicator returns true. * * This method is equivalent to: @@ -71,26 +65,25 @@ export class RemoteConditionVariable * * @param predicator A predicator function determines completion. */ - public wait(predicator: RemoteConditionVariable.Predicator): Promise; + public wait(predicator: RemoteConditionVariable.Predicator): Promise; - public async wait(predicator?: RemoteConditionVariable.Predicator): Promise - { - if (!predicator) - return await this._Wait(); - - while (!await predicator()) - await this._Wait(); - } + public async wait( + predicator?: RemoteConditionVariable.Predicator, + ): Promise { + if (!predicator) return await this._Wait(); - /** - * Wait for timeout or until notified. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether awaken by notification or timeout. - */ - public wait_for(ms: number): Promise; + while (!(await predicator())) await this._Wait(); + } - /** + /** + * Wait for timeout or until notified. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether awaken by notification or timeout. + */ + public wait_for(ms: number): Promise; + + /** * Wait until timeout or predicator returns true. * * This method is equivalent to: @@ -108,23 +101,28 @@ export class RemoteConditionVariable * @param predicator A predicator function determines the completion. * @return Returned value of the *predicator*. */ - public wait_for(ms: number, predicator: RemoteConditionVariable.Predicator): Promise; - - public async wait_for(ms: number, predicator?: RemoteConditionVariable.Predicator): Promise - { - let at: Date = new Date(Date.now() + ms); - return this.wait_until(at, predicator!); - } - - /** - * Wait until notified or time expiration. - * - * @param at The maximum time point to wait. - * @return Whether awaken by notification or time expiration. - */ - public wait_until(at: Date): Promise; - - /** + public wait_for( + ms: number, + predicator: RemoteConditionVariable.Predicator, + ): Promise; + + public async wait_for( + ms: number, + predicator?: RemoteConditionVariable.Predicator, + ): Promise { + let at: Date = new Date(Date.now() + ms); + return this.wait_until(at, predicator!); + } + + /** + * Wait until notified or time expiration. + * + * @param at The maximum time point to wait. + * @return Whether awaken by notification or time expiration. + */ + public wait_until(at: Date): Promise; + + /** * Wait until time expiration or predicator returns true. * * This method is equivalent to: @@ -141,64 +139,62 @@ export class RemoteConditionVariable * @param predicator A predicator function determines the completion. * @return Returned value of the *predicator*. */ - public wait_until(at: Date, predicator: RemoteConditionVariable.Predicator): Promise; - - public async wait_until(at: Date, predicator?: RemoteConditionVariable.Predicator): Promise - { - if (!predicator) - return await this._Wait_until(at); - - while (!await predicator()) - if (!await this._Wait_until(at)) - return await predicator(); - - return true; - } - - /** - * @hidden - */ - private _Wait(): Promise - { - return this.controller_.wait(this.name_); - } + public wait_until( + at: Date, + predicator: RemoteConditionVariable.Predicator, + ): Promise; - /** - * @hidden - */ - private async _Wait_until(at: Date): Promise - { - let ms: number = at.getTime() - Date.now(); - return await this.controller_.wait_for(this.name_, ms); - } + public async wait_until( + at: Date, + predicator?: RemoteConditionVariable.Predicator, + ): Promise { + if (!predicator) return await this._Wait_until(at); - /* ----------------------------------------------------------- - NOTIFIERS - ----------------------------------------------------------- */ - /** - * Notify, wake only one up. - */ - public notify_one(): Promise - { - return this.controller_.notify_one(this.name_); - } + while (!(await predicator())) + if (!(await this._Wait_until(at))) return await predicator(); - /** - * Notify, wake all up. - */ - public notify_all(): Promise - { - return this.controller_.notify_all(this.name_); - } + return true; + } + + /** + * @hidden + */ + private _Wait(): Promise { + return this.controller_.wait(this.name_); + } + + /** + * @hidden + */ + private async _Wait_until(at: Date): Promise { + let ms: number = at.getTime() - Date.now(); + return await this.controller_.wait_for(this.name_, ms); + } + + /* ----------------------------------------------------------- + NOTIFIERS + ----------------------------------------------------------- */ + /** + * Notify, wake only one up. + */ + public notify_one(): Promise { + return this.controller_.notify_one(this.name_); + } + + /** + * Notify, wake all up. + */ + public notify_all(): Promise { + return this.controller_.notify_all(this.name_); + } } /** - * + * */ -export namespace RemoteConditionVariable -{ - /** - * Type of predicator function who determines the completion. - */ - export type Predicator = () => boolean | Promise; -} \ No newline at end of file +export namespace RemoteConditionVariable { + /** + * Type of predicator function who determines the completion. + */ + export type Predicator = () => boolean | Promise; +} diff --git a/src/client/RemoteLatch.ts b/src/client/RemoteLatch.ts index 0073297..049e5b3 100644 --- a/src/client/RemoteLatch.ts +++ b/src/client/RemoteLatch.ts @@ -1,159 +1,144 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { Promisive } from "tgrid/typings/Promisive"; +import { Promisive } from "tgrid"; import { LatchesProvider } from "../server/providers/LatchesProvider"; /** * Remote Latch. - * - * The `RemoteLatch` class blocks critical sections until the downward counter to be zero. - * Howver, unlike {@link RemoteBarrier} who can reusable that downward counter be reset whenever + * + * The `RemoteLatch` class blocks critical sections until the downward counter to be zero. + * Howver, unlike {@link RemoteBarrier} who can reusable that downward counter be reset whenever * reach to the zero, downward of the `RemoteLatch` is not reusable but diposable. - * + * * @author Jeongho Nam - https://github.com/samchon */ -export class RemoteLatch -{ - /** - * @hidden - */ - private controller_: Promisive; +export class RemoteLatch { + /** + * @hidden + */ + private controller_: Promisive; + + /** + * @hidden + */ + private name_: string; - /** - * @hidden - */ - private name_: string; + /* ----------------------------------------------------------- + CONSTRUCTORS + ----------------------------------------------------------- */ + /** + * @hidden + */ + private constructor(controller: Promisive, name: string) { + this.controller_ = controller; + this.name_ = name; + } - /* ----------------------------------------------------------- - CONSTRUCTORS - ----------------------------------------------------------- */ - /** - * @hidden - */ - private constructor(controller: Promisive, name: string) - { - this.controller_ = controller; - this.name_ = name; - } + /** + * @internal + */ + public static async create( + controller: Promisive, + name: string, + count: number, + ): Promise { + await controller.emplace(name, count); + return new RemoteLatch(controller, name); + } - /** - * @internal - */ - public static async create - ( - controller: Promisive, - name: string, - count: number - ): Promise - { - await controller.emplace(name, count); - return new RemoteLatch(controller, name); - } + /* ----------------------------------------------------------- + WAIT FUNCTIONS + ----------------------------------------------------------- */ + /** + * Waits until the counter to be zero. + * + * Blocks the function calling until internal counter to be reached to the zero. + * + * If the {@link RemoteLatch} already has been reached to the zero, it would be returned + * immediately. + */ + public wait(): Promise { + return this.controller_.wait(this.name_); + } - /* ----------------------------------------------------------- - WAIT FUNCTIONS - ----------------------------------------------------------- */ - /** - * Waits until the counter to be zero. - * - * Blocks the function calling until internal counter to be reached to the zero. - * - * If the {@link RemoteLatch} already has been reached to the zero, it would be returned - * immediately. - */ - public wait(): Promise - { - return this.controller_.wait(this.name_); - } + /** + * Test whether the counter has been reached to the zero. + * + * The {@link try_wait} function tests whether the internal counter has been reached to the + * zero. + * + * @return Whether reached to zero or not. + */ + public try_wait(): Promise { + return this.controller_.try_wait(this.name_); + } - /** - * Test whether the counter has been reached to the zero. - * - * The {@link try_wait} function tests whether the internal counter has been reached to the - * zero. - * - * @return Whether reached to zero or not. - */ - public try_wait(): Promise - { - return this.controller_.try_wait(this.name_); - } + /** + * Tries to wait until the counter to be zero in timeout. + * + * Attempts to block the function calling until internal counter to be reached to the zero + * in timeout. If succeeded to waiting the counter to be reached to the zero, it returns + * `true`. Otherwise, the {@link RemoteLatch} fails to reach to the zero in the given time, + * the function gives up the waiting and returns `false`. + * + * If the {@link RemoteLatch} already has been reached to the zero, it would return `true` + * directly. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeeded to waiting in the given time. + */ + public wait_for(ms: number): Promise { + return this.controller_.wait_for(this.name_, ms); + } - /** - * Tries to wait until the counter to be zero in timeout. - * - * Attempts to block the function calling until internal counter to be reached to the zero - * in timeout. If succeeded to waiting the counter to be reached to the zero, it returns - * `true`. Otherwise, the {@link RemoteLatch} fails to reach to the zero in the given time, - * the function gives up the waiting and returns `false`. - * - * If the {@link RemoteLatch} already has been reached to the zero, it would return `true` - * directly. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeeded to waiting in the given time. - */ - public wait_for(ms: number): Promise - { - return this.controller_.wait_for(this.name_, ms); - } + /** + * Tries to wait until the counter to be zero in time expiration. + * + * Attempts to block the function calling until internal counter to be reached to the zero + * in time expiration. If succeeded to waiting the counter to be reached to the zero, it + * returns `true`. Otherwise, the {@link RemoteLatch} fails to reach to the zero in the + * given time, the function gives up the waiting and returns `false`. + * + * If the {@link RemoteLatch} already has been reached to the zero, it would return `true` + * directly. + * + * @param at The maximum time point to wait. + * @return Whether succeeded to waiting in the given time. + */ + public async wait_until(at: Date): Promise { + let ms: number = at.getTime() - Date.now(); + return await this.wait_for(ms); + } - /** - * Tries to wait until the counter to be zero in time expiration. - * - * Attempts to block the function calling until internal counter to be reached to the zero - * in time expiration. If succeeded to waiting the counter to be reached to the zero, it - * returns `true`. Otherwise, the {@link RemoteLatch} fails to reach to the zero in the - * given time, the function gives up the waiting and returns `false`. - * - * If the {@link RemoteLatch} already has been reached to the zero, it would return `true` - * directly. - * - * @param at The maximum time point to wait. - * @return Whether succeeded to waiting in the given time. - */ - public async wait_until(at: Date): Promise - { - let ms: number = at.getTime() - Date.now(); - return await this.wait_for(ms); - } + /* ----------------------------------------------------------- + ARRIVALS + ----------------------------------------------------------- */ + /** + * Derecements the counter. + * + * Decrements the counter by *n* without blocking. + * + * If the parametric value *n* is equal to or greater than internal counter, so that the + * internal counter be equal to or less than zero, everyone who are {@link wait waiting} for + * the {@link RemoteLatch} would continue their execution. + * + * @param n Value of the decrement. Default is 1. + */ + public count_down(n: number = 1): Promise { + return this.controller_.count_down(this.name_, n); + } - /* ----------------------------------------------------------- - ARRIVALS - ----------------------------------------------------------- */ - /** - * Derecements the counter. - * - * Decrements the counter by *n* without blocking. - * - * If the parametric value *n* is equal to or greater than internal counter, so that the - * internal counter be equal to or less than zero, everyone who are {@link wait waiting} for - * the {@link RemoteLatch} would continue their execution. - * - * @param n Value of the decrement. Default is 1. - */ - public count_down(n: number = 1): Promise - { - return this.controller_.count_down(this.name_, n); - } - - /** - * Decrements the counter and waits until the counter to be zero. - * - * Decrements the counter by *n* and blocks the section until internal counter to be zero. - * - * If the parametric value *n* is equal to or greater than internal counter, so that the - * internal counter be equal to or less than zero, everyone who are {@link wait waiting} for - * the {@link RemoteLatch} would continue their execution including this one. - * - * @param n Value of the decrement. Default is 1. - */ - public arrive_and_wait(): Promise - { - return this.controller_.arrive_and_wait(this.name_); - } -} \ No newline at end of file + /** + * Decrements the counter and waits until the counter to be zero. + * + * Decrements the counter by *n* and blocks the section until internal counter to be zero. + * + * If the parametric value *n* is equal to or greater than internal counter, so that the + * internal counter be equal to or less than zero, everyone who are {@link wait waiting} for + * the {@link RemoteLatch} would continue their execution including this one. + * + * @param n Value of the decrement. Default is 1. + */ + public arrive_and_wait(): Promise { + return this.controller_.arrive_and_wait(this.name_); + } +} diff --git a/src/client/RemoteMutex.ts b/src/client/RemoteMutex.ts index 5e67b27..889d816 100644 --- a/src/client/RemoteMutex.ts +++ b/src/client/RemoteMutex.ts @@ -1,285 +1,266 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { Promisive } from "tgrid/typings/Promisive"; +import { Promisive } from "tgrid"; import { MutexesProvider } from "../server/providers/MutexesProvider"; /** * Remote Mutex. - * + * * @author Jeongho Nam - https://github.com/samchon */ -export class RemoteMutex -{ - /** - * @hidden - */ - private controller_: Promisive; +export class RemoteMutex { + /** + * @hidden + */ + private controller_: Promisive; + + /** + * @hidden + */ + private name_: string; + + /* ----------------------------------------------------------- + CONSTRUCTORS + ----------------------------------------------------------- */ + /** + * @hidden + */ + private constructor(controller: Promisive, name: string) { + this.controller_ = controller; + this.name_ = name; + } - /** - * @hidden - */ - private name_: string; + /** + * @internal + */ + public static async create( + controller: Promisive, + name: string, + ): Promise { + await controller.emplace(name, undefined); + return new RemoteMutex(controller, name); + } - /* ----------------------------------------------------------- - CONSTRUCTORS - ----------------------------------------------------------- */ - /** - * @hidden - */ - private constructor(controller: Promisive, name: string) - { - this.controller_ = controller; - this.name_ = name; - } + /* ----------------------------------------------------------- + WRITE LOCK + ----------------------------------------------------------- */ + /** + * Write locks the mutex. + * + * Monopolies a mutex until be {@link unlock unlocked}. If there're someone who have already + * {@link lock monopolied} or {@link lock_shared shared} the mutex, the function call would + * be blocked until all of them to return their acquistions by calling {@link unlock} or + * {@link unlock_shared} methods. + * + * In same reason, if you don't call the {@link unlock} function after your business, the + * others who want to {@link lock monopoly} or {@link lock_shared share} the mutex would be + * fall into the forever sleep. Therefore, never forget to calling the {@link unlock} function + * or utilize the {@link UniqueLock.lock} function instead to ensure the safety. + */ + public lock(): Promise { + return this.controller_.lock(this.name_); + } - /** - * @internal - */ - public static async create - ( - controller: Promisive, - name: string - ): Promise - { - await controller.emplace(name, undefined); - return new RemoteMutex(controller, name); - } + /** + * Tries to write lock the mutex. + * + * Attempts to monopoly a mutex without blocking. If succeeded to monopoly the mutex + * immediately, it returns `true` directly. Otherwise there's someone who has already + * {@link lock monopolied} or {@link lock_shared shared} the mutex, the function gives up the + * trial immediately and returns `false` directly. + * + * Note that, if you succeeded to monopoly the mutex (returns `true`) but do not call the + * {@link unlock} function after your business, the others who want to {@link lock monopoly} + * or {@link lock_shared share} the mutex would be fall into the forever sleep. Therefore, + * never forget to calling the {@link unlock} function or utilize the + * {@link UniqueLock.try_lock} function instead to ensure the safety. + * + * @return Whether succeeded to monopoly the mutex or not. + */ + public try_lock(): Promise { + return this.controller_.try_lock(this.name_); + } - /* ----------------------------------------------------------- - WRITE LOCK - ----------------------------------------------------------- */ - /** - * Write locks the mutex. - * - * Monopolies a mutex until be {@link unlock unlocked}. If there're someone who have already - * {@link lock monopolied} or {@link lock_shared shared} the mutex, the function call would - * be blocked until all of them to return their acquistions by calling {@link unlock} or - * {@link unlock_shared} methods. - * - * In same reason, if you don't call the {@link unlock} function after your business, the - * others who want to {@link lock monopoly} or {@link lock_shared share} the mutex would be - * fall into the forever sleep. Therefore, never forget to calling the {@link unlock} function - * or utilize the {@link UniqueLock.lock} function instead to ensure the safety. - */ - public lock(): Promise - { - return this.controller_.lock(this.name_); - } - - /** - * Tries to write lock the mutex. - * - * Attempts to monopoly a mutex without blocking. If succeeded to monopoly the mutex - * immediately, it returns `true` directly. Otherwise there's someone who has already - * {@link lock monopolied} or {@link lock_shared shared} the mutex, the function gives up the - * trial immediately and returns `false` directly. - * - * Note that, if you succeeded to monopoly the mutex (returns `true`) but do not call the - * {@link unlock} function after your business, the others who want to {@link lock monopoly} - * or {@link lock_shared share} the mutex would be fall into the forever sleep. Therefore, - * never forget to calling the {@link unlock} function or utilize the - * {@link UniqueLock.try_lock} function instead to ensure the safety. - * - * @return Whether succeeded to monopoly the mutex or not. - */ - public try_lock(): Promise - { - return this.controller_.try_lock(this.name_); - } + /** + * Tries to write lock the mutex until timeout. + * + * Attempts to monopoly a mutex until timeout. If succeeded to monopoly the mutex until the + * timeout, it returns `true`. Otherwise failed to acquiring the lock in the given time, the + * function gives up the trial and returns `false`. + * + * Failed to acquiring the lock in the given time (returns `false`), it means that there's + * someone who has already {@link lock monopolied} or {@link lock_shared shared} the mutex and + * does not return it over the timeout. + * + * Note that, if you succeeded to monopoly the mutex (returns `true`) but do not call the + * {@link unlock} function after your business, the others who want to {@link lock monopoly} + * or {@link lock_shared share} the mutex would be fall into the forever sleep. Therefore, + * never forget to calling the {@link unlock} function or utilize the + * {@link UniqueLock.try_lock_for} function instead to ensure the safety. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeeded to monopoly the mutex or not. + */ + public try_lock_for(ms: number): Promise { + return this.controller_.try_lock_for(this.name_, ms); + } - /** - * Tries to write lock the mutex until timeout. - * - * Attempts to monopoly a mutex until timeout. If succeeded to monopoly the mutex until the - * timeout, it returns `true`. Otherwise failed to acquiring the lock in the given time, the - * function gives up the trial and returns `false`. - * - * Failed to acquiring the lock in the given time (returns `false`), it means that there's - * someone who has already {@link lock monopolied} or {@link lock_shared shared} the mutex and - * does not return it over the timeout. - * - * Note that, if you succeeded to monopoly the mutex (returns `true`) but do not call the - * {@link unlock} function after your business, the others who want to {@link lock monopoly} - * or {@link lock_shared share} the mutex would be fall into the forever sleep. Therefore, - * never forget to calling the {@link unlock} function or utilize the - * {@link UniqueLock.try_lock_for} function instead to ensure the safety. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeeded to monopoly the mutex or not. - */ - public try_lock_for(ms: number): Promise - { - return this.controller_.try_lock_for(this.name_, ms); - } + /** + * Tries to write lock the mutex until time expiration. + * + * Attemps to monopoly a mutex until time expiration. If succeeded to monopoly the mutex + * until the time expiration, it returns `true`. Otherwise failed to acquiring the lock in the + * given time, the function gives up the trial and returns `false`. + * + * Failed to acquiring the lock in the given time (returns `false`), it means that there's + * someone who has already {@link lock monopolied} or {@link lock_shared shared} the mutex and + * does not return it over the time expiration. + * + * Note that, if you succeeded to monopoly the mutex (returns `true`) but do not call the + * {@link unlock} function after your business, the others who want to {@link lock monopoly} + * or {@link lock_shared share} the mutex would be fall into the forever sleep. Therefore, + * never forget to calling the {@link unlock} function or utilize the + * {@link UniqueLock.try_lock_until} function instead to ensure the safety. + * + * @param at The maximum time point to wait. + * @return Whether succeeded to monopoly the mutex or not. + */ + public async try_lock_until(at: Date): Promise { + let ms: number = at.getTime() - Date.now(); + return await this.try_lock_for(ms); + } - /** - * Tries to write lock the mutex until time expiration. - * - * Attemps to monopoly a mutex until time expiration. If succeeded to monopoly the mutex - * until the time expiration, it returns `true`. Otherwise failed to acquiring the lock in the - * given time, the function gives up the trial and returns `false`. - * - * Failed to acquiring the lock in the given time (returns `false`), it means that there's - * someone who has already {@link lock monopolied} or {@link lock_shared shared} the mutex and - * does not return it over the time expiration. - * - * Note that, if you succeeded to monopoly the mutex (returns `true`) but do not call the - * {@link unlock} function after your business, the others who want to {@link lock monopoly} - * or {@link lock_shared share} the mutex would be fall into the forever sleep. Therefore, - * never forget to calling the {@link unlock} function or utilize the - * {@link UniqueLock.try_lock_until} function instead to ensure the safety. - * - * @param at The maximum time point to wait. - * @return Whether succeeded to monopoly the mutex or not. - */ - public async try_lock_until(at: Date): Promise - { - let ms: number = at.getTime() - Date.now(); - return await this.try_lock_for(ms); - } + /** + * Write unlocks the mutex. + * + * When you call this {@link unlock} method and there're someone who are currently blocked by + * attempting to {@link lock write} or {@link lock_shared read} lock this mutex, one of them + * (FIFO; first-in-first-out) would acquire the lock and continues its execution. + * + * Otherwise, there's not anyone who is acquiring the {@link lock write lock} of this mutex, + * the {@link DomainError} exception would be thrown. + * + * > As you know, when you succeeded to acquire the `write lock`, you don't have to forget to + * > calling this {@link unlock} method after your business. If you forget it, it would be a + * > terrible situation for the others who're attempting to lock this mutex. + * > + * > However, if you utilize the {@link UniqueLock}, you don't need to consider about this + * > {@link unlock} method. Just define your business into a callback function as a parameter + * > of methods of the {@link UniqueLock}, then this {@link unlock} method would be + * > automatically called by the {@link UniqueLock} after the business. + * + * @throw {@link DomainError} when no one is acquiring the {@link lock write lock}. + */ + public unlock(): Promise { + return this.controller_.unlock(this.name_); + } - /** - * Write unlocks the mutex. - * - * When you call this {@link unlock} method and there're someone who are currently blocked by - * attempting to {@link lock write} or {@link lock_shared read} lock this mutex, one of them - * (FIFO; first-in-first-out) would acquire the lock and continues its execution. - * - * Otherwise, there's not anyone who is acquiring the {@link lock write lock} of this mutex, - * the {@link DomainError} exception would be thrown. - * - * > As you know, when you succeeded to acquire the `write lock`, you don't have to forget to - * > calling this {@link unlock} method after your business. If you forget it, it would be a - * > terrible situation for the others who're attempting to lock this mutex. - * > - * > However, if you utilize the {@link UniqueLock}, you don't need to consider about this - * > {@link unlock} method. Just define your business into a callback function as a parameter - * > of methods of the {@link UniqueLock}, then this {@link unlock} method would be - * > automatically called by the {@link UniqueLock} after the business. - * - * @throw {@link DomainError} when no one is acquiring the {@link lock write lock}. - */ - public unlock(): Promise - { - return this.controller_.unlock(this.name_); - } + /* ----------------------------------------------------------- + READ LOCK + ----------------------------------------------------------- */ + /** + * Read locks the mutex. + * + * Shares a mutex until be {@link unlock_shared unlocked}. If there're someone who have + * already {@link lock monopolied} the mutex, the function call would be blocked until all of + * them to {@link unlock return} their acquisitions. + * + * In same reason, if you don't call the {@link unlock_shared} function after your business, + * the others who want to {@link lock monopoly} the mutex would be fall into the forever + * sleep. Therefore, never forget to calling the {@link unlock_shared} or utilize the + * {@link SharedLock.lock} function instead to ensure the safety. + */ + public lock_shared(): Promise { + return this.controller_.lock_shared(this.name_); + } - /* ----------------------------------------------------------- - READ LOCK - ----------------------------------------------------------- */ - /** - * Read locks the mutex. - * - * Shares a mutex until be {@link unlock_shared unlocked}. If there're someone who have - * already {@link lock monopolied} the mutex, the function call would be blocked until all of - * them to {@link unlock return} their acquisitions. - * - * In same reason, if you don't call the {@link unlock_shared} function after your business, - * the others who want to {@link lock monopoly} the mutex would be fall into the forever - * sleep. Therefore, never forget to calling the {@link unlock_shared} or utilize the - * {@link SharedLock.lock} function instead to ensure the safety. - */ - public lock_shared(): Promise - { - return this.controller_.lock_shared(this.name_); - } - - /** - * Tries to read lock the mutex. - * - * Attemps to share a mutex without blocking. If succeeded to share the mutex immediately, it - * returns `true` directly. Otherwise there's someone who has already {@link lock monopolied} - * the mutex, the function gives up the trial immediately and returns `false` directly. - * - * Note that, if you succeeded to share the mutex (returns `true`) but do not call the - * {@link unlock_shared} function after your buinsess, the others who want to - * {@link lock monopoly} the mutex would be fall into the forever sleep. Therefore, never - * forget to calling the {@link unlock_shared} function or utilize the - * {@link SharedLock.try_lock} function instead to ensure the safety. - * - * @return Whether succeeded to share the mutex or not. - */ - public try_lock_shared(): Promise - { - return this.controller_.try_lock_shared(this.name_); - } + /** + * Tries to read lock the mutex. + * + * Attemps to share a mutex without blocking. If succeeded to share the mutex immediately, it + * returns `true` directly. Otherwise there's someone who has already {@link lock monopolied} + * the mutex, the function gives up the trial immediately and returns `false` directly. + * + * Note that, if you succeeded to share the mutex (returns `true`) but do not call the + * {@link unlock_shared} function after your buinsess, the others who want to + * {@link lock monopoly} the mutex would be fall into the forever sleep. Therefore, never + * forget to calling the {@link unlock_shared} function or utilize the + * {@link SharedLock.try_lock} function instead to ensure the safety. + * + * @return Whether succeeded to share the mutex or not. + */ + public try_lock_shared(): Promise { + return this.controller_.try_lock_shared(this.name_); + } - /** - * Tries to read lock the mutex until timeout. - * - * Attemps to share a mutex until timeout. If succeeded to share the mutex until timeout, it - * returns `true`. Otherwise failed to acquiring the shared lock in the given time, the - * function gives up the trial and returns `false`. - * - * Failed to acquring the shared lock in the given time (returns `false`), it means that - * there's someone who has already {@link lock monopolied} the mutex and does not return it - * over the timeout. - * - * Note that, if you succeeded to share the mutex (returns `true`) but do not call the - * {@link unlock_shared} function after your buinsess, the others who want to - * {@link lock monopoly} the mutex would be fall into the forever sleep. Therefore, never - * forget to calling the {@link unlock_shared} function or utilize the - * {@link SharedLock.try_lock_for} function instead to ensure the safety. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeeded to share the mutex or not. - */ - public try_lock_shared_for(ms: number): Promise - { - return this.controller_.try_lock_shared_for(this.name_, ms); - } + /** + * Tries to read lock the mutex until timeout. + * + * Attemps to share a mutex until timeout. If succeeded to share the mutex until timeout, it + * returns `true`. Otherwise failed to acquiring the shared lock in the given time, the + * function gives up the trial and returns `false`. + * + * Failed to acquring the shared lock in the given time (returns `false`), it means that + * there's someone who has already {@link lock monopolied} the mutex and does not return it + * over the timeout. + * + * Note that, if you succeeded to share the mutex (returns `true`) but do not call the + * {@link unlock_shared} function after your buinsess, the others who want to + * {@link lock monopoly} the mutex would be fall into the forever sleep. Therefore, never + * forget to calling the {@link unlock_shared} function or utilize the + * {@link SharedLock.try_lock_for} function instead to ensure the safety. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeeded to share the mutex or not. + */ + public try_lock_shared_for(ms: number): Promise { + return this.controller_.try_lock_shared_for(this.name_, ms); + } - /** - * Tries to read lock the mutex until time expiration. - * - * Attemps to share a mutex until time expiration. If succeeded to share the mutex until time - * expiration, it returns `true`. Otherwise failed to acquiring the shared lock in the given - * time, the function gives up the trial and returns `false`. - * - * Failed to acquring the shared lock in the given time (returns `false`), it means that - * there's someone who has already {@link lock monopolied} the mutex and does not return it - * over the time expiration. - * - * Note that, if you succeeded to share the mutex (returns `true`) but do not call the - * {@link unlock_shared} function after your buinsess, the others who want to - * {@link lock monopoly} the mutex would be fall into the forever sleep. Therefore, never - * forget to calling the {@link unlock_shared} function or utilize the - * {@link SharedLock.try_lock_until} function instead to ensure the safety. - * - * @param at The maximum time point to wait. - * @return Whether succeeded to share the mutex or not. - */ - public async try_lock_shared_until(at: Date): Promise - { - let ms: number = at.getTime() - Date.now(); - return await this.try_lock_shared_for(ms); - } + /** + * Tries to read lock the mutex until time expiration. + * + * Attemps to share a mutex until time expiration. If succeeded to share the mutex until time + * expiration, it returns `true`. Otherwise failed to acquiring the shared lock in the given + * time, the function gives up the trial and returns `false`. + * + * Failed to acquring the shared lock in the given time (returns `false`), it means that + * there's someone who has already {@link lock monopolied} the mutex and does not return it + * over the time expiration. + * + * Note that, if you succeeded to share the mutex (returns `true`) but do not call the + * {@link unlock_shared} function after your buinsess, the others who want to + * {@link lock monopoly} the mutex would be fall into the forever sleep. Therefore, never + * forget to calling the {@link unlock_shared} function or utilize the + * {@link SharedLock.try_lock_until} function instead to ensure the safety. + * + * @param at The maximum time point to wait. + * @return Whether succeeded to share the mutex or not. + */ + public async try_lock_shared_until(at: Date): Promise { + let ms: number = at.getTime() - Date.now(); + return await this.try_lock_shared_for(ms); + } - /** - * Read unlocks the mutex. - * - * When you call this {@link unlock_shared} method and there're someone who are currently - * blocked by attempting to {@link lock monopoly} this mutex, one of them - * (FIFO; first-in-first-out) would acquire the lock and continues its execution. - * - * Otherwise, there's not anyone who is acquiring the {@link lock_shared read lock} of this - * mutex, the {@link DomainError} exception would be thrown. - * - * > As you know, when you succeeded to acquire the `read lock`, you don't have to forget to - * > calling this {@link unlock_shared} method after your business. If you forget it, it would - * > be a terrible situation for the others who're attempting to lock this mutex. - * > - * > However, if you utilize the {@link SharedLock}, you don't need to consider about this - * > {@link unlock_shared} method. Just define your business into a callback function as a - * > parameter of methods of the {@link SharedLock}, then this {@link unlock_shared} method - * > would be automatically called by the {@link SharedLock} after the business. - */ - public unlock_shared(): Promise - { - return this.controller_.unlock_shared(this.name_); - } -} \ No newline at end of file + /** + * Read unlocks the mutex. + * + * When you call this {@link unlock_shared} method and there're someone who are currently + * blocked by attempting to {@link lock monopoly} this mutex, one of them + * (FIFO; first-in-first-out) would acquire the lock and continues its execution. + * + * Otherwise, there's not anyone who is acquiring the {@link lock_shared read lock} of this + * mutex, the {@link DomainError} exception would be thrown. + * + * > As you know, when you succeeded to acquire the `read lock`, you don't have to forget to + * > calling this {@link unlock_shared} method after your business. If you forget it, it would + * > be a terrible situation for the others who're attempting to lock this mutex. + * > + * > However, if you utilize the {@link SharedLock}, you don't need to consider about this + * > {@link unlock_shared} method. Just define your business into a callback function as a + * > parameter of methods of the {@link SharedLock}, then this {@link unlock_shared} method + * > would be automatically called by the {@link SharedLock} after the business. + */ + public unlock_shared(): Promise { + return this.controller_.unlock_shared(this.name_); + } +} diff --git a/src/client/RemoteSemaphore.ts b/src/client/RemoteSemaphore.ts index 18f5512..4e77678 100644 --- a/src/client/RemoteSemaphore.ts +++ b/src/client/RemoteSemaphore.ts @@ -1,209 +1,197 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { Promisive } from "tgrid/typings/Promisive"; -import { Semaphore } from "tstl/thread/Semaphore"; +import { Promisive } from "tgrid"; +import { Semaphore } from "tstl"; import { SemaphoresProvider } from "../server/providers/SemaphoresProvider"; /** * Remote Semaphore. - * + * * @author Jeongho Nam - https://github.com/samchon */ -export class RemoteSemaphore -{ - /** - * @hidden - */ - private controller_: Promisive; +export class RemoteSemaphore { + /** + * @hidden + */ + private controller_: Promisive; - /** - * @hidden - */ - private name_: string; + /** + * @hidden + */ + private name_: string; - /** - * @hidden - */ - private max_: number; + /** + * @hidden + */ + private max_: number; - /* ----------------------------------------------------------- - CONSTRUCTORS - ----------------------------------------------------------- */ - /** - * @hidden - */ - private constructor(controller: Promisive, name: string, max: number) - { - this.controller_ = controller; - this.name_ = name; - this.max_ = max; - } + /* ----------------------------------------------------------- + CONSTRUCTORS + ----------------------------------------------------------- */ + /** + * @hidden + */ + private constructor( + controller: Promisive, + name: string, + max: number, + ) { + this.controller_ = controller; + this.name_ = name; + this.max_ = max; + } - /** - * @internal - */ - public static async create - ( - controller: Promisive, - name: string, - count: number - ): Promise - { - let max: number = await controller.emplace(name, count); - return new RemoteSemaphore(controller, name, max); - } + /** + * @internal + */ + public static async create( + controller: Promisive, + name: string, + count: number, + ): Promise { + let max: number = await controller.emplace(name, count); + return new RemoteSemaphore(controller, name, max); + } - /** - * Get number of maximum sections lockable. - * - * @return Number of maximum sections lockable. - */ - public max(): number - { - return this.max_; - } + /** + * Get number of maximum sections lockable. + * + * @return Number of maximum sections lockable. + */ + public max(): number { + return this.max_; + } - /* ----------------------------------------------------------- - LOCKERS - ----------------------------------------------------------- */ - /** - * Acquires a section. - * - * Acquires a section until be {@link release released}. If all of the sections in the - * semaphore already have been acquired by others, the function call would be blocked until - * one of them returns its acquisition by calling the {@link release} method. - * - * In same reason, if you don't call the {@link release} function after you business, the - * others who want to {@link acquire} a section from the semaphore would be fall into the - * forever sleep. Therefore, never forget to calling the {@link release} function or utilize - * the {@link UniqueLock.lock} function instead with {@link RemoteSemaphore.get_lockable} to - * ensure the safety. - */ - public acquire(): Promise - { - return this.controller_.acquire(this.name_); - } + /* ----------------------------------------------------------- + LOCKERS + ----------------------------------------------------------- */ + /** + * Acquires a section. + * + * Acquires a section until be {@link release released}. If all of the sections in the + * semaphore already have been acquired by others, the function call would be blocked until + * one of them returns its acquisition by calling the {@link release} method. + * + * In same reason, if you don't call the {@link release} function after you business, the + * others who want to {@link acquire} a section from the semaphore would be fall into the + * forever sleep. Therefore, never forget to calling the {@link release} function or utilize + * the {@link UniqueLock.lock} function instead with {@link RemoteSemaphore.get_lockable} to + * ensure the safety. + */ + public acquire(): Promise { + return this.controller_.acquire(this.name_); + } - /** - * Tries to acquire a section. - * - * Attempts to acquire a section without blocking. If succeeded to acquire a section from the - * semaphore immediately, it returns `true` directly. Otherwise all of the sections in the - * semaphore are full, the function gives up the trial immediately and returns `false` - * directly. - * - * Note that, if you succeeded to acquire a section from the semaphore (returns `true) but do - * not call the {@link release} function after your business, the others who want to - * {@link acquire} a section from the semaphore would be fall into the forever sleep. - * Therefore, never forget to calling the {@link release} function or utilize the - * {@link UniqueLock.try_lock} function instead with {@link RemoteSemaphore.get_lockable} to - * ensure the safety. - * - * @return Whether succeeded to acquire or not. - */ - public try_acquire(): Promise - { - return this.controller_.try_acquire(this.name_); - } + /** + * Tries to acquire a section. + * + * Attempts to acquire a section without blocking. If succeeded to acquire a section from the + * semaphore immediately, it returns `true` directly. Otherwise all of the sections in the + * semaphore are full, the function gives up the trial immediately and returns `false` + * directly. + * + * Note that, if you succeeded to acquire a section from the semaphore (returns `true) but do + * not call the {@link release} function after your business, the others who want to + * {@link acquire} a section from the semaphore would be fall into the forever sleep. + * Therefore, never forget to calling the {@link release} function or utilize the + * {@link UniqueLock.try_lock} function instead with {@link RemoteSemaphore.get_lockable} to + * ensure the safety. + * + * @return Whether succeeded to acquire or not. + */ + public try_acquire(): Promise { + return this.controller_.try_acquire(this.name_); + } - /** - * Tries to acquire a section until timeout. - * - * Attempts to acquire a section from the semaphore until timeout. If succeeded to acquire a - * section until the timeout, it returns `true`. Otherwise failed to acquiring a section in - * given the time, the function gives up the trial and returns `false`. - * - * Failed to acquiring a section in the given time (returns `false`), it means that there're - * someone who have already {@link acquire acquired} sections and do not return them over the - * time expiration. - * - * Note that, if you succeeded to acquire a section from the semaphore (returns `true) but do - * not call the {@link release} function after your business, the others who want to - * {@link acquire} a section from the semaphore would be fall into the forever sleep. - * Therefore, never forget to calling the {@link release} function or utilize the - * {@link UniqueLock.try_acquire_for} function instead with - * {@link RemoteSemaphore.get_lockable} to ensure the safety. - * - * @param ms The maximum miliseconds for waiting. - * @return Whether succeded to acquire or not. - */ - public try_acquire_for(ms: number): Promise - { - return this.controller_.try_acquire_for(this.name_, ms); - } + /** + * Tries to acquire a section until timeout. + * + * Attempts to acquire a section from the semaphore until timeout. If succeeded to acquire a + * section until the timeout, it returns `true`. Otherwise failed to acquiring a section in + * given the time, the function gives up the trial and returns `false`. + * + * Failed to acquiring a section in the given time (returns `false`), it means that there're + * someone who have already {@link acquire acquired} sections and do not return them over the + * time expiration. + * + * Note that, if you succeeded to acquire a section from the semaphore (returns `true) but do + * not call the {@link release} function after your business, the others who want to + * {@link acquire} a section from the semaphore would be fall into the forever sleep. + * Therefore, never forget to calling the {@link release} function or utilize the + * {@link UniqueLock.try_acquire_for} function instead with + * {@link RemoteSemaphore.get_lockable} to ensure the safety. + * + * @param ms The maximum miliseconds for waiting. + * @return Whether succeded to acquire or not. + */ + public try_acquire_for(ms: number): Promise { + return this.controller_.try_acquire_for(this.name_, ms); + } - /** - * Tries to acquire a section until timeout. - * - * Attempts to acquire a section from the semaphore until time expiration. If succeeded to - * acquire a section until the time expiration, it returns `true`. Otherwise failed to - * acquiring a section in the given time, the function gives up the trial and returns `false`. - * - * Failed to acquiring a section in the given time (returns `false`), it means that there're - * someone who have already {@link acquire acquired} sections and do not return them over the - * time expiration. - * - * Note that, if you succeeded to acquire a section from the semaphore (returns `true) but do - * not call the {@link release} function after your business, the others who want to - * {@link acquire} a section from the semaphore would be fall into the forever sleep. - * Therefore, never forget to calling the {@link release} function or utilize the - * {@link UniqueLock.try_acquire_until} function instead with - * {@link RemoteSemaphore.get_lockable} to ensure the safety. - * - * @param at The maximum time point to wait. - * @return Whether succeded to acquire or not. - */ - public async try_acquire_until(at: Date): Promise - { - let ms: number = at.getTime() - Date.now(); - return await this.try_acquire_for(ms); - } + /** + * Tries to acquire a section until timeout. + * + * Attempts to acquire a section from the semaphore until time expiration. If succeeded to + * acquire a section until the time expiration, it returns `true`. Otherwise failed to + * acquiring a section in the given time, the function gives up the trial and returns `false`. + * + * Failed to acquiring a section in the given time (returns `false`), it means that there're + * someone who have already {@link acquire acquired} sections and do not return them over the + * time expiration. + * + * Note that, if you succeeded to acquire a section from the semaphore (returns `true) but do + * not call the {@link release} function after your business, the others who want to + * {@link acquire} a section from the semaphore would be fall into the forever sleep. + * Therefore, never forget to calling the {@link release} function or utilize the + * {@link UniqueLock.try_acquire_until} function instead with + * {@link RemoteSemaphore.get_lockable} to ensure the safety. + * + * @param at The maximum time point to wait. + * @return Whether succeded to acquire or not. + */ + public async try_acquire_until(at: Date): Promise { + let ms: number = at.getTime() - Date.now(); + return await this.try_acquire_for(ms); + } - /** - * Release sections. - * - * When you call this {@link release} method and there're someone who are currently blocked - * by attemping to {@link acquire} a section from this semaphore, *n* of them - * (FIFO; first-in-first-out) would {@link acquire} those {@link release released} sections - * and continue their executions. - * - * Otherwise, there's not anyone who is {@link acquire acquiring} the section or number of - * the blocked are less than *n*, the {@link OutOfRange} error would be thrown. - * - * > As you know, when you succeeded to {@link acquire} a section, you don't have to forget - * > to calling this {@link release} method after your business. If you forget it, it would - * > be a terrible situation for the others who're attempting to {@link acquire} a section - * > from this semaphore. - * > - * > However, if you utilize the {@link UniqueLock} with {@link RemoteSemaphore.get_lockable}, - * > you don't need to consider about this {@link release} method. Just define your business - * > into a callback function as a parameter of methods of the {@link UniqueLock}, then this - * > {@link release} method would be automatically called by the {@link UniqueLock} after the - * > business. - * - * @param n Number of sections to be released. Default is 1. - * @throw {@link OutOfRange} when *n* is greater than currently {@link acquire acquired} sections. - */ - public release(count: number = 1): Promise - { - return this.controller_.release(this.name_, count); - } + /** + * Release sections. + * + * When you call this {@link release} method and there're someone who are currently blocked + * by attemping to {@link acquire} a section from this semaphore, *n* of them + * (FIFO; first-in-first-out) would {@link acquire} those {@link release released} sections + * and continue their executions. + * + * Otherwise, there's not anyone who is {@link acquire acquiring} the section or number of + * the blocked are less than *n*, the {@link OutOfRange} error would be thrown. + * + * > As you know, when you succeeded to {@link acquire} a section, you don't have to forget + * > to calling this {@link release} method after your business. If you forget it, it would + * > be a terrible situation for the others who're attempting to {@link acquire} a section + * > from this semaphore. + * > + * > However, if you utilize the {@link UniqueLock} with {@link RemoteSemaphore.get_lockable}, + * > you don't need to consider about this {@link release} method. Just define your business + * > into a callback function as a parameter of methods of the {@link UniqueLock}, then this + * > {@link release} method would be automatically called by the {@link UniqueLock} after the + * > business. + * + * @param n Number of sections to be released. Default is 1. + * @throw {@link OutOfRange} when *n* is greater than currently {@link acquire acquired} sections. + */ + public release(count: number = 1): Promise { + return this.controller_.release(this.name_, count); + } } /** - * + * */ -export namespace RemoteSemaphore -{ - /** - * Capsules a {@link RemoteSemaphore} to be suitable for the {@link UniqueLock}. - * - * @param semaphore Target semaphore to capsule. - * @return Lockable instance suitable for the {@link UniqueLock} - */ - export import get_lockable = Semaphore.get_lockable; -} \ No newline at end of file +export namespace RemoteSemaphore { + /** + * Capsules a {@link RemoteSemaphore} to be suitable for the {@link UniqueLock}. + * + * @param semaphore Target semaphore to capsule. + * @return Lockable instance suitable for the {@link UniqueLock} + */ + export import get_lockable = Semaphore.get_lockable; +} diff --git a/src/client/index.ts b/src/client/index.ts index 3a4451a..f062ae0 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -1,6 +1,5 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -export * from "./RemoteMutex"; \ No newline at end of file +export * from "./RemoteBarrier"; +export * from "./RemoteConditionVariable"; +export * from "./RemoteLatch"; +export * from "./RemoteMutex"; +export * from "./RemoteSemaphore"; diff --git a/src/index.ts b/src/index.ts index 2ef6727..a5c393c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,4 +6,4 @@ import * as msv from "./module"; export default msv; -export * from "./module"; \ No newline at end of file +export * from "./module"; diff --git a/src/module.ts b/src/module.ts index d9785d6..8d0bda0 100644 --- a/src/module.ts +++ b/src/module.ts @@ -7,4 +7,4 @@ export * from "./MutexServer"; export * from "./MutexAcceptor"; export * from "./MutexConnector"; -export * from "./client/index"; \ No newline at end of file +export * from "./client/index"; diff --git a/src/server/GlobalGroup.ts b/src/server/GlobalGroup.ts index 0a6a335..07946a6 100644 --- a/src/server/GlobalGroup.ts +++ b/src/server/GlobalGroup.ts @@ -1,26 +1,20 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- +import { GlobalBarriers } from "./global/GlobalBarriers"; import { GlobalConditionVariablaes } from "./global/GlobalConditionVariables"; +import { GlobalLatches } from "./global/GlobalLatches"; import { GlobalMutexes } from "./global/GlobalMutexes"; import { GlobalSemaphores } from "./global/GlobalSemaphores"; -import { GlobalBarriers } from "./global/GlobalBarriers"; -import { GlobalLatches } from "./global/GlobalLatches"; - /** * @internal */ -export class GlobalGroup -{ - // MAIN COMPONENTS - public condition_variables: GlobalConditionVariablaes = new GlobalConditionVariablaes(); - public mutexes: GlobalMutexes = new GlobalMutexes(); - public semaphores: GlobalSemaphores = new GlobalSemaphores(); +export class GlobalGroup { + // MAIN COMPONENTS + public condition_variables: GlobalConditionVariablaes = + new GlobalConditionVariablaes(); + public mutexes: GlobalMutexes = new GlobalMutexes(); + public semaphores: GlobalSemaphores = new GlobalSemaphores(); - // ADAPTOR COMPONENTS - public barriers: GlobalBarriers = new GlobalBarriers(); - public latches: GlobalLatches = new GlobalLatches(); -} \ No newline at end of file + // ADAPTOR COMPONENTS + public barriers: GlobalBarriers = new GlobalBarriers(); + public latches: GlobalLatches = new GlobalLatches(); +} diff --git a/src/server/ProviderCapsule.ts b/src/server/ProviderCapsule.ts index a7246b2..6680a66 100644 --- a/src/server/ProviderCapsule.ts +++ b/src/server/ProviderCapsule.ts @@ -1,15 +1,9 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { ProviderGroup } from "./ProviderGroup"; /** * @internal */ -export interface ProviderCapsule -{ - group: ProviderGroup; - provider: T; -} \ No newline at end of file +export interface ProviderCapsule { + group: ProviderGroup; + provider: T; +} diff --git a/src/server/ProviderGroup.ts b/src/server/ProviderGroup.ts index 07b47e5..c2fca44 100644 --- a/src/server/ProviderGroup.ts +++ b/src/server/ProviderGroup.ts @@ -1,14 +1,8 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; +import { WebAcceptor } from "tgrid"; import { List } from "tstl/container/List"; import { Joiner } from "./components/internal/Joiner"; import { GlobalGroup } from "./GlobalGroup"; - import { ConditionVariablesProvider } from "./providers/ConditionVariablesProvider"; import { SemaphoresProvider } from "./providers/SemaphoresProvider"; import { MutexesProvider } from "./providers/MutexesProvider"; @@ -18,39 +12,60 @@ import { LatchesProvider } from "./providers/LatchesProvider"; /** * @internal */ -export class ProviderGroup -{ - private readonly disolvers_: List; - - // MAIN COMPONENTS - public readonly condition_variables: ConditionVariablesProvider - public readonly mutexes: MutexesProvider; - public readonly semaphores: SemaphoresProvider; - - // ADAPTOR COMPONENTS - public readonly barriers: BarriersProvider; - public readonly latches: LatchesProvider; - - public constructor(group: GlobalGroup, acceptor: WebAcceptor) - { - this.disolvers_ = new List(); - - this.condition_variables = new ConditionVariablesProvider(group.condition_variables, acceptor, this.disolvers_, "RemoteConditionVariable"); - this.mutexes = new MutexesProvider(group.mutexes, acceptor, this.disolvers_, "RemoteMutex"); - this.semaphores = new SemaphoresProvider(group.semaphores, acceptor, this.disolvers_, "RemoteSemaphore"); - - this.barriers = new BarriersProvider(group.barriers, acceptor, this.disolvers_, "RemoteBarrier"); - this.latches = new LatchesProvider(group.latches, acceptor, this.disolvers_, "RemoteLatch"); - } - - public async destructor(): Promise - { - for (let joiner of this.disolvers_) - if (joiner !== undefined) - await joiner(); - - await this.condition_variables.destructor(); - await this.mutexes.destructor(); - await this.semaphores.destructor(); - } -} \ No newline at end of file +export class ProviderGroup { + private readonly disolvers_: List; + + // MAIN COMPONENTS + public readonly condition_variables: ConditionVariablesProvider; + public readonly mutexes: MutexesProvider; + public readonly semaphores: SemaphoresProvider; + + // ADAPTOR COMPONENTS + public readonly barriers: BarriersProvider; + public readonly latches: LatchesProvider; + + public constructor(group: GlobalGroup, acceptor: WebAcceptor) { + this.disolvers_ = new List(); + + this.condition_variables = new ConditionVariablesProvider( + group.condition_variables, + acceptor, + this.disolvers_, + "RemoteConditionVariable", + ); + this.mutexes = new MutexesProvider( + group.mutexes, + acceptor, + this.disolvers_, + "RemoteMutex", + ); + this.semaphores = new SemaphoresProvider( + group.semaphores, + acceptor, + this.disolvers_, + "RemoteSemaphore", + ); + + this.barriers = new BarriersProvider( + group.barriers, + acceptor, + this.disolvers_, + "RemoteBarrier", + ); + this.latches = new LatchesProvider( + group.latches, + acceptor, + this.disolvers_, + "RemoteLatch", + ); + } + + public async destructor(): Promise { + for (let joiner of this.disolvers_) + if (joiner !== undefined) await joiner(); + + await this.condition_variables.destructor(); + await this.mutexes.destructor(); + await this.semaphores.destructor(); + } +} diff --git a/src/server/components/IComponent.ts b/src/server/components/IComponent.ts index 5ef75b7..cdbec0d 100644 --- a/src/server/components/IComponent.ts +++ b/src/server/components/IComponent.ts @@ -1,15 +1,9 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; +import { WebAcceptor } from "tgrid"; /** * @internal */ -export interface IComponent -{ - _Insert_acceptor(acceptor: WebAcceptor): void; - _Erase_acceptor(acceptor: WebAcceptor): boolean; -} \ No newline at end of file +export interface IComponent { + _Insert_acceptor(acceptor: WebAcceptor): void; + _Erase_acceptor(acceptor: WebAcceptor): boolean; +} diff --git a/src/server/components/ServerBarrier.ts b/src/server/components/ServerBarrier.ts index 4c0e0ff..098a0be 100644 --- a/src/server/components/ServerBarrier.ts +++ b/src/server/components/ServerBarrier.ts @@ -1,81 +1,76 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- +import { WebAcceptor } from "tgrid"; +import { List } from "tstl"; + import { IComponent } from "./IComponent"; import { ServerConditionVariable } from "./ServerConditionVariable"; - -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; -import { List } from "tstl/container/List"; import { Joiner } from "./internal/Joiner"; /** * @internal */ -export class ServerBarrier implements IComponent -{ - private cv_: ServerConditionVariable; +export class ServerBarrier implements IComponent { + private cv_: ServerConditionVariable; - private size_: number; - private count_: number; + private size_: number; + private count_: number; - /* --------------------------------------------------------- - CONSTRUCTORS - --------------------------------------------------------- */ - public constructor(size: number) - { - this.cv_ = new ServerConditionVariable(); + /* --------------------------------------------------------- + CONSTRUCTORS + --------------------------------------------------------- */ + public constructor(size: number) { + this.cv_ = new ServerConditionVariable(); - this.size_ = size; - this.count_ = size; - } + this.size_ = size; + this.count_ = size; + } - public _Insert_acceptor(acceptor: WebAcceptor): void - { - this.cv_._Insert_acceptor(acceptor); - } + public _Insert_acceptor(acceptor: WebAcceptor): void { + this.cv_._Insert_acceptor(acceptor); + } - public _Erase_acceptor(acceptor: WebAcceptor): boolean - { - return this.cv_._Erase_acceptor(acceptor); - } + public _Erase_acceptor(acceptor: WebAcceptor): boolean { + return this.cv_._Erase_acceptor(acceptor); + } - /* --------------------------------------------------------- - WAITORS - --------------------------------------------------------- */ - public wait(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return this.cv_.wait(acceptor, disolver); - } + /* --------------------------------------------------------- + WAITORS + --------------------------------------------------------- */ + public wait( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return this.cv_.wait(acceptor, disolver); + } - public wait_for(ms: number, acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return this.cv_.wait_for(ms, acceptor, disolver); - } + public wait_for( + ms: number, + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return this.cv_.wait_for(ms, acceptor, disolver); + } - /* --------------------------------------------------------- - ARRIVERS - --------------------------------------------------------- */ - public async arrive(n: number): Promise - { - let completed: boolean = (this.count_ += n) <= this.size_; - if (completed === false) - return; + /* --------------------------------------------------------- + ARRIVERS + --------------------------------------------------------- */ + public async arrive(n: number): Promise { + let completed: boolean = (this.count_ += n) <= this.size_; + if (completed === false) return; - this.count_ %= this.size_; - await this.cv_.notify_all(); - } + this.count_ %= this.size_; + await this.cv_.notify_all(); + } - public async arrive_and_wait(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - await this.arrive(1); - await this.wait(acceptor, disolver); - } + public async arrive_and_wait( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + await this.arrive(1); + await this.wait(acceptor, disolver); + } - public async arrive_and_drop(): Promise - { - --this.size_; - await this.arrive(0); - } -} \ No newline at end of file + public async arrive_and_drop(): Promise { + --this.size_; + await this.arrive(0); + } +} diff --git a/src/server/components/ServerConditionVariable.ts b/src/server/components/ServerConditionVariable.ts index d61ec2e..bb47b65 100644 --- a/src/server/components/ServerConditionVariable.ts +++ b/src/server/components/ServerConditionVariable.ts @@ -1,133 +1,117 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { SolidComponent } from "./SolidComponent"; - -import { List } from "tstl/container/List"; -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; -import { sleep_for } from "tstl/thread/global"; +import { WebAcceptor } from "tgrid"; +import { List, sleep_for } from "tstl"; +import { SolidComponent } from "./SolidComponent"; import { LockType } from "tstl/internal/thread/LockType"; import { Joiner } from "./internal/Joiner"; /** * @internal */ -export class ServerConditionVariable extends SolidComponent -{ - /* --------------------------------------------------------- - WAITORS - --------------------------------------------------------- */ - public wait(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return new Promise(resolve => - { - // ENROLL TO THE RESOLVERS - let it: List.Iterator = this._Insert_resolver({ - handler: resolve, - lockType: LockType.HOLD, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Cancel_wait(it); - }); - } - - public wait_for(ms: number, acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return new Promise(resolve => - { - // ENROLL TO THE RESOLVERS - let it: List.Iterator = this._Insert_resolver({ - handler: resolve, - lockType: LockType.KNOCK, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Cancel_wait(it); - - // TIME EXPIRATION HANDLER - sleep_for(ms).then(() => - { - resolve(this._Cancel_wait(it) === false); - }); - }); - } - - private _Cancel_wait(it: List.Iterator): boolean - { - if (it.value.handler === null) - return false; - - it.value.destructor!(); - this.queue_.erase(it); - - return true; - } - - /* --------------------------------------------------------- - NOTIFIERS - --------------------------------------------------------- */ - public async notify_one(): Promise - { - if (this.queue_.empty()) - return; - - // POP THE FIRST ITEM - let it: List.Iterator = this.queue_.begin(); - this.queue_.erase(it); - - // DO RESOLVE - this._Notify(it.value, true); - } - - public async notify_all(): Promise - { - if (this.queue_.empty()) - return; - - // COPY RESOLVERS - let ordinaryResolvers: Resolver[] = [ ...this.queue_ ]; - let copiedResolvers: Resolver[] = ordinaryResolvers.map(resolver => ({ ...resolver })); - - // CLEAR OLD ITEMS - this.queue_.clear(); - for (let resolver of ordinaryResolvers) - resolver.destructor!(); - - // DO NOTIFY - for (let resolver of copiedResolvers) - this._Notify(resolver, false); - } - - private _Notify(resolver: Resolver, discard: boolean): void - { - // RESERVE HANDLER - let handler = resolver.handler!; - - // DISCARD FOR SEQUENCE - if (discard === true) - resolver.destructor!(); - - // CALL HANDLER - if (resolver.lockType === LockType.HOLD) - handler(); - else - handler(true); - } +export class ServerConditionVariable extends SolidComponent { + /* --------------------------------------------------------- + WAITORS + --------------------------------------------------------- */ + public wait( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return new Promise((resolve) => { + // ENROLL TO THE RESOLVERS + let it: List.Iterator = this._Insert_resolver({ + handler: resolve, + lockType: LockType.HOLD, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Cancel_wait(it); + }); + } + + public wait_for( + ms: number, + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return new Promise((resolve) => { + // ENROLL TO THE RESOLVERS + let it: List.Iterator = this._Insert_resolver({ + handler: resolve, + lockType: LockType.KNOCK, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Cancel_wait(it); + + // TIME EXPIRATION HANDLER + sleep_for(ms).then(() => { + resolve(this._Cancel_wait(it) === false); + }); + }); + } + + private _Cancel_wait(it: List.Iterator): boolean { + if (it.value.handler === null) return false; + + it.value.destructor!(); + this.queue_.erase(it); + + return true; + } + + /* --------------------------------------------------------- + NOTIFIERS + --------------------------------------------------------- */ + public async notify_one(): Promise { + if (this.queue_.empty()) return; + + // POP THE FIRST ITEM + let it: List.Iterator = this.queue_.begin(); + this.queue_.erase(it); + + // DO RESOLVE + this._Notify(it.value, true); + } + + public async notify_all(): Promise { + if (this.queue_.empty()) return; + + // COPY RESOLVERS + let ordinaryResolvers: Resolver[] = [...this.queue_]; + let copiedResolvers: Resolver[] = ordinaryResolvers.map((resolver) => ({ + ...resolver, + })); + + // CLEAR OLD ITEMS + this.queue_.clear(); + for (let resolver of ordinaryResolvers) resolver.destructor!(); + + // DO NOTIFY + for (let resolver of copiedResolvers) this._Notify(resolver, false); + } + + private _Notify(resolver: Resolver, discard: boolean): void { + // RESERVE HANDLER + let handler = resolver.handler!; + + // DISCARD FOR SEQUENCE + if (discard === true) resolver.destructor!(); + + // CALL HANDLER + if (resolver.lockType === LockType.HOLD) handler(); + else handler(true); + } } /** * @internal */ -type Resolver = SolidComponent.Resolver; \ No newline at end of file +type Resolver = SolidComponent.Resolver; diff --git a/src/server/components/ServerLatch.ts b/src/server/components/ServerLatch.ts index cdcf765..63ae934 100644 --- a/src/server/components/ServerLatch.ts +++ b/src/server/components/ServerLatch.ts @@ -1,81 +1,73 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- +import { WebAcceptor } from "tgrid"; +import { List } from "tstl"; + import { IComponent } from "./IComponent"; import { ServerConditionVariable } from "./ServerConditionVariable"; - -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; -import { List } from "tstl/container/List"; import { Joiner } from "./internal/Joiner"; /** * @internal */ -export class ServerLatch implements IComponent -{ - private cv_: ServerConditionVariable; - private count_: number; +export class ServerLatch implements IComponent { + private cv_: ServerConditionVariable; + private count_: number; - /* --------------------------------------------------------- - CONSTRUCTORS - --------------------------------------------------------- */ - public constructor(size: number) - { - this.cv_ = new ServerConditionVariable(); - this.count_ = size; - } + /* --------------------------------------------------------- + CONSTRUCTORS + --------------------------------------------------------- */ + public constructor(size: number) { + this.cv_ = new ServerConditionVariable(); + this.count_ = size; + } - public _Insert_acceptor(acceptor: WebAcceptor): void - { - this.cv_._Insert_acceptor(acceptor); - } + public _Insert_acceptor(acceptor: WebAcceptor): void { + this.cv_._Insert_acceptor(acceptor); + } - public _Erase_acceptor(acceptor: WebAcceptor): boolean - { - return this.cv_._Erase_acceptor(acceptor); - } + public _Erase_acceptor(acceptor: WebAcceptor): boolean { + return this.cv_._Erase_acceptor(acceptor); + } - /* --------------------------------------------------------- - WAITORS - --------------------------------------------------------- */ - public async wait(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - if (this._Try_wait() === false) - return this.cv_.wait(acceptor, disolver); - } + /* --------------------------------------------------------- + WAITORS + --------------------------------------------------------- */ + public async wait( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + if (this._Try_wait() === false) return this.cv_.wait(acceptor, disolver); + } - public async try_wait(): Promise - { - return this._Try_wait(); - } + public async try_wait(): Promise { + return this._Try_wait(); + } - public async wait_for(ms: number, acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - if (this._Try_wait() === true) - return true; - return await this.cv_.wait_for(ms, acceptor, disolver); - } + public async wait_for( + ms: number, + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + if (this._Try_wait() === true) return true; + return await this.cv_.wait_for(ms, acceptor, disolver); + } - private _Try_wait(): boolean - { - return this.count_ <= 0; - } + private _Try_wait(): boolean { + return this.count_ <= 0; + } - /* --------------------------------------------------------- - DECOUNTERS - --------------------------------------------------------- */ - public async count_down(n: number): Promise - { - this.count_ -= n; - if (this._Try_wait() === true) - await this.cv_.notify_all(); - } + /* --------------------------------------------------------- + DECOUNTERS + --------------------------------------------------------- */ + public async count_down(n: number): Promise { + this.count_ -= n; + if (this._Try_wait() === true) await this.cv_.notify_all(); + } - public async arrive_and_wait(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - await this.count_down(1); - await this.wait(acceptor, disolver); - } -} \ No newline at end of file + public async arrive_and_wait( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + await this.count_down(1); + await this.wait(acceptor, disolver); + } +} diff --git a/src/server/components/ServerMutex.ts b/src/server/components/ServerMutex.ts index 0338b3d..bb30fb7 100644 --- a/src/server/components/ServerMutex.ts +++ b/src/server/components/ServerMutex.ts @@ -1,377 +1,359 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { SolidComponent } from "./SolidComponent"; - -import { List } from "tstl/container/List"; -import { OutOfRange } from "tstl/exception/OutOfRange"; -import { Pair } from "tstl/utility/Pair"; -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; -import { sleep_for } from "tstl/thread/global"; - +import { WebAcceptor } from "tgrid"; +import { List, sleep_for, OutOfRange, Pair } from "tstl"; import { AccessType } from "tstl/internal/thread/AccessType"; import { LockType } from "tstl/internal/thread/LockType"; + +import { SolidComponent } from "./SolidComponent"; import { Disolver } from "./internal/Disolver"; import { Joiner } from "./internal/Joiner"; /** * @internal */ -export class ServerMutex extends SolidComponent -{ - private writing_: number; - private reading_: number; - - /* --------------------------------------------------------- - CONSTRUCTORS - --------------------------------------------------------- */ - public constructor() - { - super(); - - this.writing_ = 0; - this.reading_ = 0; - } - - protected _Insert_resolver(resolver: Resolver): List.Iterator - { - let it: List.Iterator = super._Insert_resolver(resolver); - resolver.iterator = it; - - return it; - } - - /* --------------------------------------------------------- - WRITE LOCK - --------------------------------------------------------- */ - public lock(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return new Promise(resolve => - { - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver({ - handler: (this.writing_++ === 0 && this.reading_ === 0) - ? null - : resolve, - accessType: AccessType.WRITE, - lockType: LockType.HOLD, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS OR WAIT - if (it.value.handler === null) - resolve(); - }); - } - - public async try_lock(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - // LOCKABLE ? - if (this.writing_ !== 0 || this.reading_ !== 0) - return false; - - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver( - { - handler: null, - accessType: AccessType.WRITE, - lockType: LockType.KNOCK, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS - ++this.writing_; - return true; - } - - public try_lock_for(ms: number, acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return new Promise(resolve => - { - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver( - { - handler: (this.writing_++ === 0 && this.reading_ === 0) - ? null - : resolve, - accessType: AccessType.WRITE, - lockType: LockType.KNOCK, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS OR WAIT UNTIL TIMEOUT - if (it.value.handler === null) - resolve(true); // SUCCESS - else - sleep_for(ms).then(() => - { - // NOT YET, THEN DO UNLOCK - if (it.value.handler !== null) - { - --this.writing_; - this._Cancel(it); - } - }); - }); - } - - public async unlock(acceptor: WebAcceptor): Promise - { - //---- - // VALIDATION - //---- - // IN GLOBAL AREA - if (this.queue_.empty() === true || this.queue_.front().accessType !== AccessType.WRITE) - throw new OutOfRange(`Error on RemoteMutex.unlock(): this mutex is free on the unique lock.`); - - // IN LOCAL AREA - let local: SolidComponent.LocalArea | null = this._Get_local_area(acceptor); - if (local === null || local.queue.empty() === true || this.queue_.front() !== local.queue.front()) - throw new OutOfRange("Error on RemoteMutex.unlock(): you're free on the unique lock."); - - //---- - // RELEASE - //---- - // DESTRUCT TOP RESOLVER - let top: Resolver = local.queue.front(); - - this.queue_.erase(top.iterator!); - top.destructor!(); - - // DO RELEASE - --this.writing_; - this._Release(); - } - - /* --------------------------------------------------------- - READ LOCK - --------------------------------------------------------- */ - public lock_shared(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return new Promise(resolve => - { - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver({ - handler: (this.writing_ === 0) - ? null - : resolve, - accessType: AccessType.READ, - lockType: LockType.HOLD, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS OR WAIT - ++this.reading_; - if (it.value.handler === null) - resolve(); - }); - } - - public async try_lock_shared(acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - if (this.writing_ !== 0) - return false; - - // CONSTRUCT RESOLVER - let it = this._Insert_resolver({ - handler: null, - accessType: AccessType.READ, - lockType: LockType.KNOCK, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} +export class ServerMutex extends SolidComponent { + private writing_: number; + private reading_: number; + + /* --------------------------------------------------------- + CONSTRUCTORS + --------------------------------------------------------- */ + public constructor() { + super(); + + this.writing_ = 0; + this.reading_ = 0; + } + + protected _Insert_resolver(resolver: Resolver): List.Iterator { + let it: List.Iterator = super._Insert_resolver(resolver); + resolver.iterator = it; + + return it; + } + + /* --------------------------------------------------------- + WRITE LOCK + --------------------------------------------------------- */ + public lock( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return new Promise((resolve) => { + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: this.writing_++ === 0 && this.reading_ === 0 ? null : resolve, + accessType: AccessType.WRITE, + lockType: LockType.HOLD, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS OR WAIT + if (it.value.handler === null) resolve(); + }); + } + + public async try_lock( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + // LOCKABLE ? + if (this.writing_ !== 0 || this.reading_ !== 0) return false; + + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: null, + accessType: AccessType.WRITE, + lockType: LockType.KNOCK, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS + ++this.writing_; + return true; + } + + public try_lock_for( + ms: number, + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return new Promise((resolve) => { + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: this.writing_++ === 0 && this.reading_ === 0 ? null : resolve, + accessType: AccessType.WRITE, + lockType: LockType.KNOCK, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS OR WAIT UNTIL TIMEOUT + if (it.value.handler === null) + resolve(true); // SUCCESS + else + sleep_for(ms).then(() => { + // NOT YET, THEN DO UNLOCK + if (it.value.handler !== null) { + --this.writing_; + this._Cancel(it); + } }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS - ++this.reading_; - return true; - } - - public try_lock_shared_for(ms: number, acceptor: WebAcceptor, disolver: List.Iterator): Promise - { - return new Promise(resolve => - { - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver( - { - handler: (this.writing_ === 0) - ? null - : resolve, - accessType: AccessType.READ, - lockType: LockType.KNOCK, - - acceptor: acceptor, - disolver: disolver, - aggregate: {} - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS OR WAIT UNTIL TIMEOUT - ++this.reading_; - if (it.value.handler === null) - resolve(true); - else - sleep_for(ms).then(() => - { - if (it.value.handler !== null) - { - --this.reading_; - this._Cancel(it); - } - }); + }); + } + + public async unlock(acceptor: WebAcceptor): Promise { + //---- + // VALIDATION + //---- + // IN GLOBAL AREA + if ( + this.queue_.empty() === true || + this.queue_.front().accessType !== AccessType.WRITE + ) + throw new OutOfRange( + `Error on RemoteMutex.unlock(): this mutex is free on the unique lock.`, + ); + + // IN LOCAL AREA + let local: SolidComponent.LocalArea | null = + this._Get_local_area(acceptor); + if ( + local === null || + local.queue.empty() === true || + this.queue_.front() !== local.queue.front() + ) + throw new OutOfRange( + "Error on RemoteMutex.unlock(): you're free on the unique lock.", + ); + + //---- + // RELEASE + //---- + // DESTRUCT TOP RESOLVER + let top: Resolver = local.queue.front(); + + this.queue_.erase(top.iterator!); + top.destructor!(); + + // DO RELEASE + --this.writing_; + this._Release(); + } + + /* --------------------------------------------------------- + READ LOCK + --------------------------------------------------------- */ + public lock_shared( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return new Promise((resolve) => { + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: this.writing_ === 0 ? null : resolve, + accessType: AccessType.READ, + lockType: LockType.HOLD, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS OR WAIT + ++this.reading_; + if (it.value.handler === null) resolve(); + }); + } + + public async try_lock_shared( + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + if (this.writing_ !== 0) return false; + + // CONSTRUCT RESOLVER + let it = this._Insert_resolver({ + handler: null, + accessType: AccessType.READ, + lockType: LockType.KNOCK, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS + ++this.reading_; + return true; + } + + public try_lock_shared_for( + ms: number, + acceptor: WebAcceptor, + disolver: List.Iterator, + ): Promise { + return new Promise((resolve) => { + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: this.writing_ === 0 ? null : resolve, + accessType: AccessType.READ, + lockType: LockType.KNOCK, + + acceptor: acceptor, + disolver: disolver, + aggregate: {}, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS OR WAIT UNTIL TIMEOUT + ++this.reading_; + if (it.value.handler === null) resolve(true); + else + sleep_for(ms).then(() => { + if (it.value.handler !== null) { + --this.reading_; + this._Cancel(it); + } }); + }); + } + + public async unlock_shared(acceptor: WebAcceptor): Promise { + //---- + // VALIDATION + //---- + // IN GLOBAL AREA + if ( + this.queue_.empty() === true || + this.queue_.front().accessType !== AccessType.READ + ) + throw new OutOfRange( + `Error on RemoteMutex.unlock_shared(): this mutex is free on the shared lock.`, + ); + + // IN LOCAL AREA + let local: SolidComponent.LocalArea | null = + this._Get_local_area(acceptor); + if ( + local === null || + local.queue.empty() === true || + local.queue.front().accessType !== AccessType.READ + ) + throw new OutOfRange( + "Error on RemoteMutex.unlock_shared(): you're free on the shared lock.", + ); + + //---- + // RELEASE + //---- + // DESTRUCT THE RESOLVER + let top: Resolver = local.queue.front(); + + this.queue_.erase(top.iterator!); + top.destructor!(); + + // DO RELEASE + --this.reading_; + this._Release(); + } + + /* --------------------------------------------------------- + RELEASE + --------------------------------------------------------- */ + private _Release(): void { + if (this.queue_.empty() === true) return; + + // GATHER THE NEXT STEPS + let currentType: AccessType = this.queue_.front().accessType; + let pairList: Pair[] = []; + + for (let resolver of this.queue_) { + // STOP WHEN DIFFERENT ACCESS TYPE COMES + if (resolver.accessType !== currentType) break; + // RESERVE HANDLER + else if (resolver.handler !== null) { + pairList.push(new Pair(resolver.handler, resolver.lockType)); + resolver.handler = null; + } + + // STOP AFTER WRITE LOCK + if (resolver.accessType === AccessType.WRITE) break; } - public async unlock_shared(acceptor: WebAcceptor): Promise - { - //---- - // VALIDATION - //---- - // IN GLOBAL AREA - if (this.queue_.empty() === true || this.queue_.front().accessType !== AccessType.READ) - throw new OutOfRange(`Error on RemoteMutex.unlock_shared(): this mutex is free on the shared lock.`); - - // IN LOCAL AREA - let local: SolidComponent.LocalArea | null = this._Get_local_area(acceptor); - if (local === null || local.queue.empty() === true || local.queue.front().accessType !== AccessType.READ) - throw new OutOfRange("Error on RemoteMutex.unlock_shared(): you're free on the shared lock."); - - //---- - // RELEASE - //---- - // DESTRUCT THE RESOLVER - let top: Resolver = local.queue.front(); - - this.queue_.erase(top.iterator!); - top.destructor!(); - - // DO RELEASE - --this.reading_; - this._Release(); - } - - /* --------------------------------------------------------- - RELEASE - --------------------------------------------------------- */ - private _Release(): void - { - if (this.queue_.empty() === true) - return; - - // GATHER THE NEXT STEPS - let currentType: AccessType = this.queue_.front().accessType; - let pairList: Pair[] = []; - - for (let resolver of this.queue_) - { - // STOP WHEN DIFFERENT ACCESS TYPE COMES - if (resolver.accessType !== currentType) - break; - - // RESERVE HANDLER - else if (resolver.handler !== null) - { - pairList.push(new Pair(resolver.handler, resolver.lockType)); - resolver.handler = null; - } - - // STOP AFTER WRITE LOCK - if (resolver.accessType === AccessType.WRITE) - break; - } - - // CALL THE HANDLERS - for (let pair of pairList) - if (pair.second === LockType.HOLD) - pair.first(); - else - pair.first(true); - } - - private _Cancel(it: List.Iterator): void - { - // POP HANDLER & DESTRUCT - let handler: Function = it.value.handler!; - - this.queue_.erase(it); - it.value.destructor!(); - - // CHECK THE PREVIOUS HANDLER - let prev: List.Iterator = it.prev(); - if (prev.equals(this.queue_.end()) === false && prev.value.handler === null) - this._Release(); - - // (LAZY) RETURNS FAILURE - handler(false); - } - - private async _Handle_disconnection(it: List.Iterator): Promise - { - // CHECK ALIVE - if ((it as Disolver).erased_ === true) - return; - - //---- - // ROLLBACK ACTION - //---- - else if (it.value.handler === null) - { - // SUCCEEDED TO GET LOCK - if (it.value.accessType === AccessType.WRITE) - await this.unlock(it.value.acceptor); - else - await this.unlock_shared(it.value.acceptor); - } - else - { - // HAD BEEN WAITING - if (it.value.accessType === AccessType.WRITE) - --this.writing_; - else - --this.reading_; - - // CANCEL THE LOCK - this._Cancel(it); - } + // CALL THE HANDLERS + for (let pair of pairList) + if (pair.second === LockType.HOLD) pair.first(); + else pair.first(true); + } + + private _Cancel(it: List.Iterator): void { + // POP HANDLER & DESTRUCT + let handler: Function = it.value.handler!; + + this.queue_.erase(it); + it.value.destructor!(); + + // CHECK THE PREVIOUS HANDLER + let prev: List.Iterator = it.prev(); + if (prev.equals(this.queue_.end()) === false && prev.value.handler === null) + this._Release(); + + // (LAZY) RETURNS FAILURE + handler(false); + } + + private async _Handle_disconnection( + it: List.Iterator, + ): Promise { + // CHECK ALIVE + if (((it) as Disolver).erased_ === true) return; + //---- + // ROLLBACK ACTION + //---- + else if (it.value.handler === null) { + // SUCCEEDED TO GET LOCK + if (it.value.accessType === AccessType.WRITE) + await this.unlock(it.value.acceptor); + else await this.unlock_shared(it.value.acceptor); + } else { + // HAD BEEN WAITING + if (it.value.accessType === AccessType.WRITE) --this.writing_; + else --this.reading_; + + // CANCEL THE LOCK + this._Cancel(it); } + } } /** * @internal */ -interface Resolver extends SolidComponent.Resolver -{ - accessType: AccessType; -} \ No newline at end of file +interface Resolver extends SolidComponent.Resolver { + accessType: AccessType; +} diff --git a/src/server/components/ServerSemaphore.ts b/src/server/components/ServerSemaphore.ts index 847efa4..8e2f4a4 100644 --- a/src/server/components/ServerSemaphore.ts +++ b/src/server/components/ServerSemaphore.ts @@ -1,223 +1,217 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { SolidComponent } from "./SolidComponent"; - -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; - -import { List } from "tstl/container/List"; -import { OutOfRange } from "tstl/exception/OutOfRange"; -import { Pair } from "tstl/utility/Pair"; -import { sleep_for } from "tstl/thread/global"; +import { WebAcceptor } from "tgrid"; +import { List, sleep_for, OutOfRange, Pair } from "tstl"; +import { LockType } from "tstl/internal/thread/LockType"; +import { SolidComponent } from "./SolidComponent"; import { Disolver } from "./internal/Disolver"; -import { LockType } from "tstl/internal/thread/LockType"; /** * @internal */ -export class ServerSemaphore extends SolidComponent -{ - private max_: number; - private acquiring_: number; - - /* --------------------------------------------------------- - CONSTRUCTORS - --------------------------------------------------------- */ - public constructor(max: number) - { - super(); - - this.max_ = max; - this.acquiring_ = 0; - } - - public max(): number - { - return this.max_; - } - - /* --------------------------------------------------------- - ACQUIRANCES - --------------------------------------------------------- */ - public acquire(acceptor: WebAcceptor, disolver: Disolver): Promise - { - return new Promise(resolve => - { - let success: boolean = this.acquiring_ < this.max_; - - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver({ - handler: success ? null : resolve, - lockType: LockType.HOLD, - acceptor: acceptor, - disolver: disolver, - aggregate: { - acquring: success ? 1 : 0 - } - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS OR WAIT - if (it.value.handler === null) - { - ++this.acquiring_; - resolve(); - } - }); - } - - public async try_acquire(acceptor: WebAcceptor, disolver: Disolver): Promise - { - // ACQUIRABLE ? - if (this.acquiring_ >= this.max_) - return false; - - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver({ - handler: null, - lockType: LockType.HOLD, - acceptor: acceptor, - disolver: disolver, - aggregate: { - acquring: 1 - } - }); +export class ServerSemaphore extends SolidComponent { + private max_: number; + private acquiring_: number; + + /* --------------------------------------------------------- + CONSTRUCTORS + --------------------------------------------------------- */ + public constructor(max: number) { + super(); + + this.max_ = max; + this.acquiring_ = 0; + } + + public max(): number { + return this.max_; + } + + /* --------------------------------------------------------- + ACQUIRANCES + --------------------------------------------------------- */ + public acquire( + acceptor: WebAcceptor, + disolver: Disolver, + ): Promise { + return new Promise((resolve) => { + let success: boolean = this.acquiring_ < this.max_; + + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: success ? null : resolve, + lockType: LockType.HOLD, + acceptor: acceptor, + disolver: disolver, + aggregate: { + acquring: success ? 1 : 0, + }, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS OR WAIT + if (it.value.handler === null) { ++this.acquiring_; + resolve(); + } + }); + } + + public async try_acquire( + acceptor: WebAcceptor, + disolver: Disolver, + ): Promise { + // ACQUIRABLE ? + if (this.acquiring_ >= this.max_) return false; + + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: null, + lockType: LockType.HOLD, + acceptor: acceptor, + disolver: disolver, + aggregate: { + acquring: 1, + }, + }); + ++this.acquiring_; + + // RETURNS WITH DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + return true; + } + + public async try_acquire_for( + ms: number, + acceptor: WebAcceptor, + disolver: Disolver, + ): Promise { + return new Promise((resolve) => { + let success: boolean = this.acquiring_ < this.max_; + + // CONSTRUCT RESOLVER + let it: List.Iterator = this._Insert_resolver({ + handler: success ? null : resolve, + lockType: LockType.KNOCK, + acceptor: acceptor, + disolver: disolver, + aggregate: { + acquring: success ? 1 : 0, + }, + }); + + // DISCONNECTION HANDLER + disolver.value = () => this._Handle_disconnection(it); + + // RETURNS OR WAIT UNTIL TIMEOUT + if (it.value.handler === null) { + ++this.acquiring_; + resolve(true); + } else + sleep_for(ms).then(() => { + let success: boolean = it.value.handler === null; + if (success === false) this._Cancel(it); - // RETURNS WITH DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - return true; - } - - public async try_acquire_for(ms: number, acceptor: WebAcceptor, disolver: Disolver): Promise - { - return new Promise(resolve => - { - let success: boolean = this.acquiring_ < this.max_; - - // CONSTRUCT RESOLVER - let it: List.Iterator = this._Insert_resolver({ - handler: success ? null : resolve, - lockType: LockType.KNOCK, - acceptor: acceptor, - disolver: disolver, - aggregate: { - acquring: success ? 1 : 0 - } - }); - - // DISCONNECTION HANDLER - disolver.value = () => this._Handle_disconnection(it); - - // RETURNS OR WAIT UNTIL TIMEOUT - if (it.value.handler === null) - { - ++this.acquiring_; - resolve(true); - } - else - sleep_for(ms).then(() => - { - let success: boolean = (it.value.handler === null); - if (success === false) - this._Cancel(it); - - resolve(success); - }); + resolve(success); }); + }); + } + + private _Cancel(it: List.Iterator): void { + // POP THE LISTENER + let handler: Function = it.value.handler!; + + this.queue_.erase(it); + it.value.destructor!(); + + // RETURNS FAILURE + handler(false); + } + + /* --------------------------------------------------------- + RELEASE + --------------------------------------------------------- */ + public async release( + n: number, + acceptor: WebAcceptor, + ): Promise { + //---- + // VALIDATION + //---- + // IN GLOBAL AREA + if (n < 1) + throw new OutOfRange( + `Error on RemoteSemaphore.release(): parametric n is less than 1 -> (n = ${n}).`, + ); + else if (n > this.max_) + throw new OutOfRange( + `Error on RemoteSemaphore.release(): parametric n is greater than max -> (n = ${n}, max = ${this.max_}).`, + ); + else if (n > this.acquiring_) + throw new OutOfRange( + `Error on RemoteSemaphore.release(): parametric n is greater than acquiring -> (n = ${n}, acquiring = ${this.acquiring_}).`, + ); + + // IN LOCAL AREA + let local: SolidComponent.LocalArea | null = + this._Get_local_area(acceptor); + if (local === null || local.queue.empty() === true) + throw new OutOfRange( + "Error on RemoteSemaphore.release(): you're free on the acquirance.", + ); + else if (local.aggregate.acquring < n) + throw new OutOfRange( + `Error onRemoteSemaphore.release(): parametric n is greater than what you've been acquiring -> (n = ${n}, acquiring = ${local.aggregate.acquring}).`, + ); + + //---- + // RELEASE + //---- + // ERASE FROM QUEUES + this.acquiring_ -= n; + local.aggregate.acquring -= n; + + let count: number = 0; + for ( + let it = local.queue.begin(); + it.equals(local.queue.end()) === false; + + ) { + this.queue_.erase(it.value.iterator!); + it = it.value.destructor!(); + + if (++count === n) break; } - private _Cancel(it: List.Iterator): void - { - // POP THE LISTENER - let handler: Function = it.value.handler!; - - this.queue_.erase(it); - it.value.destructor!(); - - // RETURNS FAILURE - handler(false); - } - - /* --------------------------------------------------------- - RELEASE - --------------------------------------------------------- */ - public async release(n: number, acceptor: WebAcceptor): Promise - { - //---- - // VALIDATION - //---- - // IN GLOBAL AREA - if (n < 1) - throw new OutOfRange(`Error on RemoteSemaphore.release(): parametric n is less than 1 -> (n = ${n}).`); - else if (n > this.max_) - throw new OutOfRange(`Error on RemoteSemaphore.release(): parametric n is greater than max -> (n = ${n}, max = ${this.max_}).`); - else if (n > this.acquiring_) - throw new OutOfRange(`Error on RemoteSemaphore.release(): parametric n is greater than acquiring -> (n = ${n}, acquiring = ${this.acquiring_}).`); - - // IN LOCAL AREA - let local: SolidComponent.LocalArea | null = this._Get_local_area(acceptor); - if (local === null || local.queue.empty() === true) - throw new OutOfRange("Error on RemoteSemaphore.release(): you're free on the acquirance."); - else if (local.aggregate.acquring < n) - throw new OutOfRange(`Error onRemoteSemaphore.release(): parametric n is greater than what you've been acquiring -> (n = ${n}, acquiring = ${local.aggregate.acquring}).`); - - //---- - // RELEASE - //---- - // ERASE FROM QUEUES - this.acquiring_ -= n; - local.aggregate.acquring -= n; - - let count: number = 0; - for (let it = local.queue.begin(); it.equals(local.queue.end()) === false; ) - { - this.queue_.erase(it.value.iterator!); - it = it.value.destructor!(); - - if (++count === n) - break; - } - - // RESERVE HANDLERS - let pairList: Pair[] = []; - - for (let resolver of this.queue_) - if (resolver.handler !== null) - { - pairList.push(new Pair(resolver.handler!, resolver.lockType)); - resolver.handler = null; - - ++resolver.aggregate.acquring; - if (++this.acquiring_ === this.max_) - break; - } - - // CALL HANDLERS - for (let pair of pairList) - if (pair.second === LockType.HOLD) - pair.first(); - else - pair.first(true); - } - - private async _Handle_disconnection(it: List.Iterator): Promise - { - // CHECK ALIVE - if ((it as Disolver).erased_ === true) - return; - - // ROLLBACK ACTION - else if (it.value.handler === null) - await this.release(1, it.value.acceptor); - else - this._Cancel(it); - } + // RESERVE HANDLERS + let pairList: Pair[] = []; + + for (let resolver of this.queue_) + if (resolver.handler !== null) { + pairList.push(new Pair(resolver.handler!, resolver.lockType)); + resolver.handler = null; + + ++resolver.aggregate.acquring; + if (++this.acquiring_ === this.max_) break; + } + + // CALL HANDLERS + for (let pair of pairList) + if (pair.second === LockType.HOLD) pair.first(); + else pair.first(true); + } + + private async _Handle_disconnection( + it: List.Iterator, + ): Promise { + // CHECK ALIVE + if (((it) as Disolver).erased_ === true) return; + // ROLLBACK ACTION + else if (it.value.handler === null) + await this.release(1, it.value.acceptor); + else this._Cancel(it); + } } /** @@ -228,4 +222,4 @@ type Resolver = SolidComponent.Resolver; /** * @internal */ -type Aggregate = Record<"acquring", number>; \ No newline at end of file +type Aggregate = Record<"acquring", number>; diff --git a/src/server/components/SolidComponent.ts b/src/server/components/SolidComponent.ts index 34567eb..b570f79 100644 --- a/src/server/components/SolidComponent.ts +++ b/src/server/components/SolidComponent.ts @@ -1,129 +1,130 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { List } from "tstl/container/List"; -import { HashMap } from "tstl/container/HashMap"; -import { HashSet } from "tstl/container/HashSet"; -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; +import { WebAcceptor } from "tgrid"; +import { HashMap, HashSet, List } from "tstl"; +import { LockType } from "tstl/internal/thread/LockType"; import { IComponent } from "./IComponent"; import { Disolver } from "./internal/Disolver"; -import { LockType } from "tstl/internal/thread/LockType"; /** * @internal */ export abstract class SolidComponent< - Resolver extends SolidComponent.Resolver, - Aggregate extends Record> - implements IComponent + Resolver extends SolidComponent.Resolver, + Aggregate extends Record, +> implements IComponent { - protected queue_: List; - protected local_areas_: HashMap, SolidComponent.LocalArea>; + protected queue_: List; + protected local_areas_: HashMap< + WebAcceptor, + SolidComponent.LocalArea + >; - private acceptors_: HashSet> = new HashSet(); + private acceptors_: HashSet> = new HashSet(); - /* --------------------------------------------------------- - CONSTRUCTORS - --------------------------------------------------------- */ - public constructor() - { - this.queue_ = new List(); - this.local_areas_ = new HashMap(); - } + /* --------------------------------------------------------- + CONSTRUCTORS + --------------------------------------------------------- */ + public constructor() { + this.queue_ = new List(); + this.local_areas_ = new HashMap(); + } - public _Insert_acceptor(acceptor: WebAcceptor): void - { - this.acceptors_.insert(acceptor); - } + public _Insert_acceptor(acceptor: WebAcceptor): void { + this.acceptors_.insert(acceptor); + } - public _Erase_acceptor(acceptor: WebAcceptor): boolean - { - this.acceptors_.erase(acceptor); - return this.acceptors_.empty(); - } + public _Erase_acceptor(acceptor: WebAcceptor): boolean { + this.acceptors_.erase(acceptor); + return this.acceptors_.empty(); + } - /* --------------------------------------------------------- - ACCESSORS - --------------------------------------------------------- */ - protected _Insert_resolver(resolver: Resolver): List.Iterator - { - //---- - // LOCAL QUEUE - //---- - // FIND OR EMPLACE - let mit: HashMap.Iterator, SolidComponent.LocalArea> = this.local_areas_.find(resolver.acceptor); - if (mit.equals(this.local_areas_.end()) === true) - mit = this.local_areas_.emplace(resolver.acceptor, { - queue: new List(), - aggregate: resolver.aggregate - }).first; - else - { - for (let key in resolver.aggregate) - (mit.second.aggregate as Record)[key] += resolver.aggregate[key]; - resolver.aggregate = mit.second.aggregate; - } + /* --------------------------------------------------------- + ACCESSORS + --------------------------------------------------------- */ + protected _Insert_resolver(resolver: Resolver): List.Iterator { + //---- + // LOCAL QUEUE + //---- + // FIND OR EMPLACE + let mit: HashMap.Iterator< + WebAcceptor, + SolidComponent.LocalArea + > = this.local_areas_.find(resolver.acceptor); + if (mit.equals(this.local_areas_.end()) === true) + mit = this.local_areas_.emplace(resolver.acceptor, { + queue: new List(), + aggregate: resolver.aggregate, + }).first; + else { + for (let key in resolver.aggregate) + (mit.second.aggregate as Record)[key] += + resolver.aggregate[key]; + resolver.aggregate = mit.second.aggregate; + } - // INSERT NEW ITEM - let lit: List.Iterator = mit.second.queue.insert(mit.second.queue.end(), resolver); + // INSERT NEW ITEM + let lit: List.Iterator = mit.second.queue.insert( + mit.second.queue.end(), + resolver, + ); - //---- - // GLOBAL QUEUE - //---- - let ret: List.Iterator = this.queue_.insert(this.queue_.end(), resolver); - resolver.iterator = ret; - resolver.destructor = () => - { - resolver.handler = null; - resolver.disolver.value = undefined; + //---- + // GLOBAL QUEUE + //---- + let ret: List.Iterator = this.queue_.insert( + this.queue_.end(), + resolver, + ); + resolver.iterator = ret; + resolver.destructor = () => { + resolver.handler = null; + resolver.disolver.value = undefined; - if (resolver.disolver.erased_ !== true) - resolver.disolver.source().erase(resolver.disolver); - return mit.second.queue.erase(lit); - }; - return ret; - } + if (resolver.disolver.erased_ !== true) + resolver.disolver.source().erase(resolver.disolver); + return mit.second.queue.erase(lit); + }; + return ret; + } - protected _Get_local_area(acceptor: WebAcceptor): SolidComponent.LocalArea | null - { - let it: HashMap.Iterator, SolidComponent.LocalArea> = this.local_areas_.find(acceptor); - return (it.equals(this.local_areas_.end()) === false) - ? it.second - : null; - } + protected _Get_local_area( + acceptor: WebAcceptor, + ): SolidComponent.LocalArea | null { + let it: HashMap.Iterator< + WebAcceptor, + SolidComponent.LocalArea + > = this.local_areas_.find(acceptor); + return it.equals(this.local_areas_.end()) === false ? it.second : null; + } } /** * @internal */ -export namespace SolidComponent -{ - export interface Resolver< - T extends Resolver, - AggregateT extends Record> - { - // THREAD HANDLER - handler: Function | null; - lockType: LockType; +export namespace SolidComponent { + export interface Resolver< + T extends Resolver, + AggregateT extends Record, + > { + // THREAD HANDLER + handler: Function | null; + lockType: LockType; - // ASSET FFOR CLIENT - acceptor: WebAcceptor; - aggregate: AggregateT; - disolver: Disolver; + // ASSET FFOR CLIENT + acceptor: WebAcceptor; + aggregate: AggregateT; + disolver: Disolver; - // FOR DESTRUCTION - destructor?: () => List.Iterator; - iterator?: List.Iterator; - } + // FOR DESTRUCTION + destructor?: () => List.Iterator; + iterator?: List.Iterator; + } - export type LocalArea< - ResolverT extends Resolver, - AggregateT extends Record> = - { - queue: List; - aggregate: AggregateT; - }; -} \ No newline at end of file + export type LocalArea< + ResolverT extends Resolver, + AggregateT extends Record, + > = { + queue: List; + aggregate: AggregateT; + }; +} diff --git a/src/server/components/internal/Disolver.ts b/src/server/components/internal/Disolver.ts index ce0947f..a3681f4 100644 --- a/src/server/components/internal/Disolver.ts +++ b/src/server/components/internal/Disolver.ts @@ -1,12 +1,7 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { List } from "tstl/container/List"; +import { List } from "tstl"; import { Joiner } from "./Joiner"; /** * @internal */ -export type Disolver = List.Iterator & { erased_?: boolean }; \ No newline at end of file +export type Disolver = List.Iterator & { erased_?: boolean }; diff --git a/src/server/components/internal/Joiner.ts b/src/server/components/internal/Joiner.ts index ebe21bb..cb9b6fe 100644 --- a/src/server/components/internal/Joiner.ts +++ b/src/server/components/internal/Joiner.ts @@ -1,9 +1,4 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- /** * @internal */ -export type Joiner = (() => void) | (() => Promise) | undefined; \ No newline at end of file +export type Joiner = (() => void) | (() => Promise) | undefined; diff --git a/src/server/global/GlobalBarriers.ts b/src/server/global/GlobalBarriers.ts index f0f37aa..bff2d89 100644 --- a/src/server/global/GlobalBarriers.ts +++ b/src/server/global/GlobalBarriers.ts @@ -1,20 +1,13 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { GlobalBase } from "./GlobalBase"; import { ServerBarrier } from "../components/ServerBarrier"; /** * @internal */ -export class GlobalBarriers extends GlobalBase -{ - protected _Create_component(size: number): ServerBarrier - { - return new ServerBarrier(size); - } +export class GlobalBarriers extends GlobalBase { + protected _Create_component(size: number): ServerBarrier { + return new ServerBarrier(size); + } - protected _Returns({}: ServerBarrier): void {} -} \ No newline at end of file + protected _Returns({}: ServerBarrier): void {} +} diff --git a/src/server/global/GlobalBase.ts b/src/server/global/GlobalBase.ts index f185546..fea43c2 100644 --- a/src/server/global/GlobalBase.ts +++ b/src/server/global/GlobalBase.ts @@ -1,47 +1,42 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- -import { HashMap } from "tstl/container/HashMap"; -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; +import { WebAcceptor } from "tgrid"; +import { HashMap } from "tstl"; import { IComponent } from "../components/IComponent"; /** * @internal */ -export abstract class GlobalBase -{ - private dict_: HashMap = new HashMap(); +export abstract class GlobalBase { + private dict_: HashMap = new HashMap(); - /* --------------------------------------------------------- + /* --------------------------------------------------------- CONSTRUCTORS --------------------------------------------------------- */ - protected abstract _Create_component(param: Param): ComponentT; - protected abstract _Returns(component: ComponentT): Ret; + protected abstract _Create_component(param: Param): ComponentT; + protected abstract _Returns(component: ComponentT): Ret; - public emplace(name: string, param: Param, acceptor: WebAcceptor): Ret - { - let it: HashMap.Iterator = this.dict_.find(name); - if (it.equals(this.dict_.end()) === true) - it = this.dict_.emplace(name, this._Create_component(param)).first; - it.second._Insert_acceptor(acceptor); + public emplace( + name: string, + param: Param, + acceptor: WebAcceptor, + ): Ret { + let it: HashMap.Iterator = this.dict_.find(name); + if (it.equals(this.dict_.end()) === true) + it = this.dict_.emplace(name, this._Create_component(param)).first; + it.second._Insert_acceptor(acceptor); - return this._Returns(it.second); - } + return this._Returns(it.second); + } - public erase(name: string, acceptor: WebAcceptor): void - { - if (this.dict_.get(name)._Erase_acceptor(acceptor) === true) - this.dict_.erase(name); - } + public erase(name: string, acceptor: WebAcceptor): void { + if (this.dict_.get(name)._Erase_acceptor(acceptor) === true) + this.dict_.erase(name); + } - /* --------------------------------------------------------- + /* --------------------------------------------------------- ACCESSORS --------------------------------------------------------- */ - public get(name: string): ComponentT - { - return this.dict_.get(name); - } -} \ No newline at end of file + public get(name: string): ComponentT { + return this.dict_.get(name); + } +} diff --git a/src/server/global/GlobalConditionVariables.ts b/src/server/global/GlobalConditionVariables.ts index cc867bb..baf43e1 100644 --- a/src/server/global/GlobalConditionVariables.ts +++ b/src/server/global/GlobalConditionVariables.ts @@ -1,20 +1,17 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { GlobalBase } from "./GlobalBase"; import { ServerConditionVariable } from "../components/ServerConditionVariable"; /** * @internal */ -export class GlobalConditionVariablaes extends GlobalBase -{ - protected _Create_component(): ServerConditionVariable - { - return new ServerConditionVariable(); - } +export class GlobalConditionVariablaes extends GlobalBase< + ServerConditionVariable, + undefined, + void +> { + protected _Create_component(): ServerConditionVariable { + return new ServerConditionVariable(); + } - protected _Returns({}: ServerConditionVariable): void {} -} \ No newline at end of file + protected _Returns({}: ServerConditionVariable): void {} +} diff --git a/src/server/global/GlobalLatches.ts b/src/server/global/GlobalLatches.ts index 8ac26e4..acc1272 100644 --- a/src/server/global/GlobalLatches.ts +++ b/src/server/global/GlobalLatches.ts @@ -1,20 +1,13 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { GlobalBase } from "./GlobalBase"; import { ServerLatch } from "../components/ServerLatch"; /** * @internal */ -export class GlobalLatches extends GlobalBase -{ - protected _Create_component(size: number): ServerLatch - { - return new ServerLatch(size); - } +export class GlobalLatches extends GlobalBase { + protected _Create_component(size: number): ServerLatch { + return new ServerLatch(size); + } - protected _Returns({}: ServerLatch): void {} -} \ No newline at end of file + protected _Returns({}: ServerLatch): void {} +} diff --git a/src/server/global/GlobalMutexes.ts b/src/server/global/GlobalMutexes.ts index f8924a0..4e88fd1 100644 --- a/src/server/global/GlobalMutexes.ts +++ b/src/server/global/GlobalMutexes.ts @@ -1,20 +1,13 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { GlobalBase } from "./GlobalBase"; import { ServerMutex } from "../components/ServerMutex"; /** * @internal */ -export class GlobalMutexes extends GlobalBase -{ - protected _Create_component(): ServerMutex - { - return new ServerMutex(); - } +export class GlobalMutexes extends GlobalBase { + protected _Create_component(): ServerMutex { + return new ServerMutex(); + } - protected _Returns({}: ServerMutex): void {} -} \ No newline at end of file + protected _Returns({}: ServerMutex): void {} +} diff --git a/src/server/global/GlobalSemaphores.ts b/src/server/global/GlobalSemaphores.ts index 4a4d9c9..c2f1b4c 100644 --- a/src/server/global/GlobalSemaphores.ts +++ b/src/server/global/GlobalSemaphores.ts @@ -1,23 +1,19 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { GlobalBase } from "./GlobalBase"; import { ServerSemaphore } from "../components/ServerSemaphore"; /** * @internal */ -export class GlobalSemaphores extends GlobalBase -{ - protected _Create_component(max: number): ServerSemaphore - { - return new ServerSemaphore(max); - } +export class GlobalSemaphores extends GlobalBase< + ServerSemaphore, + number, + number +> { + protected _Create_component(max: number): ServerSemaphore { + return new ServerSemaphore(max); + } - protected _Returns(sema: ServerSemaphore): number - { - return sema.max(); - } -} \ No newline at end of file + protected _Returns(sema: ServerSemaphore): number { + return sema.max(); + } +} diff --git a/src/server/providers/BarriersProvider.ts b/src/server/providers/BarriersProvider.ts index e86ed2d..1146976 100644 --- a/src/server/providers/BarriersProvider.ts +++ b/src/server/providers/BarriersProvider.ts @@ -1,38 +1,34 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { ProviderBase } from "./ProviderBase"; import { GlobalBarriers } from "../global/GlobalBarriers"; /** * @internal */ -export class BarriersProvider extends ProviderBase -{ - public wait(name: string): Promise - { - return this.get(name).wait(this.acceptor_, this.createDisolver()); - } +export class BarriersProvider extends ProviderBase< + GlobalBarriers, + number, + void +> { + public wait(name: string): Promise { + return this.get(name).wait(this.acceptor_, this.createDisolver()); + } - public wait_for(name: string, ms: number): Promise - { - return this.get(name).wait_for(ms, this.acceptor_, this.createDisolver()); - } + public wait_for(name: string, ms: number): Promise { + return this.get(name).wait_for(ms, this.acceptor_, this.createDisolver()); + } - public arrive(name: string, n: number): Promise - { - return this.get(name).arrive(n); - } + public arrive(name: string, n: number): Promise { + return this.get(name).arrive(n); + } - public arrive_and_drop(name: string): Promise - { - return this.get(name).arrive_and_drop(); - } + public arrive_and_drop(name: string): Promise { + return this.get(name).arrive_and_drop(); + } - public arrive_and_wait(name: string): Promise - { - return this.get(name).arrive_and_wait(this.acceptor_, this.createDisolver()); - } -} \ No newline at end of file + public arrive_and_wait(name: string): Promise { + return this.get(name).arrive_and_wait( + this.acceptor_, + this.createDisolver(), + ); + } +} diff --git a/src/server/providers/ConditionVariablesProvider.ts b/src/server/providers/ConditionVariablesProvider.ts index 4bf3241..404752a 100644 --- a/src/server/providers/ConditionVariablesProvider.ts +++ b/src/server/providers/ConditionVariablesProvider.ts @@ -1,33 +1,27 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { ProviderBase } from "./ProviderBase"; import { GlobalConditionVariablaes } from "../global/GlobalConditionVariables"; /** * @internal */ -export class ConditionVariablesProvider extends ProviderBase -{ - public wait(name: string): Promise - { - return this.get(name).wait(this.acceptor_, this.createDisolver()); - } +export class ConditionVariablesProvider extends ProviderBase< + GlobalConditionVariablaes, + undefined, + void +> { + public wait(name: string): Promise { + return this.get(name).wait(this.acceptor_, this.createDisolver()); + } - public wait_for(name: string, ms: number): Promise - { - return this.get(name).wait_for(ms, this.acceptor_, this.createDisolver()); - } + public wait_for(name: string, ms: number): Promise { + return this.get(name).wait_for(ms, this.acceptor_, this.createDisolver()); + } - public notify_one(name: string): Promise - { - return this.get(name).notify_one(); - } + public notify_one(name: string): Promise { + return this.get(name).notify_one(); + } - public notify_all(name: string): Promise - { - return this.get(name).notify_all(); - } -} \ No newline at end of file + public notify_all(name: string): Promise { + return this.get(name).notify_all(); + } +} diff --git a/src/server/providers/LatchesProvider.ts b/src/server/providers/LatchesProvider.ts index d4c2a02..8f8ba04 100644 --- a/src/server/providers/LatchesProvider.ts +++ b/src/server/providers/LatchesProvider.ts @@ -1,38 +1,30 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { ProviderBase } from "./ProviderBase"; import { GlobalLatches } from "../global/GlobalLatches"; /** * @internal */ -export class LatchesProvider extends ProviderBase -{ - public wait(name: string): Promise - { - return this.get(name).wait(this.acceptor_, this.createDisolver()); - } +export class LatchesProvider extends ProviderBase { + public wait(name: string): Promise { + return this.get(name).wait(this.acceptor_, this.createDisolver()); + } - public try_wait(name: string): Promise - { - return this.get(name).try_wait(); - } + public try_wait(name: string): Promise { + return this.get(name).try_wait(); + } - public wait_for(name: string, ms: number): Promise - { - return this.get(name).wait_for(ms, this.acceptor_, this.createDisolver()); - } + public wait_for(name: string, ms: number): Promise { + return this.get(name).wait_for(ms, this.acceptor_, this.createDisolver()); + } - public count_down(name: string, n: number): Promise - { - return this.get(name).count_down(n); - } + public count_down(name: string, n: number): Promise { + return this.get(name).count_down(n); + } - public arrive_and_wait(name: string): Promise - { - return this.get(name).arrive_and_wait(this.acceptor_, this.createDisolver()); - } -} \ No newline at end of file + public arrive_and_wait(name: string): Promise { + return this.get(name).arrive_and_wait( + this.acceptor_, + this.createDisolver(), + ); + } +} diff --git a/src/server/providers/MutexesProvider.ts b/src/server/providers/MutexesProvider.ts index cb03646..f8f2877 100644 --- a/src/server/providers/MutexesProvider.ts +++ b/src/server/providers/MutexesProvider.ts @@ -1,58 +1,59 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { ProviderBase } from "./ProviderBase"; import { GlobalMutexes } from "../global/GlobalMutexes"; /** * @internal */ -export class MutexesProvider extends ProviderBase -{ - /* --------------------------------------------------------- - WRITE - --------------------------------------------------------- */ - public lock(name: string): Promise - { - return this.get(name).lock(this.acceptor_, this.createDisolver()); - } +export class MutexesProvider extends ProviderBase< + GlobalMutexes, + undefined, + void +> { + /* --------------------------------------------------------- + WRITE + --------------------------------------------------------- */ + public lock(name: string): Promise { + return this.get(name).lock(this.acceptor_, this.createDisolver()); + } - public try_lock(name: string): Promise - { - return this.get(name).try_lock(this.acceptor_, this.createDisolver()); - } + public try_lock(name: string): Promise { + return this.get(name).try_lock(this.acceptor_, this.createDisolver()); + } - public try_lock_for(name: string, ms: number): Promise - { - return this.get(name).try_lock_for(ms, this.acceptor_, this.createDisolver()); - } + public try_lock_for(name: string, ms: number): Promise { + return this.get(name).try_lock_for( + ms, + this.acceptor_, + this.createDisolver(), + ); + } - public unlock(name: string): Promise - { - return this.get(name).unlock(this.acceptor_); - } + public unlock(name: string): Promise { + return this.get(name).unlock(this.acceptor_); + } - /* --------------------------------------------------------- - READ - --------------------------------------------------------- */ - public lock_shared(name: string): Promise - { - return this.get(name).lock_shared(this.acceptor_, this.createDisolver()); - } - public try_lock_shared(name: string): Promise - { - return this.get(name).try_lock_shared(this.acceptor_, this.createDisolver()); - } + /* --------------------------------------------------------- + READ + --------------------------------------------------------- */ + public lock_shared(name: string): Promise { + return this.get(name).lock_shared(this.acceptor_, this.createDisolver()); + } + public try_lock_shared(name: string): Promise { + return this.get(name).try_lock_shared( + this.acceptor_, + this.createDisolver(), + ); + } - public try_lock_shared_for(name: string, ms: number): Promise - { - return this.get(name).try_lock_shared_for(ms, this.acceptor_, this.createDisolver()); - } + public try_lock_shared_for(name: string, ms: number): Promise { + return this.get(name).try_lock_shared_for( + ms, + this.acceptor_, + this.createDisolver(), + ); + } - public unlock_shared(name: string): Promise - { - return this.get(name).unlock_shared(this.acceptor_); - } -} \ No newline at end of file + public unlock_shared(name: string): Promise { + return this.get(name).unlock_shared(this.acceptor_); + } +} diff --git a/src/server/providers/ProviderBase.ts b/src/server/providers/ProviderBase.ts index d761c04..021d15a 100644 --- a/src/server/providers/ProviderBase.ts +++ b/src/server/providers/ProviderBase.ts @@ -3,10 +3,8 @@ * @module msv */ //----------------------------------------------------------- -import { WebAcceptor } from "tgrid/protocols/web/WebAcceptor"; - -import { HashSet } from "tstl/container/HashSet"; -import { List } from "tstl/container/List"; +import { WebAcceptor } from "tgrid"; +import { List, HashSet } from "tstl"; import { GlobalBase } from "../global/GlobalBase"; import { Joiner } from "../components/internal/Joiner"; @@ -15,59 +13,66 @@ import { OutOfRange } from "tstl/exception/OutOfRange"; /** * @internal */ -export abstract class ProviderBase, Param, Ret> -{ - protected readonly global_: GlobalT; - protected readonly acceptor_: WebAcceptor; - private readonly disolvers_: List; +export abstract class ProviderBase< + GlobalT extends GlobalBase, + Param, + Ret, +> { + protected readonly global_: GlobalT; + protected readonly acceptor_: WebAcceptor; + private readonly disolvers_: List; - private readonly names_: HashSet; - private readonly remote_name_: string; + private readonly names_: HashSet; + private readonly remote_name_: string; - /* --------------------------------------------------------- - CONSTRUCTORS - --------------------------------------------------------- */ - public constructor(global: GlobalT, acceptor: WebAcceptor, disolvers: List, remoteName: string) - { - this.global_ = global; - this.acceptor_ = acceptor; - this.disolvers_ = disolvers; + /* --------------------------------------------------------- + CONSTRUCTORS + --------------------------------------------------------- */ + public constructor( + global: GlobalT, + acceptor: WebAcceptor, + disolvers: List, + remoteName: string, + ) { + this.global_ = global; + this.acceptor_ = acceptor; + this.disolvers_ = disolvers; - this.names_ = new HashSet(); - this.remote_name_ = remoteName; - } + this.names_ = new HashSet(); + this.remote_name_ = remoteName; + } - public destructor(): void - { - for (let name of this.names_) - this.erase(name); - } + public destructor(): void { + for (let name of this.names_) this.erase(name); + } - protected createDisolver(): List.Iterator - { - return this.disolvers_.insert(this.disolvers_.end(), undefined); - } + protected createDisolver(): List.Iterator { + return this.disolvers_.insert(this.disolvers_.end(), undefined); + } - /* --------------------------------------------------------- - ACCESSORS - --------------------------------------------------------- */ - public emplace(name: string, param: Param): Ret - { - this.names_.insert(name); - return this.global_.emplace(name, param, this.acceptor_); - } + /* --------------------------------------------------------- + ACCESSORS + --------------------------------------------------------- */ + public emplace(name: string, param: Param): Ret { + this.names_.insert(name); + return this.global_.emplace(name, param, this.acceptor_); + } - public erase(name: string): void - { - this.names_.erase(name); - this.global_.erase(name, this.acceptor_); - } + public erase(name: string): void { + this.names_.erase(name); + this.global_.erase(name, this.acceptor_); + } - public get(name: string): GlobalT extends GlobalBase ? ComponentT : unknown - { - if (this.names_.has(name) === false) - throw new OutOfRange(`Error on ${this.remote_name_}: you've already erased the component.`); + public get( + name: string, + ): GlobalT extends GlobalBase + ? ComponentT + : unknown { + if (this.names_.has(name) === false) + throw new OutOfRange( + `Error on ${this.remote_name_}: you've already erased the component.`, + ); - return this.global_.get(name); - } -} \ No newline at end of file + return this.global_.get(name); + } +} diff --git a/src/server/providers/SemaphoresProvider.ts b/src/server/providers/SemaphoresProvider.ts index a27bbeb..c73b51b 100644 --- a/src/server/providers/SemaphoresProvider.ts +++ b/src/server/providers/SemaphoresProvider.ts @@ -1,33 +1,31 @@ -/** - * @packageDocumentation - * @module msv - */ -//----------------------------------------------------------- import { ProviderBase } from "./ProviderBase"; import { GlobalSemaphores } from "../global/GlobalSemaphores"; /** * @internal */ -export class SemaphoresProvider extends ProviderBase -{ - public acquire(name: string): Promise - { - return this.get(name).acquire(this.acceptor_, this.createDisolver()); - } +export class SemaphoresProvider extends ProviderBase< + GlobalSemaphores, + number, + number +> { + public acquire(name: string): Promise { + return this.get(name).acquire(this.acceptor_, this.createDisolver()); + } - public try_acquire(name: string): Promise - { - return this.get(name).try_acquire(this.acceptor_, this.createDisolver()); - } + public try_acquire(name: string): Promise { + return this.get(name).try_acquire(this.acceptor_, this.createDisolver()); + } - public try_acquire_for(name: string, ms: number): Promise - { - return this.get(name).try_acquire_for(ms, this.acceptor_, this.createDisolver()); - } + public try_acquire_for(name: string, ms: number): Promise { + return this.get(name).try_acquire_for( + ms, + this.acceptor_, + this.createDisolver(), + ); + } - public release(name: string, n: number): Promise - { - return this.get(name).release(n, this.acceptor_); - } -} \ No newline at end of file + public release(name: string, n: number): Promise { + return this.get(name).release(n, this.acceptor_); + } +} diff --git a/src/test/condition_variables/test_condition_variable_disconnections.ts b/src/test/condition_variables/test_condition_variable_disconnections.ts deleted file mode 100644 index 7bf5dbb..0000000 --- a/src/test/condition_variables/test_condition_variable_disconnections.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { ConnectionFactory } from "../internal/ConnectionFactory"; -import { sleep_for } from "tstl/thread/global"; -import { IPointer } from "tstl/functional/IPointer"; - -async function wait_and_disconnect(factory: ConnectionFactory, ptr: IPointer): Promise -{ - let connector = await factory(); - let cv = await connector.getConditionVariable("test_condition_variable_disconnections"); - - cv.wait().then(() => ++ptr.value).catch(() => {}); - await sleep_for(50); - await connector.close(); -} - -export async function test_condition_variable_disconnections(factory: ConnectionFactory): Promise -{ - let connector = await factory(); - let cv = await connector.getConditionVariable("test_condition_variable_disconnections"); - let ptr: IPointer = { value: 0 }; - - let promises: Promise[] = []; - for (let i: number = 0; i < 4; ++i) - promises.push( wait_and_disconnect(factory, ptr) ); - await Promise.all(promises); - - cv.wait().then(() => ++ptr.value); - await cv.notify_all(); - await sleep_for(50); - - if (ptr.value !== 1) - throw new Error("Error on RemoteConditionVariable.wait(): disconnection does not cancel the wait."); -} - diff --git a/src/test/condition_variables/test_condition_variable_waits.ts b/src/test/condition_variables/test_condition_variable_waits.ts deleted file mode 100644 index 2238be9..0000000 --- a/src/test/condition_variables/test_condition_variable_waits.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { ConnectionFactory } from "../internal/ConnectionFactory"; -import { sleep_for } from "tstl/thread/global"; - -const SLEEP_TIME: number = 100; -const WAIT_COUNT: number = 10; - -export async function test_condition_variable_waits(factory: ConnectionFactory): Promise -{ - let connector = await factory(); - let cv = await connector.getConditionVariable("test_condition_variable_waits"); - let wait_count: number = 0; - - //---- - // WAIT & NOTIFY - //---- - // THERE'RE 10 WAITERS; HOLDERS - for (let i: number = 0; i < WAIT_COUNT; ++i) - { - cv.wait().then(() => - { - --wait_count; - }); - ++wait_count; - } - - // NOTIFY ONE - cv.notify_one(); - await sleep_for(SLEEP_TIME); - - if (wait_count !== WAIT_COUNT - 1) - throw new Error("Bug on ConditionVariable.notify_one()."); - - // NOTIFY ALL - cv.notify_all(); - await sleep_for(SLEEP_TIME); - - if (wait_count !== 0) - throw new Error("Bug on ConditionVariable.notify_all()."); - - //---- - // WAIT FOR & NOTIFY - //---- - let success_count: number = 0; - - // THERE'RE 10 WAITERS, HOLDERS, WITH DIFFERENT TIMES - for (let i: number = 0; i < WAIT_COUNT; ++i) - { - cv.wait_for(i * SLEEP_TIME).then(ret => - { - if (ret === true) - ++success_count; - }); - } - - // NOTIFY ONE - await cv.notify_one(); - - // NOTIFY ALL WHEN BE HALT TIME - await sleep_for(WAIT_COUNT * SLEEP_TIME / 2.0); - cv.notify_all(); - - // VALIDATE SUCCESS COUNT - await sleep_for(SLEEP_TIME); - if (success_count < 3 || success_count > 7) - throw new Error("Bug on ConditionVariable.wait_for(): it does not work in exact time."); -} \ No newline at end of file diff --git a/src/test/index.ts b/src/test/index.ts deleted file mode 100644 index adc0c01..0000000 --- a/src/test/index.ts +++ /dev/null @@ -1,130 +0,0 @@ -import * as fs from "fs"; - -import { IActivation } from "./internal/IActivation"; -import { ConnectionFactory } from "./internal/ConnectionFactory"; - -import { MutexServer } from "../MutexServer"; -import { MutexConnector } from "../MutexConnector"; - -const EXTENSION = __filename.substr(-2); -if (EXTENSION === "js") - require("source-map-support").install(); - -const PORT = 44994; -const URL = `ws://127.0.0.1:${PORT}`; -const HEADER = { password: "some_password" }; - -interface IModule -{ - [key: string]: (factory: ConnectionFactory, server: MutexServer) => Promise; -} - -async function measure(job: () => Promise): Promise -{ - let time: number = Date.now(); - await job(); - return Date.now() - time; -} - -async function iterate(factory: ConnectionFactory, server: MutexServer, path: string): Promise -{ - let fileList: string[] = await fs.promises.readdir(path); - for (let file of fileList) - { - let currentPath: string = `${path}/${file}`; - let stats: fs.Stats = await fs.promises.lstat(currentPath); - - if (stats.isDirectory() === true && file !== "internal" && file !== "manual") - { - await iterate(factory, server, currentPath); - continue; - } - else if (file.substr(-3) !== `.${EXTENSION}` || currentPath === `${__dirname}/index.${EXTENSION}`) - continue; - - let external: IModule = await import(currentPath.substr(0, currentPath.length - 3)); - for (let key in external) - { - if (key.substr(0, 5) !== "test_") - continue; - else if (process.argv[2] && key.indexOf(process.argv[2]) === -1) - continue; - - process.stdout.write(` - ${key}`); - let time: number = await measure(() => external[key](factory, server)); - console.log(`: ${time} ms`); - } - } -} - -async function main(): Promise -{ - //---- - // PREPARE ASSETS - //---- - // PRINT TITLE - console.log("=========================================================="); - console.log(" Mutex Server - Test Automation Program "); - console.log("=========================================================="); - - // OPEN SERVER - let server: MutexServer = new MutexServer(); - await server.open(PORT, async acceptor => - { - if (acceptor.header.password === HEADER.password) - await acceptor.accept(null); - else - await acceptor.reject(); - }); - - // CONNECTION-FACTORY TO THE SERVER - let sequence: number = 0; - let connectorList: MutexConnector[] = []; - - let factory: ConnectionFactory = async () => - { - let connector: MutexConnector = new MutexConnector({ - uid: ++sequence, - ...HEADER - }, null); - await connector.connect(URL); - - connectorList.push(connector); - return connector; - }; - - //---- - // TEST AUTOMATION - //---- - // DO TEST WITH ELAPSED TIME - let time: number = await measure(() => iterate(factory, server, __dirname)); - - // PRINT ELAPSED TIME - console.log("----------------------------------------------------------"); - console.log("Success"); - console.log(` - elapsed time: ${time} ms`); - - // MEMORY USAGE - let memory: NodeJS.MemoryUsage = process.memoryUsage(); - for (let property in memory) - { - let amount: number = memory[property as keyof NodeJS.MemoryUsage] / 10**6; - console.log(` - ${property}: ${amount} MB`); - } - console.log("----------------------------------------------------------\n"); - - //---- - // TERMINATE - //---- - for (let connector of connectorList) - if (connector.state === MutexConnector.State.OPEN) - await connector.close(); - - await server.close(); -} -main().catch(exp => -{ - process.stdout.write("\n"); - console.log(exp); - process.exit(-1); -}); \ No newline at end of file diff --git a/src/test/internal/ConnectionFactory.ts b/src/test/internal/ConnectionFactory.ts deleted file mode 100644 index 9089e10..0000000 --- a/src/test/internal/ConnectionFactory.ts +++ /dev/null @@ -1,7 +0,0 @@ -import { MutexConnector } from "../../MutexConnector"; -import { IActivation } from "./IActivation"; - -export interface ConnectionFactory -{ - (): Promise>; -} \ No newline at end of file diff --git a/src/test/internal/IActivation.ts b/src/test/internal/IActivation.ts deleted file mode 100644 index fe71346..0000000 --- a/src/test/internal/IActivation.ts +++ /dev/null @@ -1,5 +0,0 @@ -export interface IActivation -{ - uid: number; - password: string; -} \ No newline at end of file diff --git a/src/test/internal/Validator.ts b/src/test/internal/Validator.ts deleted file mode 100644 index df20388..0000000 --- a/src/test/internal/Validator.ts +++ /dev/null @@ -1,198 +0,0 @@ -import { ILockable } from "tstl/base/thread/ILockable"; -import { ITimedLockable } from "tstl/base/thread/ITimedLockable"; -import { ISharedLockable } from "tstl/base/thread/ISharedLockable"; -import { ISharedTimedLockable } from "tstl/base/thread/ISharedTimedLockable"; - -import { sleep_for } from "tstl/thread/global"; - -export namespace Validator -{ - const SLEEP_TIME = 50; - const READ_COUNT = 10; - - /* --------------------------------------------------------- - WRITE LOCK - --------------------------------------------------------- */ - export async function lock(mutex: ILockable): Promise - { - let start: number = Date.now(); - - // LOCK FOR A SECOND - await mutex.lock(); - sleep_for(SLEEP_TIME).then(() => mutex.unlock()); - - // TRY LOCK AGAIN - await mutex.lock(); - let elapsed: number = Date.now() - start; - await mutex.unlock(); - - if (elapsed < SLEEP_TIME * .95) - throw new Error(`Error on ${mutex.constructor.name}.lock() & unlock(): it does not work in exact time.`); - } - - export async function try_lock(mtx: ITimedLockable, name: string = mtx.constructor.name): Promise - { - let start: number = Date.now(); - - // DO LOCK - let ret: boolean = await mtx.try_lock_for(SLEEP_TIME); - if (ret === false) - throw new Error(`Bug on ${name}.try_lock_for(): it does not return exact value`); - - // TRY LOCK AGAIN - ret = await mtx.try_lock_for(SLEEP_TIME); - let elapsed: number = Date.now() - start; - - if (ret === true) - throw new Error(`Bug on ${name}.try_lock_for(): it does not return exact value`); - else if (elapsed < SLEEP_TIME * .95) - throw new Error(`Bug on ${name}.try_lock_for(): it does not work in exact time`); - - await mtx.unlock(); - } - - /* --------------------------------------------------------- - READ LOCK - --------------------------------------------------------- */ - export async function lock_shared(mtx: ILockable & ISharedLockable): Promise - { - //---- - // READ SIMULTANEOUSLY - //---- - // READ LOCK; 10 TIMES - let read_count: number = 0; - for (let i: number = 0; i < READ_COUNT; ++i) - { - mtx.lock_shared(); - ++read_count; - } - if (read_count !== READ_COUNT) // READ LOCK CAN BE DONE SIMULTANEOUSLY - throw new Error(`Bug on ${mtx.constructor.name}.lock_shared(): it doesn't support the simultaneous lock.`); - - //---- - // READ FIRST, WRITE LATER - //---- - let start_time: number = Date.now(); - sleep_for(SLEEP_TIME).then(() => - { - // SLEEP FOR A SECOND AND UNLOCK ALL READINGS - for (let i: number = 0; i < READ_COUNT; ++i) - mtx.unlock_shared(); - }); - - // DO WRITE LOCK; MUST BE BLOCKED - await mtx.lock(); - - // VALIDATE ELAPSED TIME - let elapsed_time: number = Date.now() - start_time; - if (elapsed_time < SLEEP_TIME * .95) - throw new Error(`Bug on ${mtx.constructor.name}.lock(): it does not block writing while reading.`); - - //---- - // WRITE FIRST, READ LATER - //---- - start_time = Date.now(); - - // SLEEP FOR A SECOND AND UNLOCK WRITINGS - sleep_for(SLEEP_TIME).then(() => mtx.unlock()); - for (let i: number = 0; i < READ_COUNT; ++i) - await mtx.lock_shared(); - - // VALIDATE ELAPSED TIME - elapsed_time = Date.now() - start_time; - if (elapsed_time < SLEEP_TIME * .95) - throw new Error(`Bug on ${mtx.constructor.name}.lock_shared(): it does not block reading while writing.`); - - // RELEASE READING LOCK FOR THE NEXT STEP - for (let i: number = 0; i < READ_COUNT; ++i) - await mtx.unlock_shared(); - } - - export async function try_lock_shared(mtx: ITimedLockable & ISharedTimedLockable): Promise - { - let start: number; - let elapsed: number; - let flag: boolean; - - //---- - // READ SIMULTANEOUSLY - //---- - start = Date.now(); - - // READ LOCK; 10 TIMES - for (let i: number = 0; i < READ_COUNT; ++i) - { - flag = await mtx.try_lock_shared_for(SLEEP_TIME); - if (flag === false) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not return exact value.`); - } - - // VALIDATE ELAPSED TIME - elapsed = Date.now() - start; - if (elapsed >= SLEEP_TIME) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not support simultaneous lock.`); - - //---- - // WRITE LOCK - //---- - // TRY WRITE LOCK ON READING - start = Date.now(); - flag = await mtx.try_lock_for(SLEEP_TIME); - elapsed = Date.now() - start; - - if (flag === true) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_for(): it does not return exact value while reading.`); - else if (elapsed < SLEEP_TIME * .95) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_for(): it does not block while reading.`); - - // TRY WRITE LOCK AFTER READING - sleep_for(SLEEP_TIME).then(() => - { - for (let i: number = 0; i < READ_COUNT; ++i) - mtx.unlock_shared(); - }); - start = Date.now(); - flag = await mtx.try_lock_for(SLEEP_TIME * 1.5); - elapsed = Date.now() - start; - - if (flag === false) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_for(): it does not return exact value while reading.`); - else if (elapsed < SLEEP_TIME * .95) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_for(): it does not work in exact time.`); - - //---- - // READ LOCK - //---- - // READ LOCK ON WRITING - start = Date.now(); - for (let i: number = 0; i < READ_COUNT; ++i) - { - flag = await mtx.try_lock_shared_for(SLEEP_TIME); - if (flag === true) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not return exact value while writing.`); - } - elapsed = Date.now() - start; - - if (elapsed < SLEEP_TIME * READ_COUNT * .95) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not work in exact time.`); - - // READ LOCK AFTER WRITING - start = Date.now(); - sleep_for(SLEEP_TIME).then(() => mtx.unlock()); - - for (let i: number = 0; i < READ_COUNT; ++i) - { - flag = await mtx.try_lock_shared_for(SLEEP_TIME * 1.5); - if (flag === false) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not return exact value after writing.`); - } - elapsed = Date.now() - start; - - if (elapsed < SLEEP_TIME * .95 || elapsed >= SLEEP_TIME * 5.0) - throw new Error(`Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not work in exact time.`); - - // RELEASE READING LOCK FOR THE NEXT STEP - for (let i: number = 0; i < READ_COUNT; ++i) - await mtx.unlock_shared(); - } -} \ No newline at end of file diff --git a/src/test/manual/remote-mutex.ts b/src/test/manual/remote-mutex.ts deleted file mode 100644 index 1bd070c..0000000 --- a/src/test/manual/remote-mutex.ts +++ /dev/null @@ -1,57 +0,0 @@ -import msv from "../../index"; -import std from "tstl"; - -const PASSWORD = "qweqwe123!"; -const PORT = 37119; - -async function client(index: number, character: string): Promise -{ - // CONNECT TO THE SERVER - let connector: msv.MutexConnector = new msv.MutexConnector(PASSWORD, null); - await connector.connect(`ws://127.0.0.1:${PORT}`); - - // GET LOCK - let mutex: msv.RemoteMutex = await connector.getMutex("printer"); - await mutex.lock(); - - // PRINTS A LINE VERY SLOWLY MONOPOLYING THE MUTEX - process.stdout.write(`Connector #${index} is monopolying a mutex: `); - for (let i: number = 0; i < 20; ++i) - { - process.stdout.write(character); - await std.sleep_for(50); - } - process.stdout.write("\n"); - - // ALTHOUGH THE CLIENT DOES NOT RELEASE THE LOCK - if (Math.random() < 0.5) - await mutex.unlock(); - else // SERVER WILL UNLOCK IT AUTOMATICALLY AFTER THE DISCONNECTION - await connector.close(); -} - -async function main(): Promise -{ - // OPEN SERVER - let server: msv.MutexServer = new msv.MutexServer(); - await server.open(PORT, async acceptor => - { - if (acceptor.header === PASSWORD) - await acceptor.accept(null); - else - await acceptor.reject(); - }); - - // CREATE 10 CLIENTS LOCKING MUTEX - let promises: Promise[] = []; - for (let i: number = 0; i < 4; ++i) - { - let character: string = std.randint(0, 9).toString(); - promises.push( client(i + 1, character) ); - } - - // WAIT THE CLIENTS TO BE DISCONNCTED AND CLOSE SERVER - await Promise.all(promises); - await server.close(); -} -main(); \ No newline at end of file diff --git a/src/test/mutextes/test_mutex_disconnections.ts b/src/test/mutextes/test_mutex_disconnections.ts deleted file mode 100644 index 81a19a3..0000000 --- a/src/test/mutextes/test_mutex_disconnections.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { IActivation } from "../internal/IActivation"; -import { ConnectionFactory } from "../internal/ConnectionFactory"; -import { MutexConnector } from "../../MutexConnector"; - -import { RemoteMutex } from "../../client/RemoteMutex"; -import { sleep_for } from "tstl/thread/global"; - -async function test_disconnection(factory: ConnectionFactory, index: number): Promise -{ - let connector: MutexConnector = await factory(); - let mutex: RemoteMutex = await connector.getMutex("test_mutex_disconnection"); - - if (index % 2 === 0) - { - if (await mutex.try_lock_for(SLEEP * COUNT * 20) === false) - throw new Error("Error on RemoteMutex.try_lock_for(): disconnected clients do not return their acquisitions."); - } - else - mutex.try_lock_for(SLEEP * COUNT * 20) - .catch(() => {}); - - await sleep_for(SLEEP * index); - await connector.close(); -} - -export async function test_mutex_disconnections(factory: ConnectionFactory): Promise -{ - let promises: Promise[] = []; - for (let i: number = 0; i < COUNT; ++i) - promises.push( test_disconnection(factory, i) ); - - await Promise.all(promises); -} - -const COUNT = 4; -const SLEEP = 10; \ No newline at end of file diff --git a/src/test/mutextes/test_mutex_locks.ts b/src/test/mutextes/test_mutex_locks.ts deleted file mode 100644 index da4ca36..0000000 --- a/src/test/mutextes/test_mutex_locks.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { Pair } from "tstl/utility/Pair"; -import { sleep_for } from "tstl/thread/global"; - -import { IActivation } from "../internal/IActivation"; -import { MutexConnector } from "../../MutexConnector"; -import { RemoteMutex } from "../../client/RemoteMutex"; - -import { ConnectionFactory } from "../internal/ConnectionFactory"; -import { Validator } from "../internal/Validator"; - -const enum Status -{ - START_READING = "Start Reading", - END_READING = "End Reading", - START_WRITING = "Start Writing", - END_WRITING = "End Writing" -} -const MAGNIFIER: number = 3; - -export async function test_mutex_locks(factory: ConnectionFactory): Promise -{ - let connector: MutexConnector = await factory(); - let mutex: RemoteMutex = await connector.getMutex("remote_mutex"); - - // TEST COMMON FEATURES - await Validator.lock(mutex); - await Validator.try_lock(mutex); - await Validator.lock_shared(mutex); - await Validator.try_lock_shared(mutex); - - // @todo: must be removed - if (1 === 1) - return; - - // TEST SPECIAL FEATURES - let statusList: Pair[] = []; - - try - { - let promises: Promise[] = []; - for (let i: number = 0; i < 25; ++i) - promises.push(read(mutex, statusList)); - promises.push(write(mutex, statusList)); - - await Promise.all(promises); - - let reading: number = 0; - let writing: number = 0; - - for (let i: number = 0; i < statusList.length; ++i) - { - let status: Status = statusList[i].first; - - if (status === Status.START_READING) - ++reading; - else if (status === Status.START_WRITING) - ++writing; - else if (status === Status.END_READING) - --reading; - else - --writing; - - if (writing > 0 && reading > 0) - throw new Error(`Bug on SharedTimeMutex; reading and writing at the same time at ${i}`); - } - } - catch (exp) - { - for (let pair of statusList) - console.log(pair.first, pair.second); - throw exp; - } -} - -async function write(mutex: RemoteMutex, statusList: Pair[]): Promise -{ - for (let i: number = 0; i < MAGNIFIER * 10; ++i) - { - // JUST DELAY FOR SAFETY - await sleep_for(100); - let time: number = Date.now(); - - // DO WRITE - await mutex.lock(); - { - let now: number = Date.now(); - statusList.push(new Pair(Status.START_WRITING, now - time)); - - await sleep_for(50); - statusList.push(new Pair(Status.END_WRITING, Date.now() - now)); - } - await mutex.unlock(); - } -} - -async function read(mutex: RemoteMutex, statusList: Pair[]): Promise -{ - for (let i: number = 0; i < MAGNIFIER * 100; ++i) - { - let time: number = Date.now(); - - // DO READ - await mutex.lock_shared(); - { - let now: number = Date.now(); - statusList.push(new Pair(Status.START_READING, now - time)); - - await sleep_for(10); - statusList.push(new Pair(Status.END_READING, Date.now() - now)); - } - await mutex.unlock_shared(); - } -} \ No newline at end of file diff --git a/src/test/semaphores/test_semaphore_acquires.ts b/src/test/semaphores/test_semaphore_acquires.ts deleted file mode 100644 index 4140435..0000000 --- a/src/test/semaphores/test_semaphore_acquires.ts +++ /dev/null @@ -1,104 +0,0 @@ -import { ConnectionFactory } from "../internal/ConnectionFactory"; -import { MutexConnector } from "../../MutexConnector"; -import { RemoteSemaphore } from "../../client/RemoteSemaphore"; - -import { IActivation } from "../internal/IActivation"; -import { ITimedLockable } from "tstl/base/thread/ITimedLockable"; -import { Validator } from "../internal/Validator"; -import { sleep_for } from "tstl/thread/global"; - -const SIZE = 8; - -export async function test_semaphore_acquires(factory: ConnectionFactory): Promise -{ - //---- - // TEST MUTEX FEATURES - //---- - let connector: MutexConnector = await factory(); - let mutex: RemoteSemaphore = await connector.getSemaphore("test_semaphore_acquires_binary", 1); - let wrapper: ITimedLockable = RemoteSemaphore.get_lockable(mutex); - - await Validator.lock(wrapper); - await Validator.try_lock(wrapper); - - //---- - // TEST SPECIAL FEATURES OF SEMAPHORE - //---- - let semaphore: RemoteSemaphore = await connector.getSemaphore("test_semaphore_acquires_counting", SIZE); - - await _Test_semaphore(semaphore); - await _Test_timed_semaphore(semaphore); -} - -async function _Test_semaphore(s: RemoteSemaphore): Promise -{ - let acquired_count: number = 0; - - // LOCK 4 TIMES - for (let i: number = 0; i < await s.max(); ++i) - { - await s.acquire(); - ++acquired_count; - } - if (acquired_count !== await s.max()) - throw new Error(`Bug on Semaphore.acquire()`); - else if (await s.try_acquire() === true) - throw new Error(`Bug on Semaphore.try_acquire()`); - - // LOCK 4 TIMES AGAIN -> THEY SHOULD BE HOLD - for (let i: number = 0; i < await s.max(); ++i) - s.acquire().then(() => - { - ++acquired_count; - }); - if (acquired_count !== await s.max()) - throw new Error(`Bug on Semaphore.acquire() when Semaphore is full`); - - // DO UNLOCK - await s.release(await s.max()); - - if (acquired_count !== 2 * await s.max()) - throw new Error(`Bug on Semaphore.release()`); - - // RELEASE UNRESOLVED LOCKS - await sleep_for(0); - await s.release(await s.max()); -} - -async function _Test_timed_semaphore(ts: RemoteSemaphore): Promise -{ - // TRY LOCK FIRST - for (let i: number = 0; i < await ts.max(); ++i) - { - let flag: boolean = await ts.try_acquire_for(0); - if (flag === false) - throw new Error("Bug on TimedSemaphore.try_lock_for(); failed to lock when clear"); - } - - // TRY LOCK FOR -> MUST BE FAILED - if (await ts.try_acquire_for(50) === true) - throw new Error("Bug on TimedSemaphore.try_lock_for(); succeeded to lock when must be failed."); - - // LOCK WOULD BE HOLD - let cnt: number = 0; - for (let i: number = 0; i < await ts.max() / 2; ++i) - ts.acquire().then(() => - { - ++cnt; - }); - - await sleep_for(100); - if (cnt === await ts.max() / 2) - throw new Error("Bug on TimedSemaphore.try_lock_for(); failed to release holdings."); - - // RELEASE AND LOCK - await ts.release(await ts.max()); - - for (let i: number = 0; i < await ts.max() / 2; ++i) - { - let flag: boolean = await ts.try_acquire_for(100); - if (flag === false) - throw new Error("Bug on TimedSemaphore.try_lock_for(); failed to lock when released."); - } - await ts.release(await ts.max()); -} \ No newline at end of file diff --git a/src/test/semaphores/test_semaphore_disconnections.ts b/src/test/semaphores/test_semaphore_disconnections.ts deleted file mode 100644 index 0c489b9..0000000 --- a/src/test/semaphores/test_semaphore_disconnections.ts +++ /dev/null @@ -1,55 +0,0 @@ -import { ConnectionFactory } from "../internal/ConnectionFactory"; -import { MutexConnector } from "../../MutexConnector"; -import { IActivation } from "../internal/IActivation"; - -import { RemoteSemaphore } from "../../client/RemoteSemaphore"; -import { sleep_for } from "tstl/thread/global"; - -const MAX = 8; -const SLEEP = 50; - -async function acquire_and_disconnect(factory: ConnectionFactory, ms: number): Promise -{ - let connector: MutexConnector = await factory(); - let semaphore: RemoteSemaphore = await connector.getSemaphore("test_semaphore_disconnections", 0); - - if (await semaphore.max() !== MAX) - throw new Error("Error on RemoteSemaphore.max(): invalid value."); - else if (await semaphore.try_acquire() === false) - throw new Error("Error on RemoteSemaphore.try_acquire(): must be true but returns false."); - - sleep_for(ms).then(() => connector.close()); -} - -export async function test_semaphore_disconnections(factory: ConnectionFactory): Promise -{ - let connector: MutexConnector = await factory(); - let semaphore: RemoteSemaphore = await connector.getSemaphore("test_semaphore_disconnections", MAX); - - for (let i: number = 0; i < MAX / 2; ++i) - if (await semaphore.try_acquire() === false) - throw new Error("Error on RemoteSemaphore.try_acquire(): must be true but returns false."); - - for (let i: number = 0; i < MAX / 2; ++i) - await acquire_and_disconnect(factory, SLEEP); - - if (await semaphore.try_acquire() === true) - throw new Error("Error on RemoteSemaphore.try_acquire(): must be false but returns true."); - - let promises: Promise[] = []; - let count: number = 0; - - for (let i: number = 0; i < MAX / 2; ++i) - promises.push(semaphore.try_acquire_for(SLEEP * 1.2).then(() => { - ++count; - })); - - if (count !== 0) - throw new Error("Error on RemoteSemaphore.try_acquire_for(): succeded too early."); - - await Promise.all(promises); - if (count !== MAX / 2) - throw new Error("Error on RemoteSemaphore.try_acquire_for(): must be succeeded but failed."); - - await semaphore.release(4); -} \ No newline at end of file diff --git a/src/test/semaphores/test_semaphore_simple.ts b/src/test/semaphores/test_semaphore_simple.ts deleted file mode 100644 index 424707c..0000000 --- a/src/test/semaphores/test_semaphore_simple.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { ConnectionFactory } from "../internal/ConnectionFactory"; - -export async function test_semaphore_simple(factory: ConnectionFactory): Promise -{ - let connector = await factory(); - let semaphore = await connector.getSemaphore("test_sema2", 4); - - for (let i: number = 0; i < 4; ++i) - await semaphore.acquire(); - - let promises: Promise[] = []; - let count: number = 0; - - for (let i: number = 0; i < 4; ++i) - promises.push(semaphore.acquire().then(() => { ++count })); - - await semaphore.release(4); - await Promise.all(promises); - - if (count !== 4) - throw new Error("Error on RemoteSemaphore.release(4)"); -} \ No newline at end of file diff --git a/src/test/test_destructors.ts b/src/test/test_destructors.ts deleted file mode 100644 index f09c8c4..0000000 --- a/src/test/test_destructors.ts +++ /dev/null @@ -1,32 +0,0 @@ -import { IActivation } from "./internal/IActivation"; -import { ConnectionFactory } from "./internal/ConnectionFactory"; -import { MutexServer } from "../MutexServer"; -import { MutexConnector } from "../MutexConnector"; - -import { RemoteMutex } from "../client/RemoteMutex"; -import { sleep_for } from "tstl/thread/global"; - -async function test(factory: ConnectionFactory): Promise -{ - let connector: MutexConnector = await factory(); - let mutex: RemoteMutex = await connector.getMutex("test_destructors"); - - await mutex.lock_shared(); - await sleep_for(50); - - // mutex.unlock_shared() would be automatically called - await connector.close(); -} - -export async function test_destructors(factory: ConnectionFactory, server: MutexServer): Promise -{ - let promises: Promise[] = []; - for (let i: number = 0; i < 4; ++i) - promises.push( test(factory) ); - - await Promise.all(promises); - await sleep_for(50); - - if (server["components_"].mutexes["dict_"].has("test_destructors") === true) - throw new Error("Destructor is not working."); -} \ No newline at end of file diff --git a/test/condition_variables/test_condition_variable_disconnections.ts b/test/condition_variables/test_condition_variable_disconnections.ts new file mode 100644 index 0000000..9ab7140 --- /dev/null +++ b/test/condition_variables/test_condition_variable_disconnections.ts @@ -0,0 +1,43 @@ +import { ConnectionFactory } from "../internal/ConnectionFactory"; +import { sleep_for } from "tstl/thread/global"; +import { IPointer } from "tstl/functional/IPointer"; + +async function wait_and_disconnect( + factory: ConnectionFactory, + ptr: IPointer, +): Promise { + let connector = await factory(); + let cv = await connector.getConditionVariable( + "test_condition_variable_disconnections", + ); + + cv.wait() + .then(() => ++ptr.value) + .catch(() => {}); + await sleep_for(50); + await connector.close(); +} + +export async function test_condition_variable_disconnections( + factory: ConnectionFactory, +): Promise { + let connector = await factory(); + let cv = await connector.getConditionVariable( + "test_condition_variable_disconnections", + ); + let ptr: IPointer = { value: 0 }; + + let promises: Promise[] = []; + for (let i: number = 0; i < 4; ++i) + promises.push(wait_and_disconnect(factory, ptr)); + await Promise.all(promises); + + cv.wait().then(() => ++ptr.value); + await cv.notify_all(); + await sleep_for(50); + + if (ptr.value !== 1) + throw new Error( + "Error on RemoteConditionVariable.wait(): disconnection does not cancel the wait.", + ); +} diff --git a/test/condition_variables/test_condition_variable_waits.ts b/test/condition_variables/test_condition_variable_waits.ts new file mode 100644 index 0000000..c524098 --- /dev/null +++ b/test/condition_variables/test_condition_variable_waits.ts @@ -0,0 +1,66 @@ +import { ConnectionFactory } from "../internal/ConnectionFactory"; +import { sleep_for } from "tstl/thread/global"; + +const SLEEP_TIME: number = 100; +const WAIT_COUNT: number = 10; + +export async function test_condition_variable_waits( + factory: ConnectionFactory, +): Promise { + let connector = await factory(); + let cv = await connector.getConditionVariable( + "test_condition_variable_waits", + ); + let wait_count: number = 0; + + //---- + // WAIT & NOTIFY + //---- + // THERE'RE 10 WAITERS; HOLDERS + for (let i: number = 0; i < WAIT_COUNT; ++i) { + cv.wait().then(() => { + --wait_count; + }); + ++wait_count; + } + + // NOTIFY ONE + cv.notify_one(); + await sleep_for(SLEEP_TIME); + + if (wait_count !== WAIT_COUNT - 1) + throw new Error("Bug on ConditionVariable.notify_one()."); + + // NOTIFY ALL + cv.notify_all(); + await sleep_for(SLEEP_TIME); + + if (wait_count !== 0) + throw new Error("Bug on ConditionVariable.notify_all()."); + + //---- + // WAIT FOR & NOTIFY + //---- + let success_count: number = 0; + + // THERE'RE 10 WAITERS, HOLDERS, WITH DIFFERENT TIMES + for (let i: number = 0; i < WAIT_COUNT; ++i) { + cv.wait_for(i * SLEEP_TIME).then((ret) => { + if (ret === true) ++success_count; + }); + } + + // NOTIFY ONE + await cv.notify_one(); + + // NOTIFY ALL WHEN BE HALT TIME + await sleep_for((WAIT_COUNT * SLEEP_TIME) / 2.0); + cv.notify_all(); + + // VALIDATE SUCCESS COUNT + await sleep_for(SLEEP_TIME); + if (success_count < 3 || success_count > 7) + throw new Error( + "Bug on ConditionVariable.wait_for(): it does not work in exact time.", + ); +} diff --git a/test/index.ts b/test/index.ts new file mode 100644 index 0000000..89e599d --- /dev/null +++ b/test/index.ts @@ -0,0 +1,130 @@ +import * as fs from "fs"; +import { MutexConnector, MutexServer } from "mutex-server"; + +import { IActivation } from "./internal/IActivation"; +import { ConnectionFactory } from "./internal/ConnectionFactory"; + +const EXTENSION = __filename.substr(-2); +if (EXTENSION === "js") require("source-map-support").install(); + +const PORT = 44994; +const URL = `ws://127.0.0.1:${PORT}`; +const HEADER = { password: "some_password" }; + +interface IModule { + [key: string]: ( + factory: ConnectionFactory, + server: MutexServer, + ) => Promise; +} + +async function measure(job: () => Promise): Promise { + let time: number = Date.now(); + await job(); + return Date.now() - time; +} + +async function iterate( + factory: ConnectionFactory, + server: MutexServer, + path: string, +): Promise { + let fileList: string[] = await fs.promises.readdir(path); + for (let file of fileList) { + let currentPath: string = `${path}/${file}`; + let stats: fs.Stats = await fs.promises.lstat(currentPath); + + if ( + stats.isDirectory() === true && + file !== "internal" && + file !== "manual" + ) { + await iterate(factory, server, currentPath); + continue; + } else if ( + file.substr(-3) !== `.${EXTENSION}` || + currentPath === `${__dirname}/index.${EXTENSION}` + ) + continue; + + let external: IModule = await import( + currentPath.substr(0, currentPath.length - 3) + ); + for (let key in external) { + if (key.substr(0, 5) !== "test_") continue; + else if (process.argv[2] && key.indexOf(process.argv[2]) === -1) continue; + + process.stdout.write(` - ${key}`); + let time: number = await measure(() => external[key](factory, server)); + console.log(`: ${time} ms`); + } + } +} + +async function main(): Promise { + //---- + // PREPARE ASSETS + //---- + // PRINT TITLE + console.log("=========================================================="); + console.log(" Mutex Server - Test Automation Program "); + console.log("=========================================================="); + + // OPEN SERVER + let server: MutexServer = new MutexServer(); + await server.open(PORT, async (acceptor) => { + if (acceptor.header.password === HEADER.password) + await acceptor.accept(null); + else await acceptor.reject(); + }); + + // CONNECTION-FACTORY TO THE SERVER + let sequence: number = 0; + let connectorList: MutexConnector[] = []; + + let factory: ConnectionFactory = async () => { + let connector: MutexConnector = new MutexConnector( + { + uid: ++sequence, + ...HEADER, + }, + null, + ); + await connector.connect(URL); + + connectorList.push(connector); + return connector; + }; + + //---- + // TEST AUTOMATION + //---- + // DO TEST WITH ELAPSED TIME + let time: number = await measure(() => iterate(factory, server, __dirname)); + + // PRINT ELAPSED TIME + console.log("----------------------------------------------------------"); + console.log("Success"); + console.log(` - elapsed time: ${time} ms`); + + // MEMORY USAGE + let memory: NodeJS.MemoryUsage = process.memoryUsage(); + for (let property in memory) { + let amount: number = memory[property as keyof NodeJS.MemoryUsage] / 10 ** 6; + console.log(` - ${property}: ${amount} MB`); + } + console.log("----------------------------------------------------------\n"); + + //---- + // TERMINATE + //---- + for (let connector of connectorList) + if (connector.state === MutexConnector.State.OPEN) await connector.close(); + + await server.close(); +} +main().catch((exp) => { + process.stdout.write("\n"); + console.log(exp); + process.exit(-1); +}); diff --git a/test/internal/ConnectionFactory.ts b/test/internal/ConnectionFactory.ts new file mode 100644 index 0000000..6302548 --- /dev/null +++ b/test/internal/ConnectionFactory.ts @@ -0,0 +1,7 @@ +import { MutexConnector } from "mutex-server"; + +import { IActivation } from "./IActivation"; + +export interface ConnectionFactory { + (): Promise>; +} diff --git a/test/internal/IActivation.ts b/test/internal/IActivation.ts new file mode 100644 index 0000000..45d3ab9 --- /dev/null +++ b/test/internal/IActivation.ts @@ -0,0 +1,4 @@ +export interface IActivation { + uid: number; + password: string; +} diff --git a/test/internal/Validator.ts b/test/internal/Validator.ts new file mode 100644 index 0000000..9617dd5 --- /dev/null +++ b/test/internal/Validator.ts @@ -0,0 +1,224 @@ +import { ILockable } from "tstl/base/thread/ILockable"; +import { ITimedLockable } from "tstl/base/thread/ITimedLockable"; +import { ISharedLockable } from "tstl/base/thread/ISharedLockable"; +import { ISharedTimedLockable } from "tstl/base/thread/ISharedTimedLockable"; + +import { sleep_for } from "tstl/thread/global"; + +export namespace Validator { + const SLEEP_TIME = 50; + const READ_COUNT = 10; + + /* --------------------------------------------------------- + WRITE LOCK + --------------------------------------------------------- */ + export async function lock(mutex: ILockable): Promise { + const start: number = Date.now(); + + // LOCK FOR A SECOND + await mutex.lock(); + sleep_for(SLEEP_TIME).then(() => mutex.unlock()); + + // TRY LOCK AGAIN + await mutex.lock(); + const elapsed: number = Date.now() - start; + await mutex.unlock(); + + if (elapsed < SLEEP_TIME * 0.95) + throw new Error( + `Error on ${mutex.constructor.name}.lock() & unlock(): it does not work in exact time.`, + ); + } + + export async function try_lock( + mtx: ITimedLockable, + name: string = mtx.constructor.name, + ): Promise { + const start: number = Date.now(); + + // DO LOCK + let ret: boolean = await mtx.try_lock_for(SLEEP_TIME); + if (ret === false) + throw new Error( + `Bug on ${name}.try_lock_for(): it does not return exact value`, + ); + + // TRY LOCK AGAIN + ret = await mtx.try_lock_for(SLEEP_TIME); + const elapsed: number = Date.now() - start; + + if (ret === true) + throw new Error( + `Bug on ${name}.try_lock_for(): it does not return exact value`, + ); + else if (elapsed < SLEEP_TIME * 0.95) + throw new Error( + `Bug on ${name}.try_lock_for(): it does not work in exact time`, + ); + + await mtx.unlock(); + } + + /* --------------------------------------------------------- + READ LOCK + --------------------------------------------------------- */ + export async function lock_shared( + mtx: ILockable & ISharedLockable, + ): Promise { + //---- + // READ SIMULTANEOUSLY + //---- + // READ LOCK; 10 TIMES + let read_count: number = 0; + for (let i: number = 0; i < READ_COUNT; ++i) { + mtx.lock_shared(); + ++read_count; + } + if (read_count !== READ_COUNT) + // READ LOCK CAN BE DONE SIMULTANEOUSLY + throw new Error( + `Bug on ${mtx.constructor.name}.lock_shared(): it doesn't support the simultaneous lock.`, + ); + + //---- + // READ FIRST, WRITE LATER + //---- + let start_time: number = Date.now(); + sleep_for(SLEEP_TIME).then(() => { + // SLEEP FOR A SECOND AND UNLOCK ALL READINGS + for (let i: number = 0; i < READ_COUNT; ++i) mtx.unlock_shared(); + }); + + // DO WRITE LOCK; MUST BE BLOCKED + await mtx.lock(); + + // VALIDATE ELAPSED TIME + let elapsed_time: number = Date.now() - start_time; + if (elapsed_time < SLEEP_TIME * 0.95) + throw new Error( + `Bug on ${mtx.constructor.name}.lock(): it does not block writing while reading.`, + ); + + //---- + // WRITE FIRST, READ LATER + //---- + start_time = Date.now(); + + // SLEEP FOR A SECOND AND UNLOCK WRITINGS + sleep_for(SLEEP_TIME).then(() => mtx.unlock()); + for (let i: number = 0; i < READ_COUNT; ++i) await mtx.lock_shared(); + + // VALIDATE ELAPSED TIME + elapsed_time = Date.now() - start_time; + if (elapsed_time < SLEEP_TIME * 0.95) + throw new Error( + `Bug on ${mtx.constructor.name}.lock_shared(): it does not block reading while writing.`, + ); + + // RELEASE READING LOCK FOR THE NEXT STEP + for (let i: number = 0; i < READ_COUNT; ++i) await mtx.unlock_shared(); + } + + export async function try_lock_shared( + mtx: ITimedLockable & ISharedTimedLockable, + ): Promise { + let start: number; + let elapsed: number; + let flag: boolean; + + //---- + // READ SIMULTANEOUSLY + //---- + start = Date.now(); + + // READ LOCK; 10 TIMES + for (let i: number = 0; i < READ_COUNT; ++i) { + flag = await mtx.try_lock_shared_for(SLEEP_TIME); + if (flag === false) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not return exact value.`, + ); + } + + // VALIDATE ELAPSED TIME + elapsed = Date.now() - start; + if (elapsed >= SLEEP_TIME) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not support simultaneous lock.`, + ); + + //---- + // WRITE LOCK + //---- + // TRY WRITE LOCK ON READING + start = Date.now(); + flag = await mtx.try_lock_for(SLEEP_TIME); + elapsed = Date.now() - start; + + if (flag === true) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_for(): it does not return exact value while reading.`, + ); + else if (elapsed < SLEEP_TIME * 0.95) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_for(): it does not block while reading.`, + ); + + // TRY WRITE LOCK AFTER READING + sleep_for(SLEEP_TIME).then(() => { + for (let i: number = 0; i < READ_COUNT; ++i) mtx.unlock_shared(); + }); + start = Date.now(); + flag = await mtx.try_lock_for(SLEEP_TIME * 1.5); + elapsed = Date.now() - start; + + if (flag === false) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_for(): it does not return exact value while reading.`, + ); + else if (elapsed < SLEEP_TIME * 0.95) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_for(): it does not work in exact time.`, + ); + + //---- + // READ LOCK + //---- + // READ LOCK ON WRITING + start = Date.now(); + for (let i: number = 0; i < READ_COUNT; ++i) { + flag = await mtx.try_lock_shared_for(SLEEP_TIME); + if (flag === true) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not return exact value while writing.`, + ); + } + elapsed = Date.now() - start; + + if (elapsed < SLEEP_TIME * READ_COUNT * 0.95) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not work in exact time.`, + ); + + // READ LOCK AFTER WRITING + start = Date.now(); + sleep_for(SLEEP_TIME).then(() => mtx.unlock()); + + for (let i: number = 0; i < READ_COUNT; ++i) { + flag = await mtx.try_lock_shared_for(SLEEP_TIME * 1.5); + if (flag === false) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not return exact value after writing.`, + ); + } + elapsed = Date.now() - start; + + if (elapsed < SLEEP_TIME * 0.95 || elapsed >= SLEEP_TIME * 5.0) + throw new Error( + `Bug on ${mtx.constructor.name}.try_lock_shared_for(): it does not work in exact time.`, + ); + + // RELEASE READING LOCK FOR THE NEXT STEP + for (let i: number = 0; i < READ_COUNT; ++i) await mtx.unlock_shared(); + } +} diff --git a/test/manual/remote-mutex.ts b/test/manual/remote-mutex.ts new file mode 100644 index 0000000..202a07f --- /dev/null +++ b/test/manual/remote-mutex.ts @@ -0,0 +1,52 @@ +import msv, { MutexServer } from "mutex-server"; +import std from "tstl"; + +const PASSWORD = "qweqwe123!"; +const PORT = 37119; + +async function client(index: number, character: string): Promise { + // CONNECT TO THE SERVER + const connector: msv.MutexConnector = new msv.MutexConnector( + PASSWORD, + null, + ); + await connector.connect(`ws://127.0.0.1:${PORT}`); + + // GET LOCK + const mutex: msv.RemoteMutex = await connector.getMutex("printer"); + await mutex.lock(); + + // PRINTS A LINE VERY SLOWLY MONOPOLYING THE MUTEX + process.stdout.write(`Connector #${index} is monopolying a mutex: `); + for (let i: number = 0; i < 20; ++i) { + process.stdout.write(character); + await std.sleep_for(50); + } + process.stdout.write("\n"); + + // ALTHOUGH THE CLIENT DOES NOT RELEASE THE LOCK + if (Math.random() < 0.5) await mutex.unlock(); + // SERVER WILL UNLOCK IT AUTOMATICALLY AFTER THE DISCONNECTION + else await connector.close(); +} + +async function main(): Promise { + // OPEN SERVER + const server: MutexServer = new MutexServer(); + await server.open(PORT, async (acceptor) => { + if (acceptor.header === PASSWORD) await acceptor.accept(null); + else await acceptor.reject(); + }); + + // CREATE 10 CLIENTS LOCKING MUTEX + const promises: Promise[] = []; + for (let i: number = 0; i < 4; ++i) { + let character: string = std.randint(0, 9).toString(); + promises.push(client(i + 1, character)); + } + + // WAIT THE CLIENTS TO BE DISCONNCTED AND CLOSE SERVER + await Promise.all(promises); + await server.close(); +} +main(); diff --git a/test/mutextes/test_mutex_disconnections.ts b/test/mutextes/test_mutex_disconnections.ts new file mode 100644 index 0000000..148e71e --- /dev/null +++ b/test/mutextes/test_mutex_disconnections.ts @@ -0,0 +1,38 @@ +import { MutexConnector, RemoteMutex } from "mutex-server"; +import { sleep_for } from "tstl"; + +import { IActivation } from "../internal/IActivation"; +import { ConnectionFactory } from "../internal/ConnectionFactory"; + +async function test_disconnection( + factory: ConnectionFactory, + index: number, +): Promise { + const connector: MutexConnector = await factory(); + const mutex: RemoteMutex = await connector.getMutex( + "test_mutex_disconnection", + ); + + if (index % 2 === 0) { + if ((await mutex.try_lock_for(SLEEP * COUNT * 20)) === false) + throw new Error( + "Error on RemoteMutex.try_lock_for(): disconnected clients do not return their acquisitions.", + ); + } else mutex.try_lock_for(SLEEP * COUNT * 20).catch(() => {}); + + await sleep_for(SLEEP * index); + await connector.close(); +} + +export async function test_mutex_disconnections( + factory: ConnectionFactory, +): Promise { + const promises: Promise[] = []; + for (let i: number = 0; i < COUNT; ++i) + promises.push(test_disconnection(factory, i)); + + await Promise.all(promises); +} + +const COUNT = 4; +const SLEEP = 10; diff --git a/test/mutextes/test_mutex_locks.ts b/test/mutextes/test_mutex_locks.ts new file mode 100644 index 0000000..0bde2ba --- /dev/null +++ b/test/mutextes/test_mutex_locks.ts @@ -0,0 +1,103 @@ +import { MutexConnector, RemoteMutex } from "mutex-server"; +import { Pair, sleep_for } from "tstl"; + +import { IActivation } from "../internal/IActivation"; +import { ConnectionFactory } from "../internal/ConnectionFactory"; +import { Validator } from "../internal/Validator"; + +const enum Status { + START_READING = "Start Reading", + END_READING = "End Reading", + START_WRITING = "Start Writing", + END_WRITING = "End Writing", +} +const MAGNIFIER: number = 3; + +export async function test_mutex_locks( + factory: ConnectionFactory, +): Promise { + const connector: MutexConnector = await factory(); + const mutex: RemoteMutex = await connector.getMutex("remote_mutex"); + + // TEST COMMON FEATURES + await Validator.lock(mutex); + await Validator.try_lock(mutex); + await Validator.lock_shared(mutex); + await Validator.try_lock_shared(mutex); + + // @todo: must be removed + if (1 === 1) return; + + // TEST SPECIAL FEATURES + const statusList: Pair[] = []; + + try { + const promises: Promise[] = []; + for (let i: number = 0; i < 25; ++i) promises.push(read(mutex, statusList)); + promises.push(write(mutex, statusList)); + + await Promise.all(promises); + + let reading: number = 0; + let writing: number = 0; + + for (let i: number = 0; i < statusList.length; ++i) { + const status: Status = statusList[i].first; + + if (status === Status.START_READING) ++reading; + else if (status === Status.START_WRITING) ++writing; + else if (status === Status.END_READING) --reading; + else --writing; + + if (writing > 0 && reading > 0) + throw new Error( + `Bug on SharedTimeMutex; reading and writing at the same time at ${i}`, + ); + } + } catch (exp) { + for (let pair of statusList) console.log(pair.first, pair.second); + throw exp; + } +} + +async function write( + mutex: RemoteMutex, + statusList: Pair[], +): Promise { + for (let i: number = 0; i < MAGNIFIER * 10; ++i) { + // JUST DELAY FOR SAFETY + await sleep_for(100); + const time: number = Date.now(); + + // DO WRITE + await mutex.lock(); + { + const now: number = Date.now(); + statusList.push(new Pair(Status.START_WRITING, now - time)); + + await sleep_for(50); + statusList.push(new Pair(Status.END_WRITING, Date.now() - now)); + } + await mutex.unlock(); + } +} + +async function read( + mutex: RemoteMutex, + statusList: Pair[], +): Promise { + for (let i: number = 0; i < MAGNIFIER * 100; ++i) { + const time: number = Date.now(); + + // DO READ + await mutex.lock_shared(); + { + const now: number = Date.now(); + statusList.push(new Pair(Status.START_READING, now - time)); + + await sleep_for(10); + statusList.push(new Pair(Status.END_READING, Date.now() - now)); + } + await mutex.unlock_shared(); + } +} diff --git a/test/semaphores/test_semaphore_acquires.ts b/test/semaphores/test_semaphore_acquires.ts new file mode 100644 index 0000000..8fe5969 --- /dev/null +++ b/test/semaphores/test_semaphore_acquires.ts @@ -0,0 +1,111 @@ +import { MutexConnector, RemoteSemaphore } from "mutex-server"; +import { sleep_for } from "tstl"; +import { ITimedLockable } from "tstl/base/thread"; + +import { ConnectionFactory } from "../internal/ConnectionFactory"; +import { IActivation } from "../internal/IActivation"; +import { Validator } from "../internal/Validator"; + +const SIZE = 8; + +export async function test_semaphore_acquires( + factory: ConnectionFactory, +): Promise { + //---- + // TEST MUTEX FEATURES + //---- + const connector: MutexConnector = await factory(); + const mutex: RemoteSemaphore = await connector.getSemaphore( + "test_semaphore_acquires_binary", + 1, + ); + const wrapper: ITimedLockable = RemoteSemaphore.get_lockable(mutex); + + await Validator.lock(wrapper); + await Validator.try_lock(wrapper); + + //---- + // TEST SPECIAL FEATURES OF SEMAPHORE + //---- + const semaphore: RemoteSemaphore = await connector.getSemaphore( + "test_semaphore_acquires_counting", + SIZE, + ); + + await _Test_semaphore(semaphore); + await _Test_timed_semaphore(semaphore); +} + +async function _Test_semaphore(s: RemoteSemaphore): Promise { + let acquired_count: number = 0; + + // LOCK 4 TIMES + for (let i: number = 0; i < (await s.max()); ++i) { + await s.acquire(); + ++acquired_count; + } + if (acquired_count !== (await s.max())) + throw new Error(`Bug on Semaphore.acquire()`); + else if ((await s.try_acquire()) === true) + throw new Error(`Bug on Semaphore.try_acquire()`); + + // LOCK 4 TIMES AGAIN -> THEY SHOULD BE HOLD + for (let i: number = 0; i < (await s.max()); ++i) + s.acquire().then(() => { + ++acquired_count; + }); + if (acquired_count !== (await s.max())) + throw new Error(`Bug on Semaphore.acquire() when Semaphore is full`); + + // DO UNLOCK + await s.release(await s.max()); + + if (acquired_count !== 2 * (await s.max())) + throw new Error(`Bug on Semaphore.release()`); + + // RELEASE UNRESOLVED LOCKS + await sleep_for(0); + await s.release(await s.max()); +} + +async function _Test_timed_semaphore(ts: RemoteSemaphore): Promise { + // TRY LOCK FIRST + for (let i: number = 0; i < (await ts.max()); ++i) { + const flag: boolean = await ts.try_acquire_for(0); + if (flag === false) + throw new Error( + "Bug on TimedSemaphore.try_lock_for(); failed to lock when clear", + ); + } + + // TRY LOCK FOR -> MUST BE FAILED + if ((await ts.try_acquire_for(50)) === true) + throw new Error( + "Bug on TimedSemaphore.try_lock_for(); succeeded to lock when must be failed.", + ); + + // LOCK WOULD BE HOLD + let cnt: number = 0; + for (let i: number = 0; i < (await ts.max()) / 2; ++i) + ts.acquire().then(() => { + ++cnt; + }); + + await sleep_for(100); + if (cnt === (await ts.max()) / 2) + throw new Error( + "Bug on TimedSemaphore.try_lock_for(); failed to release holdings.", + ); + + // RELEASE AND LOCK + await ts.release(await ts.max()); + + for (let i: number = 0; i < (await ts.max()) / 2; ++i) { + const flag: boolean = await ts.try_acquire_for(100); + if (flag === false) + throw new Error( + "Bug on TimedSemaphore.try_lock_for(); failed to lock when released.", + ); + } + await ts.release(await ts.max()); +} diff --git a/test/semaphores/test_semaphore_disconnections.ts b/test/semaphores/test_semaphore_disconnections.ts new file mode 100644 index 0000000..ae6f72a --- /dev/null +++ b/test/semaphores/test_semaphore_disconnections.ts @@ -0,0 +1,72 @@ +import { MutexConnector, RemoteSemaphore } from "mutex-server"; +import { ConnectionFactory } from "../internal/ConnectionFactory"; +import { IActivation } from "../internal/IActivation"; +import { sleep_for } from "tstl"; + +const MAX = 8; +const SLEEP = 50; + +async function acquire_and_disconnect( + factory: ConnectionFactory, + ms: number, +): Promise { + const connector: MutexConnector = await factory(); + const semaphore: RemoteSemaphore = await connector.getSemaphore( + "test_semaphore_disconnections", + 0, + ); + + if ((await semaphore.max()) !== MAX) + throw new Error("Error on RemoteSemaphore.max(): invalid value."); + else if ((await semaphore.try_acquire()) === false) + throw new Error( + "Error on RemoteSemaphore.try_acquire(): must be true but returns false.", + ); + + sleep_for(ms).then(() => connector.close()); +} + +export async function test_semaphore_disconnections( + factory: ConnectionFactory, +): Promise { + const connector: MutexConnector = await factory(); + const semaphore: RemoteSemaphore = await connector.getSemaphore( + "test_semaphore_disconnections", + MAX, + ); + + for (let i: number = 0; i < MAX / 2; ++i) + if ((await semaphore.try_acquire()) === false) + throw new Error( + "Error on RemoteSemaphore.try_acquire(): must be true but returns false.", + ); + + for (let i: number = 0; i < MAX / 2; ++i) + await acquire_and_disconnect(factory, SLEEP); + + if ((await semaphore.try_acquire()) === true) + throw new Error( + "Error on RemoteSemaphore.try_acquire(): must be false but returns true.", + ); + + const promises: Promise[] = []; + let count: number = 0; + for (let i: number = 0; i < MAX / 2; ++i) + promises.push( + semaphore.try_acquire_for(SLEEP * 1.2).then(() => { + ++count; + }), + ); + if (count !== 0) + throw new Error( + "Error on RemoteSemaphore.try_acquire_for(): succeded too early.", + ); + + await Promise.all(promises); + if (count !== MAX / 2) + throw new Error( + "Error on RemoteSemaphore.try_acquire_for(): must be succeeded but failed.", + ); + + await semaphore.release(4); +} diff --git a/test/semaphores/test_semaphore_simple.ts b/test/semaphores/test_semaphore_simple.ts new file mode 100644 index 0000000..09d7cc5 --- /dev/null +++ b/test/semaphores/test_semaphore_simple.ts @@ -0,0 +1,25 @@ +import { ConnectionFactory } from "../internal/ConnectionFactory"; + +export async function test_semaphore_simple( + factory: ConnectionFactory, +): Promise { + let connector = await factory(); + let semaphore = await connector.getSemaphore("test_sema2", 4); + + for (let i: number = 0; i < 4; ++i) await semaphore.acquire(); + + let promises: Promise[] = []; + let count: number = 0; + + for (let i: number = 0; i < 4; ++i) + promises.push( + semaphore.acquire().then(() => { + ++count; + }), + ); + + await semaphore.release(4); + await Promise.all(promises); + + if (count !== 4) throw new Error("Error on RemoteSemaphore.release(4)"); +} diff --git a/test/test_destructors.ts b/test/test_destructors.ts new file mode 100644 index 0000000..04678c1 --- /dev/null +++ b/test/test_destructors.ts @@ -0,0 +1,29 @@ +import { sleep_for } from "tstl/thread/global"; +import { MutexConnector, MutexServer, RemoteMutex } from "mutex-server"; +import { IActivation } from "./internal/IActivation"; +import { ConnectionFactory } from "./internal/ConnectionFactory"; + +async function test(factory: ConnectionFactory): Promise { + let connector: MutexConnector = await factory(); + let mutex: RemoteMutex = await connector.getMutex("test_destructors"); + + await mutex.lock_shared(); + await sleep_for(50); + + // mutex.unlock_shared() would be automatically called + await connector.close(); +} + +export async function test_destructors( + factory: ConnectionFactory, + server: MutexServer, +): Promise { + let promises: Promise[] = []; + for (let i: number = 0; i < 4; ++i) promises.push(test(factory)); + + await Promise.all(promises); + await sleep_for(50); + + if (server["components_"].mutexes["dict_"].has("test_destructors") === true) + throw new Error("Destructor is not working."); +} diff --git a/test/tsconfig.json b/test/tsconfig.json new file mode 100644 index 0000000..9144500 --- /dev/null +++ b/test/tsconfig.json @@ -0,0 +1,17 @@ +{ + "extends": "../tsconfig.json", + "compilerOptions": { + "outDir": "../bin", + "paths": { + "mutex-server": ["../src"], + "mutex-server/lib/*": ["../src/*"], + }, + "plugins": [ + { "transform": "typescript-transform-paths" }, + ], + }, + "include": [ + "./", + "../src", + ], +} \ No newline at end of file diff --git a/tsconfig.json b/tsconfig.json index 8291932..2939d1e 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -66,5 +66,6 @@ /* Advanced Options */ "forceConsistentCasingInFileNames": true, /* Disallow inconsistently-cased references to the same file. */ - } + }, + "include": ["./src"] } From c16e1a6bb60c357617c1af781261dcd71f3daf74 Mon Sep 17 00:00:00 2001 From: Jeongho Nam Date: Wed, 13 Mar 2024 16:14:44 +0900 Subject: [PATCH 2/2] Upgrade build.yml to be latest --- .github/workflows/build.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 91aefe6..0b540f4 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -5,10 +5,10 @@ jobs: Ubuntu: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v1 - - uses: actions/setup-node@v1 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: - node-version: 12.x + node-version: 20.x - run: npm install - run: npm run build - run: npm run test \ No newline at end of file