npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

@otpjs/gen_server

v0.18.0

Published

A gen_server implementation for @otpjs/core

Downloads

317

Readme

gen_server

A gen_server implementation is defined.

Install

npm i @otpjs/gen_server

Behavior

init(ctx, ...args)

Initialize the process state and execute any necessary startup operations.

Return can match the following signatures:

  • t(stop, Reason)
    • Immediately stops the process for Reason.
    • Does not invoke terminate
  • t(ok, State)
    • Runs the process with State as the current state

handleCall(ctx, call, from, state)

Handles a message which has requested a response.

call is the term representing the requested operation. from is a tuple representing the caller and the unique request. state is the current state of the process.

Return can match one of the following signatures:

  • t(stop, Reason, Reply, State)
    • uses State as the next state of the process
    • sends Reply to the caller
    • stops the server after invoking terminate with Reason
  • t(stop, Reason, State)
    • uses State as the next state of the process
    • stops the server after invoking terminate with Reason
  • t(reply, Response, State)
    • uses State as the next state of the process
    • responds to the caller with Response
  • t(reply, Response, State, Timeout)
    • uses State as the next state of the process
    • responds to the caller with Response
    • sends the message timeout to itself after Timeout milliseconds
  • t(noreply, State)
    • uses State as the next state of the process
    • does not respond to the caller
  • t(noreply, State, Timeout)
    • uses State as the next state of the process
    • does not respond to the caller
    • sends the message timeout to itself after Timeout milliseconds

Replies do not have to be immediate. You can store the from argument in your state and respond at a later time by invoking gen_server.reply(ctx, from, response).

handleCast(ctx, cast, state)

Handles a structured message for which there has been no response requested.

cast is the term that we are processing. state is the current state of the process.

Return can match one of the following signatures:

  • t(stop, Reason, State)
    • uses State as the next state of the process
    • stops the server after invoking terminate with Reason
  • t(noreply, State)
    • uses State as the next state of the process
    • does not respond to the caller
  • t(noreply, State, Timeout)
    • uses State as the next state of the process
    • does not respond to the caller
    • sends the message timeout to itself after Timeout milliseconds

handleInfo(ctx, info, state)

Handles an unwrapped message, such as a DOWN message or an EXIT signal.

info is the term that we are processing. state is the current state of the process.

Return can match one of the following signatures:

  • t(stop, Reason, State)
    • uses State as the next state of the process
    • stops the server after invoking terminate with Reason
  • t(noreply, State)
    • uses State as the next state of the process
    • does not respond to the caller
  • t(noreply, State, Timeout)
    • uses State as the next state of the process
    • does not respond to the caller
    • sends the message timeout to itself after Timeout milliseconds

terminate(ctx, reason, state)

Invoked when shut down explicitly with a stop response, OR when an EXIT signal is received and the process flag trap_exit is set.

Ignores the return value.

Usage

import { Node, Symbols } from '@otpjs/core';
import * as matching from '@otpjs/matching';
import { t, l } from '@otpjs/types';
import * as gen_server from '@otpjs/gen_server';

// Let's import some commmonly used symbols
const { ok } = Symbols;
const { reply, noreply } = gen_server.Symbols;

// We need to define a couple of functions to tell gen_server how to act.
// gen_server is just a pattern; without these callbacks it does nothing.
const callbacks = gen_server.callbacks((server) => {
    server.onInit(_init);

    server.onCall(t('my_remote_function', l.isList), _myRemoteFunction);
    server.onCall('get_current', _getCurrent);
    server.onCall(_, _doNothing);

    server.onCast('generate', _generate);
    server.onCast(t('finalize', Ref.isRef, _), _finalize);
    server.onCast(_, _doNothingCast);

    server.onInfo(_, _handleInfo);

    server.onTerminate(_terminate);
});

// Start a new gen_server process using our callbacks for the implementation
// Starting a gen_server is asynchronous
export async function start(ctx) {
    return gen_server.start(ctx, callbacks);
}

// Start a new gen_server like above, but also link it to the current context
export async function startLink(ctx) {
    return gen_server.startLink(ctx, callbacks);
}

// Calls implement the request/response pattern over an asynchronous communication
// channel. Calls are asynchronous, and you may never get a response! Implement a
// timeout to prevent your callers from waiting forever if something goes wrong.
// Default timeout is 5 seconds.
export async function myRemoteFunction(ctx, pid, ...args) {
    return gen_server.call(ctx, pid, t('my_remote_function', l(...args)));
}
export async function getCurrent(ctx, pid) {
    return gen_server.call(ctx, pid, 'get_current');
}

// Casts are asynchronous messages. They have a formal pattern unlike pure messages.
export function finalizeRemoteFunction(ctx, pid, ref, result) {
    return gen_server.cast(ctx, pid, t('finalize', ref, result));
}
export function generate(ctx, pid) {
    return gen_server.cast(ctx, pid, 'generate');
}

function _init(ctx) {
    // init is handled during the process startup. If something goes wrong here,
    // the process that starts us will be notified.
    // From here we can make determinations about our setup and configuration,
    // and prepare our initial state.
    return t(ok, Math.random());
}

function _getCurrent(ctx, _call, _from, state) {
    // Reply directly to the caller in this case
    return t(reply, t(ok, state), state);
}
function _myRemoteFunction(ctx, call, _from, state) {
    const [, ...args] = call;
    // Do something somewhere else. We can defer our response until later.
    doRemoteFunction(ctx, from, ...args);
    return t(noreply, state);
}

function _generate(ctx, _cast, _state) {
    const nextState = Math.random();
    // We can always update our state, whether or not a reply is needed.
    return t(noreply, nextState);
}
function _finalize(ctx, _cast, state) {
    const [, from, result] = cast;
    // Now that we've got a final result, we can reply to our deferred request
    gen_server.reply(ctx, from, result);
    return t(noreply, state);
}

function _doNothing(ctx, _call, _from, state) {
    // Not recognized. No need to handle it.
    return t(noreply, state);
}
function _doNothingCast(ctx, _cast, state) {
    // Not recognized. No need to handle it.
    return t(noreply, state);
}

function _handleInfo(ctx, info, state) {
    // handleInfo is used to handle pure messages that come in without either
    // cast or call semantics. This can be useful if you're monitoring other
    // processes, for instance.
    // For the sake of demonstration, let's assume this server's contract
    // does not allow for info messages. Given that, let's stop the process
    // if we receive one.
    return t(stop, t('badinfo', info));
}

function _terminate(ctx, reason, state) {
    // Pre-death cleanup
    return ok;
}