Re: "is not an lvalue" when passing template function to spawn function
On Thursday, 9 November 2023 at 10:14:46 UTC, Bienlein wrote: On Thursday, 9 November 2023 at 09:40:47 UTC, Bienlein wrote: On Wednesday, 8 November 2023 at 16:47:02 UTC, Paul Backus wrote: On Wednesday, 8 November 2023 at 16:30:49 UTC, Bienlein wrote: ... The actual problem here is that you can't take the address of a template without instantiating it first. To make your example work, replace `&addToBiz` with `&addToBiz!int`, like this: spawn(&addToBiz!int, biz); All right. It seems I cannot pass on an object. So I store the object in a global and access it from the callback function passed to spawn.
Re: "is not an lvalue" when passing template function to spawn function
On Thursday, 9 November 2023 at 09:40:47 UTC, Bienlein wrote: On Wednesday, 8 November 2023 at 16:47:02 UTC, Paul Backus wrote: On Wednesday, 8 November 2023 at 16:30:49 UTC, Bienlein wrote: ... The actual problem here is that you can't take the address of a template without instantiating it first. To make your example work, replace `&addToBiz` with `&addToBiz!int`, like this: spawn(&addToBiz!int, biz); Thanks, Paul. This helped a step further. When applying your change it looks like this: Biz!int biz = new Biz!int(123); spawn(&addToBiz!int, biz); Then I get this error: 'Error: static assert: "Aliases to mutable thread-local data not allowed."' For this error I found this in the Internet: https://stackoverflow.com/questions/14395018/aliases-to-mutable-thread-local-data-not-allowed But this change, did not help: spawn(&addToBiz!int, cast(shared) biz); Then I moved "Biz!int biz = new Biz!int(123);" out of the main function. Compiler complains about static this. Okay, then the code outside the main function now looks this way: class Biz(T) { private T value; this(T value) { this.value = value; } } static void addToBiz(T)(Biz!T biz) { // ... } Biz!int biz; static this() { biz = new Biz!int(123); } int main() { // ... } However, this results in no gain as the compiler now shows the initial error again: 'Error: static assert: "Aliases to mutable thread-local data not allowed."' Everything I tried on my own was also to no avail. If someone could gould give me a hint again ... ;-) Thank you. If I supply a callback function with the parameter not being an instance from a parameterized class I get the same error. The problem seems to be that the parameter of the callback function takes on object as a parameter and not a built-in type like int or String. The samples on how to use the spawn function on dlang.org does not contain a sample on how to get things to work with a objecgt being supllied as parameter to the callback function
Re: "is not an lvalue" when passing template function to spawn function
On Wednesday, 8 November 2023 at 16:47:02 UTC, Paul Backus wrote: On Wednesday, 8 November 2023 at 16:30:49 UTC, Bienlein wrote: ... The actual problem here is that you can't take the address of a template without instantiating it first. To make your example work, replace `&addToBiz` with `&addToBiz!int`, like this: spawn(&addToBiz!int, biz); Thanks, Paul. This helped a step further. When applying your change it looks like this: Biz!int biz = new Biz!int(123); spawn(&addToBiz!int, biz); Then I get this error: 'Error: static assert: "Aliases to mutable thread-local data not allowed."' For this error I found this in the Internet: https://stackoverflow.com/questions/14395018/aliases-to-mutable-thread-local-data-not-allowed But this change, did not help: spawn(&addToBiz!int, cast(shared) biz); Then I moved "Biz!int biz = new Biz!int(123);" out of the main function. Compiler complains about static this. Okay, then the code outside the main function now looks this way: class Biz(T) { private T value; this(T value) { this.value = value; } } static void addToBiz(T)(Biz!T biz) { // ... } Biz!int biz; static this() { biz = new Biz!int(123); } int main() { // ... } However, this results in no gain as the compiler now shows the initial error again: 'Error: static assert: "Aliases to mutable thread-local data not allowed."' Everything I tried on my own was also to no avail. If someone could gould give me a hint again ... ;-) Thank you.
"is not an lvalue" when passing template function to spawn function
Hello, I get the error "`addToBiz(T)(Biz!T biz)` is not an lvalue and cannot be modified" when compiling the code below. Can't find a way how to do it right. Am a D newbie and would appreciate some help. Thank you, Bienlein class Biz(T) { private T value; this(T value) { this.value = value; } } static void addToBiz(T)(Biz!T biz) { // ... } int main() { auto biz = new Biz!int(123); spawn(&addToBiz, biz); }
Re: Way to pass params to a function passed to a fiber?
On Monday, 3 October 2022 at 10:13:09 UTC, Rene Zwanenburg wrote: On Monday, 3 October 2022 at 08:10:43 UTC, Bienlein wrote: My question is whether someone has an idea for a better solution. You can pass a lambda to the fiber constructor. For example: ``` void fiberFunc(int i) { writeln(i); } void main() { auto fiber = new Fiber(() => fiberFunc(5)); fiber.call(); } ``` Oh, that simple... Thanks a lot :-).
Way to pass params to a function passed to a fiber?
Hello, I'm looking for a way to pass parameters to a function called by a fiber. Starting a fiber works like this: int main() { auto fiber = new Fiber(&myFunc); fiber.call(); fiber.call(); return 0; } void myFunc() { writeln("Fiber called"); Fiber.yield(); writeln("Fiber recalled after yielding"); } In the code above there is no way to add parameters to myFunc. The construcor of class Fiber does not allow for a function to be passed that has parameters (unlike for the spawn function when starting a thread). I ended up with something like this: class Foo { public int i; } Foo foo; static this() { foo = new Foo; } int main() { auto fiber = new Fiber(&myFunc); fiber.call(); fiber.call(); return 0; } void myFunc() { import std.stdio; writeln("foo: ", foo.i); foo.i++; Fiber.yield(); writeln("foo: ", foo.i); } But this solution is a bit clumsy. It's kind of programming with global variables. My question is whether someone has an idea for a better solution. Thank you, Bienlein
Re: null == "" is true?
why? Because an empty string is, by default, represented by an empty slice of the null pointer. I don't program in D. I just read from time to time posts in the D forum because of the good quality of what people write. So, I'm not proficient in D, but in general internals should not boil up to the surface. In my experience null and empty in DTOs usually play the same logical role. Oh, oh ... IIRC someone wrote a master thesis about the different roles for null values in databases >and came up with many different null situations (was it five?). Oh, oh, oh ... I once worked on a system where some little robot running on a track picked up material in carriers from some machine and then brought it to the next machine. If the destination of a carrier was set to null, it implied that the destination was currently undefined. Then the robot brought the carrier to some rack where it was put aside for a while till the planning system had created a new production plan. The number of null pointer exceptions we had to fix because auf this was countless. Never make null imply some meaning ...
Re: How to call destructor before free without dropping @nogc?
This works, vit. Thanks! I thought it wouldn't, because your code still makes use of embrace. But it somehow worked, although I don't understand why ... ;-). I also added a constructor using the same approach as your destructor and this also worked: this(int otherNum) @nogc { this.num = otherNum; debug writeln("this: ", this.num); } @evilrat: Will try what you suggested after work today. Too busy now.
Re: How to call destructor before free without dropping @nogc?
On Thursday, 19 August 2021 at 07:30:38 UTC, Bienlein wrote: Hello, I allocate some instance of class C manually and then free the memory again: class C { int num; ~this() { writeln("~this"); } } void foo() // @nogc { auto mem = cast(C)malloc(__traits(classInstanceSize, C)); auto c = emplace!(C)(mem); c.num = 789; destroy(c); free(cast(void*) c); c = null; } int main() { foo(); } The code above works well as the destructor of c in class C is called by destroy. Problem is that destroy cannot be used once function foo is annotated with @nogc. There seems to be no way round it. What I want is to keep the function foo annotated with @nogc, but still have the destructor of C be called before free is called. Is there a way to call the destructor through meta programming or some kind of reflection so that I can create some generic function that calls the destructor and then free for any kind of class? Thanks, Bienlein Oops, I just realized that you can also not call emplace when @nogc is present. Well that is at least consistent with not either being able to call destroy ;-). So, I guess this means that you can forget about manually allocating and freeing some instance of a class and using @nogc as well. That's a pitty, @nogc was a good idea.
How to call destructor before free without dropping @nogc?
Hello, I allocate some instance of class C manually and then free the memory again: class C { int num; ~this() { writeln("~this"); } } void foo() // @nogc { auto mem = cast(C)malloc(__traits(classInstanceSize, C)); auto c = emplace!(C)(mem); c.num = 789; destroy(c); free(cast(void*) c); c = null; } int main() { foo(); } The code above works well as the destructor of c in class C is called by destroy. Problem is that destroy cannot be used once function foo is annotated with @nogc. There seems to be no way round it. What I want is to keep the function foo annotated with @nogc, but still have the destructor of C be called before free is called. Is there a way to call the destructor through meta programming or some kind of reflection so that I can create some generic function that calls the destructor and then free for any kind of class? Thanks, Bienlein
Re: What's the best way to find out which exceptions may be thrown ?
On Wednesday, 27 May 2020 at 11:40:00 UTC, Mike Parker wrote: On Wednesday, 27 May 2020 at 10:30:36 UTC, wjoe wrote: On Wednesday, 27 May 2020 at 10:01:33 UTC, Mike Parker wrote: Could you please elaborate why checked exceptions are more annoying? For me, it's because they require all functions that touch them to either try/catch or include an exception specification in its declaration. In my Java days, I ended up just doing what so many others do and adding `throws Exception` or `catch(Exception)` to avoid having to handle multiple exception types. Most of the time, I didn't care what specific sort of exception was thrown. Because of the problems with checked exceptions they were deliberately left out in C#. Here is an interview with Anders Hejlsberg, the creator of C# at MS, where he explains the reasons for this decision: https://www.artima.com/intv/handcuffs.html
Re: Alternative to friend functions?
On Tuesday, 18 February 2020 at 12:43:22 UTC, Adnan wrote: What is the alternative to C++'s friend functions in D? module stable_matching; alias FemaleID = int; alias MaleID = int; class Person { string name; int id; } class Male : Person { this(string name = "Unnamed Male") { static int nextID = 0; this.id = nextID++; this.name = name; } } class Female : Person { this(string name = "Unnamed Female") { static int nextID = 0; this.id = nextID++; this.name = name; } } class Husband(uint N) : Male { FemaleID engagedTo = -1; const FemaleID[N] preferences; this(FemaleID[N] preferences) { this.preferences = preferences; } } class Wife(uint N) : Female { FemaleID engagedTo = -1; const MaleID[N] preferences; this(MaleID[N] preferences) { this.preferences = preferences; } } void engage(N)(ref Wife!N, wife, ref Husband!N husband) { // Here, I want to access both husband and wife's engaged_to } class MatchPool(uint N) { Husband!N[N] husbands; Wife!N[N] wives; } I would make Husband and Wife subclasses of a common abstract superclass Spouse that declares the engagedTo var. The Spouse superclass would also be the place where to put the engage method. What is different for males and females you can redefine in the respective subclass.
Re: Functional Programming in D
On Thursday, 10 October 2019 at 16:05:13 UTC, bachmeier wrote: On Thursday, 10 October 2019 at 08:59:49 UTC, Russel Winder wrote: My impressions is that the complaints about Scala are similar to C++: too many features that clash with one another and make the language complicated, plus extremely slow compilation times. I haven't seen a lot of complaints about mixing imperative and functional. Scala compile times are slow, because Scala has more compilation phases than C++. I guess that is because of feature bloat in the language as such, IMHO not necessarily because FP and OOP are mixed into the same language. Scala is just packed with too many language constructs that are also in many cases quite extensive. Then there is a problem with implicit conversions in Scala not being scalable in compilation times, see https://dzone.com/articles/implicits-scala-conversion In Scala3 (due to be released in spring 2020) implicits were replaced by what they call delegates (and extension methods were introduced). Whether that reduces compilation times I don't know. But the compiler in Scala3 is based on a complete new approach to further reduce compilation times. However, Scala3 is a new language. Whether people will make the move from Scala to Scala3 remains to be seen.
Re: Functional Programming in D
On Thursday, 10 October 2019 at 10:08:14 UTC, H. S. Teoh wrote: On Thu, Oct 10, 2019 at 09:59:49AM +0100, Russel Winder via Digitalmars-d-learn wrote: On Wed, 2019-10-09 at 11:12 -0700, H. S. Teoh via Digitalmars-d-learn wrote: […] > Actually, std.functional is somewhat of a misnomer. It > mostly deals with higher-order functions, i.e., functions > that return functions, currying, that sort of thing. These > are part of functional programming, but there's more to > functional programming than that. I'd say std.range and > std.algorithm are another major part of functional-style > programming support in D, along with the purity system. […] I feel that it is best to leave functional programming to functional programming language, e.g. Haskell, Scheme, etc. rather than try to do functional programming in imperative languages, e.g. Java, C++, Rust, D. [...] Note this is why I wrote "functional-style programming" w.r.t. D, rather than "functional programming". Clearly, what D has isn't "real" functional programming in the strict sense, but it does share similar characteristics when written in that style. I did programming in Smalltalk for about 10 years. Smalltalk has all those things that are now called "functional-style programming" or even IMHO incorrectly "functional programming" that are about applying operations on collections. In Smalltalk these functions (like collect, map, reject, detect, inject, etc.) are simply called "collection iterators". They already exist in Smalltalk-80, which is called that way, because it was published in 1980. To verify this you can download the Smalltalk-80 "blue book" from here and do a text search on these function names: http://stephane.ducasse.free.fr/FreeBooks/BlueBook/Bluebook.pdf In those 10 years where I did Smalltalk development I met no one who called making use of those collection iterators functional programming. There are several books about Smalltalk who have some importance in CS, because Smalltalk was the first truly useable OO language afer Simula. I don't think the term functional programming is ever used there. Calling collection iterators functional programming opened Pandoras box of mixing up things. I fear it is too late to explain to people that functional progeramming is what is done in Haskel and Lisp and related languages and nothing else. Collection iterators carry some aspects of functional programming such as non-destructive programming as the result of applying a function on a call returns a new collection leaving the original collection unchanged, but that alone is not enogh for things to be called functional programming. It is merely simply making use of closures.
Re: Writing Postgresql extension in D
On Friday, 16 November 2018 at 02:18:11 UTC, Ranjan wrote: On Thursday, 15 November 2018 at 17:03:55 UTC, Andrea Fontana wrote: On Thursday, 15 November 2018 at 13:05:59 UTC, Ranjan wrote: This is my first time on the Dlang forum. I like the language but my usecase is a bit different. I want to write Postgresql extension in D. Currently extension can be written in C or C linked languages. Has anyone done this or can point me to some code. Thanks Did you read this: https://dlang.org/spec/interfaceToC.html ? Andrea Yes, but it's not useful, in Postgesql extension C code needs to call D, without GarbageCollection. I am able to do this in RustLang but I am not sure how to in D, as it has a GC. Looking for examples from the community. Thanks I'm not an exert with C nor D. So I might misuderstand the issue... You can write a skeleton plugin for postgres in C that at init time starts the D runtime. From then on your C skelton plugin can call some function in D.
Re: Inherit from class based on bool value
On Tuesday, 13 November 2018 at 07:10:26 UTC, Jamie wrote: I would like my class to inherit from one of two classes based on a boolean value known at compile time. Something like this: void main() { Top!(OPTION.FALSE) top = new Top!(OPTION.FALSE); } enum OPTION { FALSE = 0., TRUE = 1. } class One {} class Two {} class Top(OPTION option) : option ? One : Two {} Is this possible? I can't get it to work in the way I'm showing above. Cheers My piece of advice would be not to do this but to model all data explicitly. And I had some exposure to OOP with over 10 years developing in Smalltalk, which is a pure OO language. Actually, I don't know whether it is good that this can be done in D ...
Re: Don't expect class destructors to be called at all by the GC
On Thursday, 21 December 2017 at 18:45:27 UTC, Adam D. Ruppe wrote: On Thursday, 21 December 2017 at 18:20:19 UTC, H. S. Teoh wrote: When the scoped destruction of structs isn't an option, RefCounted!T seems to be a less evil alternative than an unreliable class dtor. :-/ Alas, RefCounted doesn't work well with inheritance... Though, what you could do is make the refcounted owners and borrow the actual reference later. Is there some summary of the things you have to be aware of when using the GC in D and not using the GC? I feel this would be very useful especially for people that are new to D or are not used to that kind of issues (because coming from a GCed language).
Re: Is there any threadsafe queue?
On Wednesday, 13 September 2017 at 07:51:19 UTC, John Burton wrote: Is there any threadsafe queue in the standard library? I've not been able to find anything but thought I'd check before making my own. I want to be able to assemble messages (Which are just streams of bytes) in one thread into a struct and push them to the queue, and then have a another thread be able to read and process the messages. Single producer and consumer. You can take some existing container and overwrite the add and remove methods to be synchronized, e.g. public void add(T t) { synchronized { super.add(t); } } However, this could cause some lock contention depending on your use case. This is why class Vector in Java is basically discontinued. In that class every method is synchronized which has led to bad timing behavior. For the new concurrent collections in Java since Java 5 some work has been done to replace synchronized with some CAS approach. For example, class ConcurrentLinkedQueue in Java does some tricks with CAS algorithms to get around this.
Re: Non-GC based List/Set/Map implementation?
Thanks for the replies. This looks good. I meanwhile found http://dsource.org/projects/dcollections But it seems to be GC-based just like Tango ... ;-(.
Non-GC based List/Set/Map implementation?
Hello, does anyone know of a List/Set/Map implementation that does not rely on the GC? The would be the last thing I need for D to be really happy with it ;-) Thanks, Bienlein
Re: Getting RefCounted to work with classes
On Tuesday, 26 August 2014 at 06:01:25 UTC, uri wrote: RefCounted does not work with classes. Classes are reference types already. Yep, that's the problem. I also got some suspicion, then surfed the Internet and found the information about it. Thanks for explaining the error message to me. Now it even seems obvious to me what it wants to say ;-). But you can define a var inside a struct that holds a class and this way I got it working with my class as well thanks to generics in D. Woohoo!
Getting RefCounted to work with classes
Hello, the code below compiles and runs fine. However, when I change Payload from struct to class I get compiler errors: Error 1 Error: template instance std.typecons.RefCounted!(Payload, cast(RefCountedAutoInitialize)1) does not match template declaration RefCounted(T, RefCountedAutoInitialize autoInit = RefCountedAutoInitialize.yes) if (!is(T == class)) C:\Users\Nutzer\Windows Ordner\Documents\Visual Studio 2013\Projects\RefCountedScratch\RefCountedScratch\main.d 26 I tried many things, but nothing did it. Any help appreciated :-). Thanks, Bienlein import std.stdio; import std.typecons; struct Payload { private int num = 0; this(int i) { num = i; writefln("Payload's constructor called"); } ~this() { writefln("Payload's destructor called"); } } int main(string[] argv) { alias RefCounted!(Payload, RefCountedAutoInitialize.yes) Data; int bar = 12; Data data = Data(bar); return 0; }
Re: How to get nogc to work with manual memory allocation
On Sunday, 24 August 2014 at 08:48:03 UTC, bearophile wrote: Perhaps there are ways, but note that @nogc is meant mostly for stack-allocation. Ah, I missed that. Thanks for telling me. I changed nogcDel now to null out the deallocated object: void nogcDel(T)(ref T obj) { import core.stdc.stdlib : free; // calls obj's destructor destroy(obj); // free memory occupied by object free(cast(void*)obj); obj = null; } And now I also get the dearly missed protection violation ;-).
How to get nogc to work with manual memory allocation
Hello, I was having a look at the new nogc annotation and therefore wrote some code that creates an instance on the heap bypassing the GC (code adapted from http://dpaste.dzfl.pl/2377217c7870). Problem is that calls to call the class' constructor, destructor and others can't be called anymore once nogc is used. So the question is how to get manual allocation and deallocation done with using nogc. Here is the experimental code: @nogc T nogcNew(T, Args...) (Args args) { import std.conv : emplace; import core.stdc.stdlib : malloc; // get class size of class object in bytes auto size = __traits(classInstanceSize, T); // allocate memory for the object auto memory = malloc(size)[0..size]; if(!memory) { import core.exception : onOutOfMemoryError; onOutOfMemoryError(); } // call T's constructor and emplace instance on // newly allocated memory return emplace!(T, Args)(memory, args); } @nogc void nogcDel(T)(T obj) { import core.stdc.stdlib : free; // calls obj's destructor destroy(obj); // free memory occupied by object free(cast(void*)obj); } @nogc void main() { TestClass test = nogcNew!TestClass(); test.x = 123; nogcDel(test); test.x = 456; // no protection violation ?! // remove @nogc to run this TestClass t = new TestClass(); t.x = 789; delete t; t.x = 678; // protection violation as expected } I have omitted the code for the TestClass class to save space. Problem is that the compiler outputs this: Error: @nogc function 'main.nogcNew!(TestClass, ).nogcNew' cannot call non-@nogc function 'core.exception.onOutOfMemoryError' Error: @nogc function 'main.nogcNew!(TestClass, ).nogcNew' cannot call non-@nogc function 'std.conv.emplace!(TestClass, ).emplace' Error: @nogc function 'main.nogcDel!(TestClass).nogcDel' cannot call non-@nogc function 'object.destroy!(TestClass).destroy' Is there a way to get around this? Then the test.x = 456; did not cause a protection violation although the instance was deallocated before calling nogcDel. Something with the deallocation in nogcDel seems not to work. Some hint appreciated on this. When calling delete t the protection violation happens on the next line as expected. Thanks a lot, Bienlein
Re: spawn and wait
There is also a Semaphore and Barrier class: http://dlang.org/phobos/core_sync_barrier.html http://dlang.org/phobos/core_sync_semaphore.html
Re: Passing around a list of differently typed functions
On Monday, 23 June 2014 at 01:16:49 UTC, Evan Davis wrote: As the subject says, I would like to pass around an array of functions. The trick is, that the functions have different type signatures. Is there a way to put the two functions int foo(int a, int b); bool bar(bool a, bool b); into one array, that I can pass around and cast as necessary? Thanks, Evan Have functions in the array without parpameters that call the function with the applicable parameters, e.g. int foo(int a, int b) or bool bar(bool a, bool b). That doesn't address the problem of the different return types (int and bool). So change the return type to void and return the value as an inout parameter.
Re: Some kind of RPC exists for D?
On Saturday, 21 June 2014 at 18:22:54 UTC, Paul wrote: I wrote some (JSONRPC and TXTRPC) and used them with vibe. I can send you via email Hi Paul, alternatively you could upload your code to GitHub or some similar place and place the link here. Then you also have a means to "proof" that the code is your work along with the date of publication. -- Bienlein
Re: Some kind of RPC exists for D?
What data load profile do you expect? Vibe is tuned to handle thousands simultaneous incoming light requests (milliseconds), while distributed computing works better with exclusive heavy requests, at least minutes of work worth, BOINC uses hours worth work items. Communication will be bi-directional as some thread sends computation along with the data to be computed to some remote thread and somewhen waits for the answer. The amount of data will be relatively small, that is only consisting of numbers to be processed creating some result, because Julia is geared towards scientific computing. In that way there is little data compared to enterprise computing. How often remote computations are initiated depends to a large extend on the user. So that is hard to say. But from the typical use case scenario it will be anything but few large messages to go across the wire. I might have to do a bit more research looking at Julia to get a better understanding. But it's all about performance, e.g. crunching as many numbers as quickly as possible. So I will have to stick to a high-performance solution anyway. For that reason it looks more like Swift or ZeroMQ. Depends a lot which one can do marshalling and unmarshalling of the function invocation and the data more transparently than the other.
Re: Some kind of RPC exists for D?
What I want to do is some little framework that allows Julia-style (julialang.org) distributed computation: send some function invocation to some other machine along with the numbers to be crunched and get the result back. vibe.web.rest server/client combo is effectively RPC over HTTP/json This looks good. For what am I'm thinking of doing performance is important. In that way rest makes me think a bit or is this only a prejudice from the Java world? I wrote an implementation of Thrift for D a while back This also looks interesting. Because my C/C++/D skills are limited being a Smalltalk/Java developer (only played with C++ when studying) I have to stick to what is easier to use. It would be a fun & leisure & learning project anyway... Regards, Bienlein
Some kind of RPC exists for D?
Hello, I'm looking for a way to do some kind of RPC in D. Some way of being able to say aFoo.bar(int i, ...) with receiver object and method being marshalled at the sender's site and being unmarshalled and invoked at the receiver's site. Any hints appreciated. Thanks, Bienlein
Re: On Concurrency
One key difference is that coroutines won't make your programs run faster. It is a modelling mechanism that can simplify your programs where you otherwise would have to implement a state machine. This is also my impression when I look at this code (see http://www.99-bottles-of-beer.net/language-d-2547.html) that implements 99 bottles of beer in D with fibers. What seems to be happening is some alternating handover of the CPU. But when I run the code all 4 cores of my machine are under load and it looks like the runtime were able to make things run in parallel somehow. Now I'm really confused ...