GithubHelp home page GithubHelp logo

Comments (2)

nicolasdao avatar nicolasdao commented on June 27, 2024

Hi @loganpowell ,

You have a few deadlocks here

Deadlock 1

Description

yield _I_.put("Clojure") waits until a first brick is taken from the _I_ chan, but this will only happen when your namer function is called (yield _I_.take()). Because the namer waits until yield _I_.put("Clojure") yields a result, you are in a deadlock.

Solution

One way of fixing that issue (though not idiomatic of how CSP is supposed to be used) is to not wait for a process to take a brick from the _I_ channel:

co(function *() {
    _I_.put("Clojure")
    yield namer(_I_, _O_)
    const fancy_name = yield _O_.take()
    console.log("hello", fancy_name)
})

However, even with this, you'll run into the second deadlock.

Deadlock 2

Description

The namer function waits for a brick to be taken from the _O_ channel (yield _O_.put(fancy_name)). The only line of code that unlocks that wait is occurring after the namer invocation. That's the second deadlock.

Solution

One way of fixing that issue (though not idiomatic of how CSP is supposed to be used) is to not wait for taking a brick from the _O_ channel in the namer function. The new namer function would look like this:

const namer = (_I_, _O_) => co(function *() {
    const name = yield _I_.take() // This waits until a first brick is added on the _I_ channel.
    const fancy_name = `${name}izer`
    _O_.put(fancy_name)
})

Combined Solutions

If we put those 2 solutions together, the code looks like this:

const co = require('co')
const { Channel } = require("core-async")

const namer = (_I_, _O_) => co(function *() {
    const name = yield _I_.take() // This waits until a first brick is added on the _I_ channel.
    const fancy_name = `${name}izer`
    _O_.put(fancy_name) // REMOVING DEADLOCK 2
})

const _I_ = new Channel()
const _O_ = new Channel()

co(function *() {
    _I_.put("Clojure") // REMOVING DEADLOCK 1
    yield namer(_I_, _O_)
    const fancy_name = yield _O_.take()
    console.log("hello", fancy_name)
})

Better Approach

However, as I briefly mentioned before, this is not idiomatic of CSP. Indeed, what if the namer function is supposed to do more work after _O_.put(fancy_name) and that work must happen after there is a confirmation that another process has taken that brick from the _O_ channel. We could make the same remark for the _I_.put("Clojure"). What if we need to add a piece of work between _I_.put("Clojure") and yield namer(_I_, _O_) that can only be executed when there is confirmation that the "Clojure" brick has been taken from the _I_ channel. This solution would have to be refactored.

Your original namer function is fine. It works properly and does not contain deadlocks, but the way it is being used leads to deadlocks. So here is a suggestion of how your code could be re-written in what I think, IMHO, might be a more idiomatic CSP style.

const co = require('co')
const { Channel } = require("core-async")

// This is your original func
const namer = (_I_, _O_) => co(function *() {
    const name = yield _I_.take() // This waits until a first brick is added on the _I_ channel.
    const fancy_name = `${name}izer`
    yield _O_.put(fancy_name) // This waits until a first request for a brick is emitted on the _O_ channel.
})

const _I_ = new Channel()
const _O_ = new Channel()

// 'namer' does stuff asynchronously
co(function *() {
    console.log(`Doing some work before 'namer' starts`)
    yield namer(_I_, _O_)
    console.log(`Doing some more work now that 'namer' is done`)
})

// Asynchronously do things each time a brick is added to the _O_ channel.
co(function *() {
    const fancy_name = yield _O_.take()
    console.log("hello", fancy_name)
})

// Asynchronously do things each time a brick is taken from the _I_ channel.
co(function *() {
    yield _I_.put("Clojure") //?
    console.log(`Great, somebody took the 'Clojure' brick. I can do other awesome stuff now.`)
})

If you're craving for more Clojure stuff in JS (e.g., combining CSP patterns with transmuters), then a more mature library is js-csp. The only reason I built core-async instead of using js-csp is that I needed the alts function.

I hope my explanation shed some light on your problem.

Cheers,

Nic

from core-async.

loganpowell avatar loganpowell commented on June 27, 2024

Dude, just awesome all around.

from core-async.

Related Issues (3)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.