Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> Despite benefits, I don't actually think the memory safety really plays a role in the usage rate of parallelism.

I can see what you mean with explicit things like thread::spawn, but I think Tokio is a major exception. Multithreaded by default seems like it would be an insane choice without all the safety machinery. But we have the machinery, so instead most of the async ecosystem is automatically multithreaded, and it's mostly fine. (The biggest problems seem to be the Send bounds, i.e. the machinery again.) Cargo test being multithreaded by default is another big one.





> Multithreaded by default seems like it would be an insane choice without all the safety machinery

You're describing golang, and somehow it's fine. Bugs are possible, but not super common


Isn't that "somehow" super attributable to the fact that Go is garbage collected?

Garbage collection is the one other known way to achieve memory safety.


You raise a good point here. When I think about writing multi-threaded code, three things come to mind about why it is so easy in Java and C#: (1) The standard library has lots of support for concurrency. (2) Garbage collection. (3) Debuggers have excellent support for multi-threaded code.

Not really, especially as garbage collection doesn't achieve memory safety. Safety-wise, it only helps avoid UAF due to lifecycle errors.

Garbage collection is primarily just a way to handle non-trivial object lifecycles without manual effort. Parallelism happens to often bring non-trivial object lifecycles, but this is not a major problem in parallelism.

In plain C, the common pattern is trying to keep lifecycles trivial, and the moment this either doesn't make sense or isn't possible, you usually just add a reference count member:

    struct some_type {
        uint32_t refcnt;
        uint32_t otherfields;
    };

    struct some_type *some_type_ref(struct some_type *a) {
        a->refcnt++;
        return a;
    }

    void some_type_unref(struct some_type *a) {
        a->refcnt--;
        if (a->refcnt == 0) {
            free(a); // or some_type_destroy(a);
        }
    }
In both Go and C, all types used in concurrent code needs to be reviewed for thread-safety, and have appropriate serialization applied - in the C case, this just also includes the refcnt itself. And yes you could have UAF or leak if you don't call ref/unref correctly, but that' sunrelated to parallism - it's just everyday life in manual memory management land.

The issues with parallelism is the same in Go and C, that you might have invalid application states, whether due to missing serialization - e.g., forgetting to lock things appropriately or accidentally using types that are not thread safe at all - or due to business logic flaws (say, two threads both sleeping, waiting for the other one to trigger an event and wake it up).


Kind of, but Go isn't memory-safe in the face of concurrent data races.



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

Search: