Proposal of Multithread JavaScript

# Leo Dutra (7 years ago)

ECMA introduced Promises and async-await in JS. This improves coding in an amazing way, reducing the control developers need to wrap an AJAX call or async I/O.

JavaScript used to be script and not a language. Classes, workers, sound control, GL rendering, Node.js modules (with OS conversation), incredible GC strategies and compilation on V8 and Mozilla "monkeys"... the list goes on and on.

Almost all the features provided by old mature platforms, like Java, .NET and etc. For browsers, the newest JS features provide consistent tools for productivity and quality code.

But there's a huge step to accomplish.

ECMA introduced workers. Node.js came up with streams, native process spawn and libuv thread pool. This is a lot, but not enough.

All I hear about Node.js is how it is great for quick message I/O and bad for aggregations and impossible for parallel tasking. Again, we have workers and processes, but not green threads.

I invite you to take a quick look at Akka and OTP (Erlang). More than it, I will argument: workers and process spawn are the latent desire for parallel and starting one of these are not "cheap" or waiting in a pool.

We use streams extensively in Node.js and most frameworks hides it from us. Call it magic, I call it pragmatism.

Now, async, await, Promises ("Futures")... we can make it all work in parallel.

This would explore more libuv in Node.js and browsers could handle it too, seamlessly.

Each function could be run in a green thread, pulled from a browser/libuv pool, allowing Node.js and browsers to process aggregations and heavy rendering without heavy start costs and complicated message control through events.

More, I ask why not, and "single thread nature of JS" looks more like a bad legacy from old browsers. We can do it in pieces, like the proposed async-await and, on better days, provide a Parallel API (something like parallelize(() -> { // parallel stuff here })).

I wanna leave you with the possibilities in mind and bully this single thread dogma.

You have been told.

# Leo Dutra (7 years ago)

TL;DR:

(function foo() { ... })() // sync and single threaded
(*async *function() { ... })() // async and parallel

Additional:
parallel.run()
parallel.stream()
parallel.sleep()
parallel.waterfall()
...

# Bradley Meck (7 years ago)

We need to be careful about this, I would never condone adding threading that could share variables that were not intended to be multi-threaded, as such variable access outside of your parallelize construct/syntax would need to be message passing when talking to something that is not already written as a parallel structure. A notable thing here is that Shared Memory and Atomics that are in ECMA Stage 2 : tc39 ecmascript_sharedmem which would probably need to land prior to me condoning any shared mutable state.

Historically, all JS implementations are based upon a job queueing system described by the Event Loop. This is very different from parallelism which could have shared mutable state. All code is guaranteed to have exclusive access to variables in scope until it finishes running, and that the content of those variables will not change from preemption (there are cases where this is not true in the browser with a live DOM). There are alternative discussion recently on Workers : esdiscuss.org/topic standardize-es-worker . I might look there first.

In particular, I would suggest taking a look at problems of synchronization, locking, and preemption breaking existing code a bit rather than just stating that green threads are the way to go.

# Leo Dutra (7 years ago)

​There's nothing about threading that is not problem with Event loop. I'd say there's even less problems.

The proposal is a seamless behaviour, equals to what we have now.

Message passing is not a problem of JS developer in the case, but a V8/WhateverMonkey problem.

Changing a value inside a multithread async MUST behave in the same way of a change inside a single threaded async. The same way, non-referenced variables SHALL NOT be scoped in the thread. This is not Java with volatiles. This is the plain old JS with clojures, openess and loose bare metal control.

Thread interruption is a bad practice anyway. And we could have a Mutex class for the specific case or another idea.

Workers are evented and started, not pooled and easy to use.

# Leo Dutra (7 years ago)

​Understood about shared mem. I agree a declared shared space is faster and more manageable than a considering all shared.

As I said, we could use JIT to determine that or use the long reserved word "volatile": volatile var foo = "Shared string"

Accessing a non-volatile variable SHALL resolve to a copy like all the other platforms resolve.

I don't like to refer to others, but this is a problem industry resolved a long time ago.

# Bradley Meck (7 years ago)

Consider:

let a = {};

alpha: parallel {
  a.text = 'hello';
}
beta: parallel {
  a.text += ' world';
}
console.log(a);

This has racing:

  • around a.text between alpha: and beta:.
  • around console.log since a could be 1 of 3 values depending on how threads are scheduled.

I am stating that such racing/shared mutation should be prevented. Workers do this by message passing and ownership of transferable data. There could be other mechanics for synchronization, but I don't see a simplistic solution. Things like having a read-only view of data partially helps, but atomics are most likely the proper way to do this if you don't want message passing and ownership semantics.

# Leo Dutra (7 years ago)

If it is accepted, yes... Atomics should take care of racing.

We have a lighter context than most functional languages with this proposal: we reassure JavaScript memory is totally mutable (against Erlang/Rust/Haskell).

This keeps the job easier. I was thinking in the worst possibility and we have the easier.

Lock, immutability and not shared mutation are the special cases. For these cases, Atomic and Mutex constructs will fit.

# Michał Wadas (7 years ago)

Actually, is there any problem that can't be easily solved with message-passing for high-level structures or low-level shared memory buffers?

# Leo Dutra (7 years ago)

This is not only easier, this is faster.

We have a single memory allocation and all the threads share it.

# Leo Dutra (7 years ago)

let a = {};

async alpha() { a.text += 'hello' }
async beta() { a.text + ' world' }

parallel.all( a, b ) // or thread.parallel( a, b )
.then(() -> console.log(a.text))

Will output ' worldhello' or 'hello world' like any multithread platform will.

# Florian Bösch (7 years ago)

I'd like to quickly point out that Promises, async/await are actually implementations of co-routines. Unlike true co-routines however they're infective (once something down the call stack becomes a promise or an async, it infects upwards). They have none of the conveniences of actual co-routines (non infective, control flow/structural semantic is preserved), while sharing all of the drawbacks of any other cooperative multitasking scheme (data corruption).

Racing in promises:

xhr().then(function(){a.text = 'foo'}) xhr().then(function(){a.text += 'bar'})

Racing in async/await:

async function foo(){ a.text = await xhr(); } async function bar(){ a.text += await xhr(); } await* [foo(), bar()]

Racing with cooperative multitasking (assuming greenlet semantics and a trampoline I/O scheduler):

scheduler.spawn(function(){ a.text = xhr(); }); scheduler.spawn(function(){ a.text += xhr(); });

None of these examples is really representative of what people would actually do, which is:

Promises:

xhr() .then(function(){ a.text = 'foo'; return xhr(); }) .then(function(){ a.text += 'bar'; })

async/await

a.text = await xhr(); a.text += await xhr();

Co-routines:

a.text = xhr(); a.text += xhr();

However we can see how actual co-routines would have a marked advantage in simplicity and conciseness.

All forms of cooperative multitasking would eventually have to evolve a barrier mechanism to deal with data corruption. Promises are severely handycapped in that regard as they cannot create barriers at all. Async/await barriers are conceivable, but awkward. Co-routine barriers are relatively straightforward to implement if you control your scheduler implementaiton.

It should be noted that controlling what kind of scheduler you use would be kinda important, which is one convenient aspect of co-routines, they can easily implement a custom one fit for your needs.

# Leo Dutra (7 years ago)

Message passing between two objects is a Law of Nature, broad and powerful. But there's more than just workers and process spawn in life.

As I said... JS has mutable objects by default. The memory space SHALL be the same.

Atomic could handle the racing when needed and a Mutex for locking.

What is not being covered? Let it come to the surface.

# Bradley Meck (7 years ago)

As I said... JS has mutable objects by default. The memory space SHALL be

the same.

What is not being covered? Let it come to the surface.

As stated, that single threaded access to objects and shared memory multi-threaded access are very different.

# Leo Dutra (7 years ago)

Bösch, this is the legacy of callbacks.

ECMA does a great job with specs, but JS does every step to solve the last step.

Callback became a promise for modularity. Promise is hidden in async await for simplicity. Common constructs, but in JS they came in being adapted for JS world.

As I said, immutability is not strong in JS as it is not a PURE functional programming language.

Weird or not, this led us to the possibility of multithreading with total share and mutability.

If we wanna multithread in here, we have to respect all the JavaScript legacy. And the JavaScript legacy hurts Java/C#/whatever feelings cause IT IS DIFFERENT.

As said in my first argumentation, we have race condition problems and total share of scope and none died because of it. Multithread can be used to run these callbacks, functions and promises seamlessly and if we want it different... that's a huge bad conversation about foundations of JavaScript and a total change that would wreck the JS world and let us to anything that is not JS.

We have to decide if we stick with scope sharing and mutability or look for another language. JS is what it is.

# Leo Dutra (7 years ago)

​Bradley, JIT can take care of it. If too hard... "volatile" reserved word is around since the early 00's.​

# Bradley Meck (7 years ago)

Note: this is not an implementation issue, this is a shared access introducing bugs issue.

# Florian Bösch (7 years ago)

What I meant to illustrate is that concurrency always leads to race conditions, and that neither promises nor async/await are free of them, and so it's silly to argue against co-routines on that basis, because of all the continuation mechanisms, they're the most convenient to use with the least drawbacks (no infectiveness).

# Bradley Meck (7 years ago)

Florian, one of the great aspects of generators and async functions in ECMAScript is that they are explicit. It makes understanding where synchronization might need to occur very easy to find. I am unsure what your proposal to prevent infection as you call it would look like if it is explicit.

# Michał Wadas (7 years ago)

Why do you need it to be faster? Actual processing of hundreds items is fast enough on PC (under 16ms) and very likely to be fast enough on mobile. If you are writing game probably the better choice would be to go with raw numbers and/or compiled language than deal with performance hit and racing conditions in whole codebase. Because libraries are not guaranteed to be thread-safe when accessed. Even basic language spec isn't designed to be thread-safe.

Multithreading in manage language with virtual machine requires constant guards. It's not a C - changing object shape in middle of another operation is likely to cause a trouble. Even simple foo.bar = 42 requires following steps for [[Set]] operations and these steps are not thread-safe.

# Michael J. Ryan (7 years ago)

Given how many bugs I've dealt with involving race conditions in .Net, I would really rather have a solid worker pattern over any internal threading inside the main event loop in js as this thread has proposed...

Adding an rpc-like promise callback in addition to, or over message passing would be far safer...

Internal threading may be "a solved problem" but it creates so many new ones with really weird bugs to go with them.

# Florian Bösch (7 years ago)

On Wed, Nov 2, 2016 at 4:13 PM, Bradley Meck <bradley.meck at gmail.com> wrote:

Florian, one of the great aspects of generators and async functions in ECMAScript is that they are explicit. It makes understanding where synchronization might need to occur very easy to find. I am unsure what your proposal to prevent infection as you call it would look like if it is explicit.

The theory that await/async are explicit is nice, but flawed, and here's why.

If you have a call stack of say A -> B -> C -> D, and you change D to async

D, you have a problem. C isn't awaiting D, so it needs to do C await ->

async D, but now C isn't async and B isn't awaiting C, and so forth. So you'll end up with an "infection": A await -> async B await -> async C

await -> async D.

The infection spreads from down the call-stack upwards, and in doing so, it spreads sideways as well. If you have say a routine that calls a bunch of functions in succession:

A(); B(); C(); D(); and for some reason or other (because your lower level framework code became async) all of them now become awaitable as well, you end up with await A(); await B(); await C(); await D();

So eventually most calls end up being prefixed by await and most functions/methods end up being async, except for low-level code deep down.

It might surprise you that co-routine schedulers work exactly like that. Except without all the not needed line-noise. In fact, it could be argued that instead of liberally strewing await/async randomly through your code, you could simply do a preprocessor that converts every call to an await and every closure to an async, that way at least you don't need to type it out everytime. Of course you'd also get functionally true co-routines via a fairly mindless application of preprocessing.

# Leo Dutra (7 years ago)

Is not a matter of being faster. Is a matter of using machine potential and using better internal instructions. JavaScript sits over libuv and engines with multithreading without using multithreading.

And about being faster, any serious language has a simple feature like threads and Visual Basic should not emerge even in Microsoft specs discussions. What comes next? PHP?

I still don't understand why you talk so much about racing conditions in a language which one of the main aspects is loose clojuring and racing condition.

Who cares about thread-safety if it were never expected and if it all can be seamless. And where it would not be explicit?

# Michael J. Ryan (7 years ago)

There is a difference between thread safety and unexpected event ordering in a higher level language.. just because you don't think of it in the language doesn't mean it isn't there... Also the js environments are multi threaded, it's just those threads are for internals and abstracted away from you in a safe way.

# Leo Dutra (7 years ago)

Bösch... run, change your name and hide in the mountains or they will burn all this heresy.

There is a difference between thread safety and unexpected event ordering in a higher level language.. just because you don't think of it in the language doesn't mean it isn't there... Also the js environments are multi threaded, it's just those threads are for internals and abstracted away from you in a safe way.

Sure. The same safe way we can multithread async and await as I said in the first statements. That's what I'm calling seamless.

XHR does it and is seamless (natively it has to be a thread).

That's why all the bragging about racing condition applies as much to this multithreading as to current callbacks, promises, asyncs and awaits...

If you will fight against it... fight in the foundations. I'm proposing us to keep the flow.

Every async would be run in a thread from a pool. Chained or not, infectious or not, sharing variables or not... is an internal change I'm proposing here.

# Michael J. Ryan (7 years ago)

Sorry for reply...

var a = {}; Thread 1: Object.assign(a, {...}); Thread 2: Object.assign(a, {...});

So many places to blow up internally it isn't funny... Also, as I said, even where threading is first class with locking, there are really weird bugs when people who don't intimately understand things write code...

Having a clear worker pattern is safer... Adding internalized immutables to improve message passing would be nice, as would an rpc to promise interface...

Real threads however is a truly bad idea in JS...

On Nov 2, 2016 8:31 AM, "Michael J. Ryan" <tracker1 at gmail.com> wrote:

There is a difference between thread safety and unexpected event ordering in a higher level language.. just because you don't think of it in the language doesn't mean it isn't there... Also the js environments are multi threaded, it's just those threads are for internals and abstracted away from you in a safe way.

# Florian Bösch (7 years ago)

There's a fairly good implementation of co-routines called greenlets for python. It depends on a few basic API calls, these are:

  1. g = greenlet.greenlet(fun) // makes a new greenlet pointing to the given function
  2. g.switch(args) // switches to that function
  3. g.switch(value) // once started delivers value inside that functions switch call
  4. g.throw(error) // raises an exception into the function
  5. greenlet.getcurrent() // returns the currently running greenlet

With these API primitives, you can implement any flavor of asynchronous/concurrent scheduler on top if you wish to do so.

# Bradley Meck (7 years ago)

XHR does it and is seamless (natively it has to be a thread).

XHR is very different since it does not attempt to change state between threads in a racey fashion.

# Leo Dutra (7 years ago)

In JS, if you assign a string to a var containing an object... it will become an object.

Michael, I'm sorry to say JavaScript is not "safe". Try Java/Haskell/Fortran.

JavaScript is the land of pragmatic algorithms and "for internals and abstracted away from you in a safe way".

Workers are cool. I like it.

But for those who knows internals, it is very very expensive. And messaging with one worker is easy peasy. Try with 5. That amazing onmessages everywhere without a good organization and you will reclaim your Visual Studio in no time.

I'm talking about do a single

async rasterizeTheWholeGameStuff() {}

And all the stuff, as a simple async or callback, run inside a green thread.

Is that really so much? I don't think so. I think all of your statements against it, till now, are not considering about being "seamless".

Bösch, I think greenlets is a great example... but I'd take it in a second step, thinking about that additionals I put above.

They can't even agree with an existing, full of racing condition and no atomicity clojured function body, to be run SEAMLESS in another thread.

Maybe if I create an e-mail like goku at gmail.com or michiokaku at gmail.com...

# Bradley Meck (7 years ago)

You will need to convince people that their problems with your proposal are solvable and not dismiss them, not state that your idea has uses. I completely agree it has uses, but the problems vastly outweigh any use case in my mind.

# Leo Dutra (7 years ago)

XHR is very different since it does not attempt to change state between threads in a racey fashion.

Are you sure? It changes DOM all the time, people does it in almost every callback.

# Bradley Meck (7 years ago)

XHR is very different since it does not attempt to change state between

threads in a racey fashion.

Are you sure? It changes DOM all the time, people does it in almost every

callback.

Racey here refers to implicit/preemption. See the following example which blocks the browser permanently:

xhr=new XMLHttpRequest;
xhr.open('GET', 'https://cors-test.appspot.com/test');
xhr.send();
while (xhr.readyState !== 4) {}
# Leo Dutra (7 years ago)

​The problems you are seeing are not in my idea. They are JavaScript problems I don't want to solve and shall not solve to have what I propose.

The only change for those using async await and promises would be changing platform version.

Internally, these things would be just moved from Event Loop for a thread and you would not notice.

I've additionally started a proposition for explicit calls, but that is an extra and not the core objective.​

# Leo Dutra (7 years ago)

Michael... Object.freeze().

# Leo Dutra (7 years ago)

​Bradley, understood.

And what in this algorithm would prevent it from being run in another thread with this same seamless code?​

# Bradley Meck (7 years ago)

I assert that it cannot be seamless in JS due to the high level of mutability. Workers seem more viable. Having workers be first class and using something like ownership in rust might be possible, but shared mutable heap is not safe without very fine grained and complete transfer of ownership (this most likely would not work if things prototypes/getters/setters survive transfer).

# Leo Dutra (7 years ago)

there are additional checks for scoping to prevent escape from isolation that would be needed as well.. not to mention being able to call other functions, similarly isolated - Michael

Yes. Expected. And where is the problem? You already have async await inside async await. What would change? You already share variables and all the outer scope stuff.

There's not a problem with frozen object in workers. But the proposal is for multithreading, very different from worker. A worker could be run in a process for workers, as it happens in some implementations. A thread is a thread. Is directly connected with the process idea originated from POSIX. And another facade, worker requires an separate script or dynamic eval. This is not maintanance friendly.

Bradley... ownership in JavaScript? Ownership in Rust is applicable... every let in Rust, without mutation explicitation, requires ownership. JavaScript is the total opposite.

Why would you want ownership today with multithreading if you don't have it with single thread?

​ ​Ex:

(function outer() { let a = 1;

let b = async function() { a = 2 }

await b() console.log(a === 2) -> true

})()​

Where is your ownership?

# Bradley Meck (7 years ago)

Where is your ownership?

The main thread has all ownership.

# Leo Dutra (7 years ago)

The main thread has all ownership.

Not explicit, not important. I changed the outer scope the same way and a will last with the outer scope the same way.

If "b" ran in a thread... what would change for the JS dev?

Nothing.

# Bradley Meck (7 years ago)
# Bradley Meck (7 years ago)

Not explicit, not important.

Disagree, you cannot convince me otherwise.

If "b" ran in a thread... what would change for the JS dev? Nothing

Not true simply by you needing to make a proposal here.

Also for many contrived examples even with real threads; some cases would be "unchanged" since they are not racing.

# Leo Dutra (7 years ago)

​ ​That's what I'm calling seamless multithreading.​

The only change is the spec change of JS being strictly single-threaded. The proposal is to point aspects where JS can introduce seamless threads and become more paired with more mature platforms.

Async, Await, I'd add XHR and any other component with a callback, streams (as it is in Node.js), Promise resolutions and maybe events.

# Leo Dutra (7 years ago)

Bradley, show me an JS example where ownership would be important and I'll call you my guru.

# doodad-js Admin (7 years ago)

I have used multi-threading in C#/VB.NET where objects are also mutable and I have been able to deal with racing conditions without any problem using language-provided features and techniques against them.

Currently, to reproduce multi-threading with Node.js, we have to spawn processes and use IPC which implies exchange protocols and communication channels. That makes more overhead and cause more troubles than real multi-threading with shared-objects in memory.

Really, I’m waiting too for multi-threading in JS, not just for parallelism, but for simplicity and conveniences.

# Bradley Meck (7 years ago)

Everything in this thread? I don't even understand how ownership is not important if there is concurrent access in JS. All existing JS code was written under the assumption that the arguments / variables that can be accessed are not going to be concurrently mutated across threads. ALL of it.

# Leo Dutra (7 years ago)

Nop. I'm saying the opposite.

It will be mutated and it does not matter. Imagining that last example I gave, if "B" ran in a thread... Nothing would change for the JS developer. "Seamless"... Magik.

The case you are issuing is what I called a change in foundations of JS... Making threads declared as in Java... And using volatiles. I hypothesized about it... But the effort would be huge and I won't like to see this war.

My proposal is break the single-thread dogma and let async, await, promises and components run multithreaded.

Simple, but against the a dogma.

# Bradley Meck (7 years ago)

Multiple threads are fine, but the "seamless" shared mutable variables are a no go on my end. As long as concurrent access to any existing single-thread assuming code is subject to ownership guards it seems fine. Workers, Atomics, SharedArrayBuffers, Transferables, etc. are places to look.

# Leo Dutra (7 years ago)

Again, async B example is possible and is the proposed.

Workers and SharedArrayBuffers are explicit. I'm proposing a non explicit thread under existing structures.

The only explicit I'd insert in my proposal are Atomics (for explicit racing conditions treatments).

# Florian Bösch (7 years ago)

On Wed, Nov 2, 2016 at 5:59 PM, Bradley Meck <bradley.meck at gmail.com> wrote:

Multiple threads are fine, but the "seamless" shared mutable variables are a no go on my end.

You already have concurrent threads of execution accessing shared mutable variables with either Promises or async/await. OS level threads are a particularly nasty variant of multitasking which I'd rather like JS to stay away from, but I don't see how that'd be an argument against co-routines.

# Bradley Meck (7 years ago)

You already have concurrent threads of execution accessing shared mutable

variables with either Promises or async/await. OS level threads are a particularly nasty variant of multitasking which I'd rather like JS to stay away from, but I don't see how that'd be an argument against co-routines.

Yes, concurrent and explicitly cooperative. I'm fine with co-routines, just explicit ones (which we currently have via generators and async functions). Implicit ones make it hard to reason about if a variable needs to place guards prior to performing any action if actions pop the stack in order to do a co-routine pause/resume.

# Leo Dutra (7 years ago)

Libuv and browsers use it. But no pool is exposed by API to JS.

# Florian Bösch (7 years ago)

On Wed, Nov 2, 2016 at 6:16 PM, Bradley Meck <bradley.meck at gmail.com> wrote:

I'm fine with co-routines, just explicit ones (which we currently have via generators and async functions). Implicit ones make it hard to reason about if a variable needs to place guards prior to performing any action if actions pop the stack in order to do a co-routine pause/resume.

As I've illustrated, the natural tendency of explicit tagged asynchronous code combined with proper software engineering (separation of concerns, modularity, reuse, interfaces, encapsulation etc.) will be to infect most code with it, until the distinction of "explicit" becomes entirely meaningless.

# Wes Garland (7 years ago)

I'm still confused about what problem we are trying to solve here.

I had pthreads-style JS running in JS 1.7 many years ago ( wesgarland/gpsee/tree/master/modules/thread), and after investing quite a lot of time in making it work, I found that it wasn't really all that useful in solving any problems I had at the time. Or since. Mostly it was written for problems I thought I had, because I was a C developer trying to bend JS to fit old thought patterns.

Lately, I have been using messaging passing a la DOM workers when I need parallelization and find it quite useful.

Incidentally, there is no "single thread dogma". I don't know where you get this idea. Running multiple threads of unrelated JS is completely supported by the existing spec and at least one of the engines.

Finally, Message Passing is not a JS problem either, IMO. It's a host environment problem. This is why, for example, we have DOM workers and not JS workers. If you want workers for some other (ie non-browser) environment, just write it. It's not hard, and it doesn't need to be part of the language.

# Leo Dutra (7 years ago)

We could think in a pool of workers with dynamic code execution or propose, in JS spec, points where multithreading is recommended.

Anyway... looks like community is OK with the current state and that's more than enough.

Good to see interest, anyways.

# Isiah Meadows (7 years ago)

I've been working on another idea for parallelism that also leverage modules, but doesn't involve workers. It will enable read-only resource sharing of direct object instances across threads, using realms, a built-in concept of thread ownership, and atomicity for ensuring thread safety. It also allows for blocking calls for individual atomic access.

# Isiah Meadows (7 years ago)

I'll post it to the list when it's ready, though.

# Isiah Meadows (7 years ago)

And yes, I've been paying attention to safety. What I'm actually going to be using behind the scenes for the proposal is an event-driven system to coordinate thread access and having both atomic updates and synchronized calls by default, and this system will also work with the event loop as well, with a basic priority system in place.

I'm still working on it, and I plan to have a gist in tandem with this, so I don't have to have a long, detailed email to start. I just want to see something a little lighter than just cloning everything (zero copy preferable), and the ability to have high-level manipulation of shared data without requiring SharedArrayBuffers, a malloc reimplementation, and a boilerplatey abstraction on top of everything.

Isiah Meadows me at isiahmeadows.com

# Henri Tuhola (7 years ago)

Leo Dutra wrote:

looks like community is OK with the current state and that's more than

enough.

This whole mailing list looks like it suffers from a severe case of Dunning-Kruger. Most of you people barely understand what is being discussed and deny the ideas presented because you have irrational love for the concepts that have been already presented and irrational fear of the new concepts. At worst you rationalize your actions based on the popularity rather than merits.

The green thread proposal such as this would be great. Most Javascript programs are interactive and have to run concurrently. With bit of work they wouldn't exclude each other. You could both wait for an event in thread as well as pass a callback for it.

Async/await has at least two problems.

a) Async routines are infective. The problem here is that it forces you to copy/paste otherwise well-behaving programs to make them behave concurrently.

For example, you might want to run a parser such that the callback it runs will trigger a download and wait for the completion before it proceeds.

With async/await, to do this you have to rewrite portions of the parser with async/await keywords to have it run asyncronously. With green threads you could just do it.

b) Async/await is a mini-language on top of an existing one. The problem is that it's too similar to vanilla Javascript.

You already have exceptions in form of reject/catch. Did you already have async/await iterators too?

Promises are just control flow graphs in disguise. Why would you need two when you already have proper constructs to represent control flow? These are complex concepts and excess of them is bad.

The benefits of Async/await over true concurrent model are questionable:

c) Javascript is a dynamic language lacking type annotations prior evaluation. With Async/await you insist that it becomes annotated with two variations for functions prior evaluation.

You insist you should have safety for non-atomic changes of variables in a language where the correctness is up to the user in the first place. Why should we have async/await "safety net" when we don't have types "safety net"?

-- Henri Tuhola, author of leverlanguage.com

# Bradley Meck (7 years ago)

Most of you people barely understand what is being discussed and deny the

ideas presented because you have irrational love for the concepts that have been already presented and irrational fear of the new concepts. At worst you rationalize your actions based on the popularity rather than merits.

Ad hominem.

The green thread proposal such as this would be great. Most Javascript

programs are interactive and have to run concurrently. With bit of work they wouldn't exclude each other. You could both wait for an event in thread as well as pass a callback for it.

Agree if done right.

The problem is that it's too similar to vanilla Javascript.

Similarity is a good thing between language constructs.

You already have exceptions in form of reject/catch. Did you already have

async/await iterators too?

Unclear why this is relevant.

Promises are just control flow graphs in disguise. Why would you need two

when you already have proper constructs to represent control flow? These are complex concepts and excess of them is bad.

JS has been run on event loops, understanding/controlling when you may yield control back to the event loop is why. Agree that too many concepts is a bad thing. Unclear how adding greenlets would alleviate this, in particular things like deferred rendering until your DOM is in a fully formed state so you don't get FOUC when interacting with existing code that does not expect access/invocation of arguments to yield back to thread.

You insist you should have safety for non-atomic changes of variables in

a language where the correctness is up to the user in the first place. Why should we have async/await "safety net" when we don't have types "safety net"?

Adding more warts because of warts is never a compelling argument.

# Wes Garland (7 years ago)

Please do. I have also done some work in this area. I have also implemented asynchronous POSIX signals (fraught with peril) and fork(). Entertaining stuff.

My major problem has always been entrainment of crap from the global object. Although it has been a few years since I looked at this (slightly before ES5 release).

# J Decker (7 years ago)

On Wed, Nov 2, 2016 at 7:44 AM, Michał Wadas <michalwadas at gmail.com> wrote:

Actually, is there any problem that can't be easily solved with message-passing for high-level structures or low-level shared memory buffers?

Yes, meshing dynamic geometries that involve a few 20k faces. If you have a thread do the work, the overhead of srealizing the resulting buffers will kill any benefit.

But; typed arrays can be shared also. (they are with C++ addons in node)

The biggest problem with node's lack of threads is they really need separate but equal heaps. I heard that there's a global heap lock... that wouldn't be required except when allocating addition space for each heap.

To the general - stop treating programmers like idiots. Give us the rope. Let us hang ourselves.

# Michał Wadas (7 years ago)

Why you can't solve it with shared memory buffer? Shared - I mean instance of *SharedArrayBuffer. *

# J Decker (7 years ago)

On Thu, Nov 3, 2016 at 9:10 AM, Michał Wadas <michalwadas at gmail.com> wrote:

Why you can't solve it with shared memory buffer? Shared - I mean instance of *SharedArrayBuffer. *

not in node; in browser ya... where webworkers are threads. (and not in

javascript base) And while firefox is beginning to look appealing again I'm stuck on chrome (native android)

# Isiah Meadows (7 years ago)

Chrome/V8 has it behind a flag IIRC. I forget its exact name, but I know it exists.

# Rick Waldron (7 years ago)
# Leo Dutra (7 years ago)

Workers need serialization, threads would not.

In Node, lack of threading requires a prior spawn of a bunch of native processes and manually build a tasking pool.

Web Workers spawn a specific script (if you respawn the caller you are doing dirty job).

Maybe we should fork projects like the one Isiah brought to us and headbang compiling it with the lovely gyp.

So much emotion. Much doge work. So delicious.

You could simply spawn a thread in almost the same way we use Node streams...

but I won't fight 10 guys alone.

# Bradley Meck (7 years ago)

Leo you can see nodejs/node#2133 for Workers in node that use threads instead of processes.

# Wes Garland (7 years ago)

There is no requirement for a host environment to use any kind of serialization for worker threads. It's completely fine to pass messages which are binary in nature. In fact, I have passed C structs as messages between JavaScript workers.

I don't know why you think this is a fight. You should understand that you are proposing a very, very, very significant modification of the ES standard and you have not yet defined a problem which this work would solve.

# Leo Dutra (7 years ago)

I have defined many times, but you guys are in love with workers.

A little look in Java's Runnables would demonstrate de nature and difference I'm bringing to this thread.

Workers can't even modify DOM directly...

Very different of go routines, Java/Scala threads etc.

Workers require way more control and coding by the nature of their declaration and messaging. A worker lives and awaits... A thread is run against a living spawned process and is garbaged after the usage.

# Michael J. Ryan (7 years ago)

Workers define a clear boundary... In Windows, only the main thread can touch the ui.. and in Linux, threads are almost as expensive as processes...

Just the same, I'm okay with threads, but feel that not having shared state I'd better as you will avoid a large amount of potential bugs. Having clear separation still allows you to solve many problems where threading would help in a clean and clear way.

There's been other discussions of a load a threaded module, which could have a clear line in the sand. I wouldn't even mind coroutines or a good, safe csp implementation... However, those lines would take longer to develop safely and take longer still to lock down appropriately.

Having worked as threads and allowing a lighter weight message would negate a lot of the negatives you mention... It doesn't have to be a serialized message. Adding an immutable object probative would do the trick (given fewer hurdles).

All ui/Dom access needs to be serialized anyway, I don't think that's a good example of why we absolutely need shared state threads.

# Leo Dutra (7 years ago)

Why would UI/DOM access need to be serialized?

And Windows is a great reference of what not to do. .NET as a whole too, since all before C# 6 comes from bad MS and all after is copied from somewhere. NT should be thrown away and be replaced by a sweet BSD layer The only thing they did well are Registered I/O.

Threads are not that good in Linux because a process starup in Linux is blazing fast... And the work are not that great; except when you have a thread pool, and a process pool is quite not possible (at least I've never seen one for general application software).

Hard work is not a reason to discard a gamechanging enhancement.

There's many enterprises which prohibit devs from using threads and some other language features... They presume retard devs.

A bad developer is not JavaScript issue. A JavaScript issue is the impossibility of doing something Java, C, C#, Python, Scala, Haskell, Go and a bunch more do with a simple keyword or method.

# Isiah Meadows (7 years ago)

Inline.

Isiah Meadows me at isiahmeadows.com

On Thu, Nov 3, 2016 at 6:41 PM, Michael J. Ryan <tracker1 at gmail.com> wrote:

Workers define a clear boundary... In Windows, only the main thread can touch the ui.. and in Linux, threads are almost as expensive as processes...

That's actually a symptom of Linux having significantly lighter processes than Windows. Windows keeps the two as completely independent concepts, and make it rough on the developer if they want to have inter-process communication, while Linux (and many BSDs) don't draw such a clear-cut line with processes vs threads. It comes down to a fundamental difference in approach: Unix derivatives have always aimed to support multi-process applications, while Windows aimed to support multi-threaded ones. (Hence, why Node.js's child_process.fork has always created new processes - it started on Linux, and processes are almost as cheap as threads.)

Just the same, I'm okay with threads, but feel that not having shared state I'd better as you will avoid a large amount of potential bugs. Having clear separation still allows you to solve many problems where threading would help in a clean and clear way.

There's been other discussions of a load a threaded module, which could have a clear line in the sand. I wouldn't even mind coroutines or a good, safe csp implementation... However, those lines would take longer to develop safely and take longer still to lock down appropriately.

Having worked as threads and allowing a lighter weight message would negate a lot of the negatives you mention... It doesn't have to be a serialized message. Adding an immutable object probative would do the trick (given fewer hurdles).

All ui/Dom access needs to be serialized anyway, I don't think that's a good example of why we absolutely need shared state threads.

I agree that you already have to serialize stuff for the DOM to some extent, anyways. That's how much of the API works (and it's also how not to design an API).

A good case where high-level shared state would come very handy is with virtual DOM. If you can offload the backing model to another thread, you can keep the application even faster, since you can diff the old DOM while recent UI changes are being processed in another thread.

But without that ability to share those object instances in a GC-aware manner and at high frequency, it's rather difficult to do without generating a lot of garbage. As for why a SharedArrayBuffer is not acceptable, it's because there's no way to grow it in place, in case you have a very large DOM structure.

# Isiah Meadows (7 years ago)

Inline

Isiah Meadows me at isiahmeadows.com

On Thu, Nov 3, 2016 at 10:08 PM, Leo Dutra <leodutra.br at gmail.com> wrote:

Why would UI/DOM access need to be serialized?

It already kind of does when working in single-threaded code. How often do you need to do things like element.appendChild(document.createTextNode(text)), instead of just element.appendChild(text)? How often do you need to do Object.assign(newElem.style, { ... })? How often do you need to use a loop to add an array of detached DOM elements to the live DOM? Sounds like serialization boilerplate to me, just in single-threaded code.

And Windows is a great reference of what not to do. .NET as a whole too, since all before C# 6 comes from bad MS and all after is copied from somewhere. NT should be thrown away and be replaced by a sweet BSD layer The only thing they did well are Registered I/O.

Threads are not that good in Linux because a process starup in Linux is blazing fast... And the work are not that great; except when you have a thread pool, and a process pool is quite not possible (at least I've never seen one for general application software).

I'm guessing you're not too terribly familiar with Node's cluster module? It provides several primitives for building process pools, and is fairly simple to use. Usually, people use it directly, not with a general process pool abstraction.

nodejs.org/api/cluster.html

It's more useful on the server than the client, though.

(Oh, and processes are comparatively cheap in Linux.)

# doodad-js Admin (7 years ago)

I'm guessing you're not too terribly familiar with Node's cluster module? It provides several primitives for building process pools, and is fairly simple to use. Usually, people use it directly, not with a general process pool abstraction.

nodejs.org/api/cluster.html

It's more useful on the server than the client, though.

(Oh, and processes are comparatively cheap in Linux.)

That requires IPC to exchange data between the master and slaves. And currently, IPC is implemented with JSON.stringify/JSON.parse.

Using threads and shared objects, IPC and serialization/deserialization of objects will no longer be required.

# Leo Dutra (7 years ago)

Isiah, the only serialization, or at least heavy one, in DOM is .innerHTML and some other textual stuff. Appending moves pointers and references in the internal DOM representations (a real problem in old browsers like IE was this representations ran in another host Engine).

Appending nodes are blazing fast... And is structural code, not evented as the bad message passing of spawned processes in Node or Web Workers.

Isiah, I'm used to cluster, maybe more than I'd like.

I'm very used to Java threads, and now on Java 8 a Runnable is a Functional interface implementation (means in Java that Runnable.run() became a simple lambda function). This is more than enough similar to what a JS function could either be on a thread API.

If Node abstracted process as a kind of Host object like a DOM Node, messaging would be lighter and probably serialization would be done at C levels, not on JS strings / stringify level.

Java Threads are part of a scope, Node clusters are not. This implies speed, scoping and control differences enough to say Workers/clustered processes are way different from what I propose.

And more, cluster process or process spawn or Web Workers require a particular separated JS ( or you will do dirty stuff putting run JS together with massive ignored JS).

A thread would be as simple as:

thread.run( (thrd) => console.log(thrd.id) )

A simple "callback"/ stream/ whatever you call run out of Event Loop. A while(true) would not lock any other JS operation.

That's the most powerful part of thread spawn and to allow JS devs to do it is much. This with indication for components, like XHR, to run callback in a threaded function out of Event Loop.

Event Loop should do all that Node does and People say it does good: io messaging and no aggregation / loop intensive stuff.

There's no reason to care with mutability since callbacks mutated values today and most new js devs already think it is running on a parallel process (many many people will even call you crazy if u say).

Again, think in a function as any callback today, but running out of the EL.

I can't make a more realistic metaphor.

# Boris Zbarsky (7 years ago)

On 11/4/16 3:16 AM, doodad-js Admin wrote:

Using threads and shared objects, IPC and serialization/deserialization of objects will no longer be required.

Just so we're clear, what will be required, in the VM, is locking around property access and a GC that can deal with multiple threads, right?

SpiderMonkey used to have all that. We removed it because the performance penalties it imposed were not acceptable. Reintroducing it would be a pretty hard sell, for that same reason.

# /#!/JoePea (7 years ago)

Leo's idea that JavaScript code as it is today won't be impacted when async functions are run in separate threads is interesting. It would complicate the JS engine implementation so that locks are needed when different scopes access a variable (something which was formally easily safe due to running on a single thread and now needs to be guarded on multiple threads), but in theory, multiple threads for async functions that don't need guards against each other (detectable with more intricate scope analysis) could be an interesting performance boost.

Suppose the following async function does a cpu-intensive calculation. It would be nice to be able to do this without workers:

async function somethingIntensive(input) {
  // in new thread, automatically because this function isn't touching
outer scope.
  // ... some cryptographic calculation using input ...
  return result
}

// main thread)
somethingIntensive().then(result => {console.log(result)})

Maybe the awaitkeyword didn't even have to be used inside the async function, but just having the async keyword makes it possible for the engine to make a thread if it determines it can do so.

This is a good idea. There's no reason an async function needs to be on the same thread if it doesn't have to be. Maybe there can be heuristics, like "hmmm, this function doesn't touch absolutely anything in the outer scope, and the input is a primitive non-object value so we don't even need to guard anything... let's run it in a new thread".

Or, maybe there can be a new keyword for this specifically if people want it to be explicit:

thread function somethingIntense(input) {...}

The engine could automatically guard stuff in certain ways that would need to be spec'd out. So, in some cases, threading would be useless if the function needs to access stuff from outer scope, and the engine might even be able to not run the function in a separate thread if that makes guarding easier.

# /#!/JoePea (7 years ago)

On Sun, Mar 12, 2017 at 7:11 PM, /#!/JoePea <joe at trusktr.io> wrote:

Leo's idea that JavaScript code as it is today won't be impacted when async functions are run in separate threads is interesting.

​I meant, "Leo's idea that JavaScript code as it is today can work exactly the same (f.e. async functions) while behind the scenes be threaded without impacting end JS-devs is interesting."​

# Isiah Meadows (7 years ago)

For prior discussion on a fairly similar concept, see my proposal 1 and related thread 2.


Isiah Meadows me at isiahmeadows.com

# Wes Garland (7 years ago)

If anybody wants to play with MT ES (I'm not saying I think this is a good idea) -- you might want to dig up a ~ten-year old version of Spidermonkey, perhaps the JS 1.7 release (MT "safety" broke with Array Extras). Then add this code, which implements a basic Thread class: wesgarland/gpsee/blob/master/modules/thread (hopefully that code builds without too much effort.......I stopped using it as soon as it was finished, I did not find the paradigm useful in my work).

The result is lock-free property access (anarchy!) on a global object shared across operating system threads.

You might be able to then prototype a smarter API in ES3 and see how you like it.

I think you might also be able to try similar experiments with Rhino using Java threads.