4 ms·
> It doesn't matter that it's all single threaded if all your function calls may or may not block and run a bunch of other code in the meantime, mutating all ki
by pseudoramble 7y ago
> It doesn't matter that it's all single threaded if all your function calls may or may not block and run a bunch of other code in the meantime, mutating all kinds of state.
Can you elaborate a bit more on this? I'm unsure about how locking in a single-threaded environment would work. And how would it really differ from async/await or promises which can handle race conditions already?
One area where the lack-of locking primitives does worry me is the use of shared mutable memory. I've never used these techniques in JS, but if I remember right one can now do something like this:
1. Create a shared memory array
2. Spawn several workers which do work on shared arrays
3. Pass a reference to the array to each worker and let them run.
This seems like an area where we could start to see these problems start appearing. There's a module related to atomic operations [1] but there is no language-level enforcement of using it. There could be good reasons why this isn't worrying but I just don't happen to know that off hand.
[1]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
- trust07007707 7y agoThere is an Atomics API for that...
- shanemhansen 7y agoIn javascript anywhere you see await, you've introduced an explicit scheduling point. There are cases where even in a single threaded env you want to "wait" until some other async process is complete. To use one of the old school examples: function transfer(amount, acct1, acct2) { var current_balance = await acc1.balance() if current_balance > amount { await acct1.sub(amount) await acct2.add(amount) } } Now what happens if you get multiple calls to transfer? What you want is probably something like: function transfer(amount, acct1, acct2) { await acct1.Lock() .... await acct1.UnLock() }
- deleted 7y ago[deleted]
- pseudoramble 7y agoGood point! I hadn't thought of it this way since it often doesn't come up as often in my experiences with front end stuff. To lay out one example as clearly as I can: suppose you had two calls to transfer with the same args. * The 2nd call to `await acc1.balance()` was very quick beating the 1st call * The `current_balance` is greater than `amount` and so it runs `await acct1.sub(amount)` * Before that can finish, the 1st call to `balance()` returns with the old balance before subtraction begins. * Now another transfer is initiated with the incorrect amount. If `amount` is larger than `current balance` you've got an issue on your hand. I believe one could implement a mechanism around this using a set of booleans/ints for each account and managing the call to `transfer` with each one of those. But that's the point - we could have primitives to do that. By the way, there is a particular issue with this specific example. The `transfer` function signature must be marked as `async`. You would need to add this to make it run properly.
- hombre_fatal 7y agoI don't think it really makes sense to essentially say "Javascript is rife with race conditions like any other language" just because yes, you still need to use transactions when using a database.
- vidarh 7y agoYou're reading too much into the example. Your 'database' can be your frontend js state. There is nothing in the example that limits the problem to backend development.
- jeswin 7y agoYeah but the impact of race conditions on front-end code (which is always single user) is minimal; unlike the backend where it's catastrophic and not fixable by refreshing the page.
- vidarh 7y agoIt is fixable by refreshing the page if there are no race conditions in the front-end. If anything getting this right on the front-end is often extra problematic because of the high cost of round-tripping state to the server.
- olalonde 7y agoIn that case, the code would be sync and there wouldn't be a "race condition".
- vidarh 7y agoI just the other day had to step in to deal with a race condition in a frontend caused by improperly handling async API requests. It's dangerous to assume that just because JS has a single main thread that you don't need to think about sequencing of operations and locking.
- 7y ago
- blacksoil 7y agoGood example. I think this should be handled by whatever data store is being used, though. In case of SQL, transaction.
- asoo 7y agoYou'd need to lock both accounts... but even then. Doing any kind of accounting operations without being atomic, transactional or idempotent is just wrong. Even with single call - acct2.add() could fail and you're left with broken state. If this is in-memory state then you don't need asyncs at all. If it's not, Locks just give you false sense of safety.