Hacker News new | past | comments | ask | show | jobs | submit login

I think it would be weird if objects were thread-restricted by default. Concurrency is most fun when it's easy for a thread to access an object it wants.



In fact I thought willy-nilly cross-thread access was the bane of multi-threaded programming. And also the main reason that immutability in languages such as Clojure is considered a desirable feature in support of concurrency.


You say fun, but what you mean is twisted mess.


WebKit uses threads. WebKit is not a twisted mess.


I mean, you could say the same thing about assembly language programs. "Either static or dynamic typing is needed to prevent large programs from becoming a twisted mess" is a pretty uncontroversial statement today, despite the fact that there were some very high quality large assembly language programs written back in the DOS days.

Besides, WebKit doesn't do fine-grained parallelism on the level of parallel styling or parallel layout. My colleague Manish came up to me after a long back-and-forth conversation with bz to figure out which objects were thread-safe and told me "the biggest lesson of Stylo is that adding parallelism to a previously large sequential C++ project is basically impossible". That's because large-scale projects invariably fail to properly document the precise synchronization invariants that each object must uphold in order to function properly. They settle for blanket statements like "the entire render tree is single threaded only" which, while conservatively a correct statement, essentially gives up on fine-grained tracking and makes it very hard to add parallelism later.

By contrast, when you lift concurrency guarantees into the type system, the compiler essentially enforces that the documentation on thread safety is always up to date. This makes it way easier to start with sequential code and add parallelism (and/or concurrency) later as real-world profiling indicates fruitful optimization opportunities.

I think you're way too casually dismissing the very real problems that unrestricted shared state has.


WebKit does a lot of fine grained parallelism.

I think you’re way too casually dismissing how awesome shared memory is.


With all due respect, no, it doesn't do fine grained parallelism in the areas I described. Do I think it would be impossible to add parallel styling or layout to WebKit? No. Do I think it would be difficult? Absolutely, primarily because of the difficulty of finding all the concurrency hazards in existing objects and making them thread-safe. It would be easier with a type system that automatically checks for and highlights such problems. Such a system is precisely what I'm advocating.

And I'm not saying not to use shared memory. I'm saying that shared memory should be controlled by some sort of static or dynamic type system that enforces proper locking or immutability as the case may be. Such a system is hardly a far-fetched idea, as we're successfully using one right now in the style system of nightly Firefox.


It would have been a shame if the OSes on which Firefox runs had similarly limited access to shared memory to one particular "safe" style in the spirit of paternalistic caution.

Would Rust/Servo have been possible without access to the raw rope?


Rust is a relatively hands-off style of thread-safety: it protects against data races but doesn't try to get things like deadlock freedom that other more restrictive systems require. The benefits apply when using locks, or message passing or even raw atomics (and similarly to higher level libraries like data parallelism in rayon).

However, Rust is also explicitly designed to allow access to the "raw rope" when abstractions don't cut it (or when building the abstractions): that is what `unsafe` allows.


As long as we offer a similar `unsafe` option, a stronger type system around mutability+concurrency might have a range of benefits for JS. Rust's definitely has a number of virtues.

A little hard to imagine grafting onto JS though. :-)


This isn't about "paternalistic caution," it's about purposeful and sensible engineering. JavaScript wasn't designed for threading, and it's a less natural fit for it compared to a language like C, so this is understandably a pretty serious undertaking.

Your point is that this may enable some currently unimplementable application in JavaScript. The assumption is that Firefox couldn't have existed without a free-for-all shared state thread model, which is very likely false.

I ask you, what applications does free-for-all shared state make possible that SharedArrayBuffer doesn't make possible?


Imagine having an app that wants to perform a concurrent search on its model while the view and controller keep chugging on the main thread. SharedArrayBuffer would mean that all of your state has to be in an array of primitives. I’d rather use objects, classes, strings, etc. without having to serialize them all the time.

JS is actually a better fit for threading than C, and in many ways it has similar problems. Unlike C, JS has memory safety and concurrency wouldn’t break that. Concurrent programming is a lot easier if you can’t corrupt the heap. Like C, JS has some “global” variables like RegExp.lastMatch (C has errno) that need to be made into thread-locals. My proposal includes thread locals so it would be easy to make lastMatch into a getter that loads from a thread local.


For your parallel search example, the data set has to be extremely large for parallel searching to have a significant improvement.

When does a client-side JS app have access to many GBs of local data that would justify a parallel algorithm? It seems exceedingly rare but maybe you can imagine an example.

If you're talking about a server side app, if your goal is speed, why would you choose JS over C++? It seems more sensible to write the parallel database search in C++ in that case.

As for appropriateness of threading for C over JS: I think the fact that JS is garbage collected makes a threading implementation a nightmare. A naive GC implementation otherwise kills performance: imagine running a parallel computing and having to "stop the world." GC at a conceptual level is inherently "single-threaded" and it will always be a bottleneck in one way or another.


Not parallel searching. Concurrent searching.

The data set only has to be large enough that the search takes more than 1/60th of a second. Then it's profitable to do it concurrently.

GC is not single threaded at all. In WebKit, it is concurrent and parallel, and already supports mutable threads accessing the heap (since our JIT threads access the heap). Most of the famous high-performance GC implementations are at least parallel, if not also concurrent. The classic GC algorithms like mark-sweep and semi-space are straight-forward to make work with multiple threads, and they both have straight-forward extensions that support parallelism in the GC and concurrency to the mutator.


JavaScript can already do concurrent searching. Concurrent is logical, parallel is physical.

Efficient parallel GC is non-trivial to implement. In the most common implementation, you have to pause all threads before you can collect. That will often negate the performance benefits of having independent parallel threads running, especially if they are thrashing the heap with shared objects as you suggest.


Many factors and capabilities went into Firefox's success. While it's easy to enumerate the primitives required in hindsight, I'm doubtful that if OSes of the period had taken a restrictive stance based on contemporary ideas of what should be allowed, that an easy time would have been had.

This is not hypothetical, consider the present. While Firefox on iOS exists, it's just a branding skin over WebKit, due to a similar flavor of security paternalism around JITing code (only bad people write self modifying code :-). If Firefox had needed to differentiate itself originally in such a market, it's doubtful it would have had much success.

A threading free-for-all may be the wrong abstraction to use for many applications, but it has the virtue of being a decent stand-in for the hardware's actual capabilities. It's also close enough to ground truth that most other abstractions can be built on top of it. Imagine how unpleasant building a browser on top of Workers + ArrayBuffer transfer would be (especially given the lousy multi-millisecond latency of postMessage in most browsers). Also, consider that while there is often loud agreement that raw threads are dangerous, after decades of research, there's little consensus on the "right" option amongst many alternatives.

SharedArrayBuffer is nearly as powerful as the proposal, but not quite. For example, while it allows writing a multi-threaded tree processing library, it would have trouble exposing an idiomatic JS API if the trees in the library live in a single SAB (as JS has no finalizers etc. to enable release of sub-allocations of the SAB). The options are either one SAB per tree (which likely performs badly), an API where JS users need to explicitly release trees when done with them, or leaking memory. With the proposal, each tree node could be represented directly as a JS object. The proposal may not be the best way to fix this problem, but we definitely still have gaps in JS concurrency.

Agreed this would be a serious undertaking, however, and not to be lightly considered.

The proposal goes a long way to make the case this can be implemented performantly, but some deep thought should go into how it would alter / constraint future optimizations in JS JITs.


As it stands now, adding threading to JS has a negative expected value. There is more potential downside than potential upside. It's illogical and irrational to undertake the effort under those conditions.

This should be an industry driven decision. Wait for the users of SAB to say it's not meeting their needs, and for them to provide clear reasons why (not hypothetical limitations, not vague falsely-equivalent comparisons to Firefox). Then we can tangibly weigh the pros against the cons.

Right now this is a solution looking for a problem. Your analogy comparing the JS runtime to iOS runtime isn't appropriate, no single company controls the web platform. Mozilla or Google or Apple or Microsoft can push for JS threads if the arguments for it make sense. Compare to WebAssembly.

In fact the evolution of WebAssembly is a good example of how this ought to happen. Imagine if the creator of emscripten opted to instead first propose a new VM/IL for the web? It would never happen because JS was already good enough. It was more natural to use JS first then create the VM with the goal of addressing the limitations encountered with the JS approach.

Let the tangible shortcomings of SAB bubble to the surface. Then we can sensibly design something that effectively addresses those shortcomings. Not a pattern-matched solution looking for a problem.


JavaScript has no type system currently, other than a trivial one with exactly one type. So, adding concurrency via a type system would be like trying to fit a square peg into a round hole.

Also, shared memory with no type system help is a proven technique that is used in shipping software and has been for a long time. This leads me to believe that it would be easier to use our existing full shared memory approach to implementing parallel layout than it would be to use your technique. Just the fact that some research experiment did use a type system and concurrency does not mean that this is actually better than the alternative. It just means that it is an alternative.

As for finding concurrency hazards when making things concurrent, you’re sorta talking to the right guy. That’s my bread and butter. Like every hard thing, it becomes easy with practice. Maybe type system help is for people who don’t practice enough.


> Maybe type system help is for people who don’t practice enough.

That's like advocating that tests are for people who "can't" write bug free code. Is this the attitude of all JSC developers toward engineering abstractions? I would expect designing a system that JITs third-party code from adversarial parties would lead you to be more cautious about code quality, not less.


Tests are great. No harm in tests.

I just don't think that we have sufficient evidence to conclude that using type systems to aid the development of concurrent code actually leads to better concurrent code. Therefore, I err on the side of not using the type system for that purpose so that I can use it for many other things that I think it's really good at.

(Just take a look at JSC's source code if you want to see our extensive reliance on those areas of C++'s type system that we can use to get hard guarantees. It's not that we don't like types. It's that we use types in an evidence-based way in those places where they actually help us catch bugs.)


> I just don't think that we have sufficient evidence to conclude that using type systems to aid the development of concurrent code actually leads to better concurrent code.

This is like saying we don't have evidence that evolution is real.

Type systems are proof systems. If a type system encodes concurrency safety, and a program is valid under that system, then you can be 100% certain that program doesn't have a race condition bug due to shared state between threads.

If a type system can prove buggy concurrent code is invalid code, then by definition it leads to better valid concurrent code.


Lol! You would not be successful as a scientist with this attitude.

A type system is a proof. But the proof in the type system is not a proof that it’s better to use a type system than not. That’s an entirely separate question.

I think that type systems are good at some things. Concurrency isn’t one of them.


I made an argument. What's yours? You just made a statement of opinion without any justification.

Additionally your opinion is wrong, by simple counter example. Rust's type checker already has the ability to prevent data races: https://blog.rust-lang.org/2015/04/10/Fearless-Concurrency.h... This works today.


Any specific parts of JSC one should look into?

Btw, in case others are also wondering: JSC is JavaScriptCore.


All over.

We use smart pointers over raw pointers.

We have probably hundreds of data types defined as classes, which have only very controlled conversions to other data types. See things like DFG::AbstractHeap or B3::ValueRep.


Servo, which pcwalton works on, is largely motivated by the awesomeness of shared memory.


WebKit needs tons of extra effort to keep it from being a twisted mess precisely because it uses threads.


Any well written software requires effort to properly design and maintain. What’s the argument here? “Don’t give them shotguns, they might shoot themselves”?


Don't load the shotguns, turn off the safety, point it at their foot and tell them “please be careful”.


Can you be very specific?


When told that multi-threaded access is hard and messy, "X program uses threads and it's not a mess" is not an argument -- for one because nobody said X program is a mess. Threads being messy is not a transitive property.

Nobody doing threads ever said that they are not messy or that multi-thread access and locking et al are easy.


I do threads and I just said that they are not messy.


Well, nobody sane then if I'm allowed the Scotsman! People also defend all kinds of dangerous constructs or practices blaming other programmers for not being good enough to use them even though statistically even the best programmers suffer from problems derived from them (e.g. buffer overflows, or in the case of threads race conditions, starvation, etc.).

And since we're particularly talking about adding threads to Javascript, here's the opinion of the creator of Javascript itself on threads:

  You must be this tall to hack on threaded systems, and that 
  means most programmers should run away crying. But they 
  don’t. Instead, as with most other sharp tools, the 
  temptation is to show how big one is by picking up the 
  nearest ST code and jamming it into a MT embedding, or 
  tempting race-condition fate otherwise. Occasionally the 
  results are infamous, but too often, with only virtual 
  fingers and limbs lost, no one learns.

  Threads violate abstractions six ways to Sunday. Mainly by 
  creating race conditions, deadlock hazards, and pessimistic 
  locking overhead. And still they don’t scale up to handle 
  the megacore teraflop future.

  https://brendaneich.com/2007/02/threads-suck/
Also, are you the author of TFA? That sure speaks to your skills, but that doesn't mean standard definitions (e.g. on concurrency) or traditional consensus (e.g. on threads being messy) don't apply.


Appealing to authority isn't a valid argument.

Brendan and I have had many discussions about the nature of threading in JS.

The reality is that to get high performance in JS on modern (and future afaict) is that JS will need to have some lower cost threading mechanism than that provided by workers.

If you read the article you'll see that there isn't any way to mismatch lock/unlock.

The other massive footgun is the for whatever reason people insist on creating standard libraries that are not thread safe (collections, apparently bigint in java?). JS is never going to have completely undefined behavior, but non-determinism already exists.

Is concurrent code easier to screw up than single thread? yes. Is that cost enough to discount the possibility of ever having threads? I don't know. The article goes one way, you clearly go the other.


>Appealing to authority isn't a valid argument.

Calling out "argument from authority" when someone posted actual arguments an authority made is even less valid.

>The reality is that to get high performance in JS on modern (and future afaict) is that JS will need to have some lower cost threading mechanism than that provided by workers.

Perhaps, but this is an orthogonal concern as to the issues with threads themselves (not to mention with an environment that has since forever assumed a single thread of execution).


I think that someone taught you the wrong definition of concurrency. I’m just trying to help you learn the right one.


Oh ffs I feel like I'm back in 1994 or so...


Because there was some huge breakthrough since eliminating threading issues?

(If you answer just use Erlang, CSP or any other variation, well, my point exactly).


The only breakthrough is that some of us learned to use threads. Apparently some of us didn’t.


Apparently, after decades of CS and tons of costly errors, some of us still haven't learned than issues like memory safety, race conditions, etc are either handled at the language level, or are dangerous for everybody -- and that those that consider themselves immune to them because they "know how to use them" and are "better programmers" are just deluded.


You just called WebKit a twisted mess. I asked you for examples. Looks like you don’t have any.

Like I said, WebKit uses threads and it’s great. Maybe some day you will also learn to use threads.


coldtea literally never said that WebKit was a twisted mess.

robertwhiuk, responding to you, referenced using "twisted mess"

pizlonator:

  I think it would be weird if objects were thread-
  restricted by default. Concurrency is most fun when it's
  easy for a thread to access an object it wants.
robertwhiuk:

  You say fun, but what you mean is twisted mess. 
No reference yet to WebKit until your response:

  WebKit uses threads. WebKit is not a twisted mess.
coldtea chimed in with this:

  WebKit needs tons of extra effort to keep it from being a
  twisted mess precisely because it uses threads.
What is that "extra effort"? Discipline, care, good work. coldtea does not call WebKit a twisted mess. In fact, they seem to be saying the opposite. That it is not a twisted mess despite using threads.

You have some good points in this discussion, generally. But you should at least be honest in your characterization of other people's comments.

  Maybe some day you will also learn to use threads.
Maybe someday you will also learn reading comprehension.


coldtea was unable to come up with a single example of where WebKit is a twisted mess or even needs effort to avoid becoming one. No matter what kinds of semantics you throw at this, I think it’s the case that these arguments against threads lack basis.


>coldtea was unable to come up with a single example of where WebKit is a twisted mess

That's because coldtea (me) never said it IS a twisted mess.

What I said is that with threads, WebKit (and any program for that matter) needs extra effort to not be a twisted mess. Exactly what the parent explained again, and you don't seem to have got even this second time.

Why that is the case (in other words, why parallel programming with threads is harder and requires more effort than without), is CS 101.

Some reasons?

Synchronization and scheduling access to resources. Race conditions -- data Races, deadlocks, etc. Starvation. Balancing the number of threads (diminishing returns). Locking subtleties. VM/state/etc overhead of threading.

In fact you'd be hard pressed to find seasoned programmers that would disagree that threads are problematic and require extra caution.

Heck:

  "Although threads seem to be a small step from sequential computation, in fact, they 
  represent a huge step. They discard the most essential and appealing properties of 
  sequential computation: understandability, predictability, and determinism. Threads, as a 
  model of computation, are wildly nondeterministic, and the job of the programmer becomes one 
  of pruning that nondeterminism" -- https://www2.eecs.berkeley.edu/Pubs/TechRpts/2006/EECS-2006-1.pdf


You won’t weasel out on this that easily. You still have not provided anything other than the usual BS reasons for why threads require extra effort.

You know, drawing stuff to the screen also requires effort. As does file I/O. It’s computing - people get things wrong. The interesting question is: are threads so unusual in this regard that your whining makes any sense?

We have lots of threads but except when we are in the middle of something big and concurrency related like concurrent GC, we have very few bugs of the sort you describe. Most of the bugs that bother me right now are in the compiler and runtime and they are not concurrency bugs at all. It’s true that if someone is deliberately trying to increase concurrency, they will fix some concurrency bugs along the way just like a person writing compiler phases will fix compiler bugs alone the way. We haven’t given up on compilers just because they are might have bugs.

Finally, you said: “WebKit needs tons of extra effort to keep it from being a twisted mess precisely because it uses threads.” You seem to have now significantly walked back from this statement since all you can come up with are reasons why threads are hard that are not related to WebKit or any specific software package. Sounds like maybe you just don’t know how to use threads so you spread FUD to avoid having to learn them.


>What is that "extra effort"? Discipline, care, good work. coldtea does not call WebKit a twisted mess. In fact, they seem to be saying the opposite. That it is not a twisted mess despite using threads.

Exactly, thanks.


Imagine how much more fun it would be if threads sometimes got access to objects they didn't want!


I think the proposal was to thread-restrict variables, not objects. (Or more precisely, any variable is either restricted to a thread or guarded by a lock.) So it wouldn't actually stop you from accessing any object you want, as long as you can reach it from something in scope. But it also might not be a very useful safety guarantee.


Thread.restrict is about objects.

It’s interesting to also have a function like that for properties and/or variables.


Yeah, if by "fun" you mean "catastrophic"




Consider applying for YC's Spring batch! Applications are open till Feb 11.

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: