News for hackers. Stuff that matters.
Re:V8 adds support for top-level await by wayneftw on Tuesday September 24, 2019 @06:59PM (#21065567)
In a single-threaded universe, what more do you need to lock the world than a boolean variable?
Re:V8 adds support for top-level await by nitely on Tuesday September 24, 2019 @09:14PM (#21066591)
Since booleans can't be awaited until their state is negated/toggled, I'd say a lock that works like a lock is needed.
Re:V8 adds support for top-level await by wayneftw on Tuesday September 24, 2019 @10:05PM (#21066858)
So use a Promise like a lock then. If it exists, wait on it. If not create one…

Of course you don’t need to use the existence of a Promise as your Boolean in this case. You can simply use the Boolean state in addition to the Promise because your code is not going to yield while atomically setting one Boolean variable.

Re:V8 adds support for top-level await by nitely on Wednesday September 25, 2019 @12:32AM (#21067623)
That would unblock all coroutines waiting for the promise, instead of just the first one. It's not that trivial, that's the point.
Re:V8 adds support for top-level await by esprehn on Wednesday September 25, 2019 @12:59AM (#21067740)
For queued ordering instead of unblocking everyone at once you can reassign the Promise.

  let lock = Promise.resolve()
  const wait = (callback) => lock = lock.then(() => callback());
Until the callback resolves the lock is held, so you can do:

  wait(async () => {
    await step1();
    await step2();
    // release the lock.
  });
Re:V8 adds support for top-level await by nitely on Wednesday September 25, 2019 @02:45AM (#21068336)
If the callback throws an error, none of the awaiting coroutines will run, and adding a catch would break the callback error propagation.
Re:V8 adds support for top-level await by wayneftw on Wednesday September 25, 2019 @09:17AM (#21070449)
Yes, but he was giving the simplest implementation for the sake of brevity.

Also, nobody said that we need callback error propagation in a priority queue to act the same exact way that callback error propagation works in a straight promise chain... but if you just want to reject all waiters in case of an error, you can do that.

Take a look at sindresorhus' p-queue project - https://github.com/sindresorhus/p-queue - which is about 40 lines of code that take care of queueing exactly the way you want. And it's easy to modify if you want to reject the whole queue in case of an error, which is something that I've done myself in the past. You can see them discussing the possibility of that feature right here - https://github.com/sindresorhus/p-queue/issues/29

Long story short: Your problem is with ordering, not locking.

Re:V8 adds support for top-level await by nitely on Wednesday September 25, 2019 @01:45PM (#21073172)
Even the original solution is a lot more complex than just checking some primitive value. A lock is still needed when doing concurrency on a single thread, which is the point I'm making anyway.

> Long story short: Your problem is with ordering, not locking.

Look up the definition of a concurrency lock.

Re:V8 adds support for top-level await by wayneftw on Wednesday September 25, 2019 @01:31AM (#21067907)
You moved the goal posts on me. But the solution for your next scenario is actually pretty trivial though isn’t it?

You don’t really need what you call locking in a single-threaded world. You just need a state machine.

Re:V8 adds support for top-level await by nitely on Wednesday September 25, 2019 @02:47AM (#21068350)
No, I didn't. That's how a lock works. Only one coroutine (or thread) is allowed to hold it at a time while the others must await their turn. A state machine is a lot more than a boolean variable.
Re:V8 adds support for top-level await by wayneftw on Wednesday September 25, 2019 @09:02AM (#21070325)
Both of your statements are incorrect. Locks are an abstract concept that have many different behaviors. In a multi-threaded world (not JS) a certain type of synchronization lock, such as a single-writer/multiple-reader locking mechanism would specifically allow you to unblock all waiting coroutines. Another type of lock might only allow the first waiter to unblock.

There simply is no concurrency in JS though, so there is no need for locking. There is no way that you would ever "unblock all coroutines waiting for the promise" because you just can't run more than 1 coroutine at a time. So your problem is with ordering, not concurrency and locks are a solution for concurrency.

And the simplest state machine is a boolean variable.

Anyway, I guess if you still disagree then you can go and ask the people who build JS engines why they don't want to add locks. Maybe ask the v8 team - they add stuff that isn't in the spec all the time and they haven't seen a big need for this, so they must know the answer...

Re:V8 adds support for top-level await by nitely on Wednesday September 25, 2019 @01:56PM (#21073313)
First look up the definition of a lock and mutual exclusion. There are two types of basic locks: mutexes and semaphores. Both keep two threads or coroutines from executing a block of code/instructions at the same time, and both work in a similar way I already described.

> There simply is no concurrency in JS though,

There is no parallelism in JS (well there is with workers now), concurrency is not parallelism.

Re:V8 adds support for top-level await by wayneftw on Wednesday September 25, 2019 @05:07PM (#21075486)
Show me a piece of code where you think you need locks I guess.

I will concede that I got the two terms confused, mostly because I haven’t had to think about this kind of stuff in years because there’s literally nothing I’ve ever had to do with JavaScript that’s required me to think about it.

But I’d really love to see some code where you can’t handle concurrently waiting coroutine priorities in a single threaded world. Concurrency without parallelism is not really a problem the way I see it.

Re:V8 adds support for top-level await by eropple on Tuesday September 24, 2019 @07:43PM (#21065961)
There are more and more valuable locking mechanisms than a bool. Read-write locks, for example, are still sometimes (not often but I've done it on occasion) valuable in NodeJS.
Re:V8 adds support for top-level await by manigandham on Tuesday September 24, 2019 @09:39PM (#21066715)
Only the single JS thread is ever reading or writing a value so what is the lock for?
Re:V8 adds support for top-level await by eropple on Tuesday September 24, 2019 @10:58PM (#21067171)
You get that it's super common to await in the middle of something that could be considered a critical section, yeah? And that doing so will result in a different coroutine (for lack of a better term; that's what Promise-driven code effectively is) being executed until control returns to the awaited Promise within that critical section?
Re:V8 adds support for top-level await by manigandham on Tuesday September 24, 2019 @11:15PM (#21067260)
Yes but async/await is for concurrency, not parallelism. Only one or the other "coroutine" will run at any time so what is the lock protecting? There's no way for multiple branches of code to access the same variable at the same time in JS. What else would a reader/writer lock be used for?
Re:V8 adds support for top-level await by eropple on Wednesday September 25, 2019 @12:13AM (#21067532)
Locks are for concurrency. Parallelism is orthogonal.

When you are forced to yield inside of your critical section (a database call, a file write, whatever), as is common in NodeJS, you must acquire a lock that another coroutine can't run through.

Re:V8 adds support for top-level await by wayneftw on Wednesday September 25, 2019 @01:53AM (#21068037)
He got the terms wrong but he’s absolutely right.

> There's no way for multiple branches of code to access the same variable at the same time in JS.

This is 100% true and it’s the very reason why we do not need locks. There is no thing called a critical section in JavaScript because in JavaScript all of your code runs on a single thread and it’s all equal therefore none of it is more volatile than any other piece code. All that you need to manage asynchronous concurrency is good state management, not “locks”, because there is nothing to lock since there are not multiple threads asking any of your JS code for control of execution at the same exact time. It’s all scheduled for you onto your single thread.

With that, what you don’t get in JS of course, is shared-state parallelism.

Re:V8 adds support for top-level await by eropple on Wednesday September 25, 2019 @02:07AM (#21068120)
None of the concerns of concurrency go away because you don't have threads, they go away when you have no multitasking, and NodeJS is a cooperatively multitasking environment (modulo a few minor asterisks).

So of course there are still critical sections--a critical section is a set of operations that must have uninterrupted access to a resource in order to maintain a desired level of consistency. If you have to yield (that is, `await` on a Promise or wait for the invocation of a callback), which sometimes you do have to do (i.e., you have to do IO, you've got a crunchy computation running in C++ outside of the JS loop, whatever), then you need to prevent access to that resource from whatever is being picked up next by the JS runtime while the first user is waiting for that IO to finish or that computation to come back.

To that end, locks are that "good state management" to which you refer. Most of the time one can use a mutex--the `async-lock` library is common--but I've even encountered cases where resource management must occur in structured forms and it was necessary to implement a read-write lock, where multiple readers can operate in tandem but all readers must close out before a writer can take control.

The basics don't change when your multitasking is cooperative rather than preemptive--all the same stuff still applies. This is not a controversial set of assertions. I don't understand at all where you're coming from.

Re:V8 adds support for top-level await by manigandham on Wednesday September 25, 2019 @04:22PM (#21074915)
Your note about 'async-lock' NPM package clears up the confusion. That's used to serialize operations to external systems, with their readme showing an example with multiple Redis calls within await blocks setting the same key incorrectly.

This conversation was talking about access to variables and data within Javascript code itself. Any language is open to data races when dealing with external systems. I can see why having a `lock` construct can be helpful here, but it seems extraneous when the actual language doesn't support concurrent access and simple packages already exist. The 'async-lock' is just a wrapper around a few variables, and only works precisely because JS is already single-threaded and serialized.

Re:V8 adds support for top-level await by wayneftw on Wednesday September 25, 2019 @08:47AM (#21070219)
When you have to bring C++ into the conversation to make your point, then it sounds like it’s a problem for C++…

There simply is no concurrency in JavaScript, so lock primitives are not solving any problem that JavaScript has.

Can you give an example of some code where you think locks would help you?

Re:V8 adds support for top-level await by nickitolas on Wednesday September 25, 2019 @06:59AM (#21069499)
I don't think a critical section is necessarily related to parallelism, but concurrency (As shown in other examples in this thread). If you google for "critical section", the first link (Wikipedia) begins "In concurrent programming,".

Regardless, if you were to have need of a lock, which was the basis of this chain of comments unless I'm missremembering, a read-write lock could improve performance, regardless of things being singlel threaded, precisely because of things being async (If you have N async tasks all read-only locking an rwlock, they can go on to fire async requests, which will run concurrently, despite the execution being single threaded).

Re:V8 adds support for top-level await by sosodev on Tuesday September 24, 2019 @07:22PM (#21065778)
Nothing really, but you'll still want be sure the lock is released correctly.