Re: Casting JSONValues arrays to native arrays ... ??? ...
On Thursday, 23 September 2021 at 20:32:36 UTC, james.p.leblanc wrote: On Thursday, 23 September 2021 at 19:18:11 UTC, james.p.leblanc wrote: On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote: On 9/23/21 2:20 PM, james.p.leblanc wrote: ``` Produces: typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399]} typeid(z5): question.main.MapResult!(__lambda2, JSONValue[]).MapResult, z5: [true, false, true] z5: [true, false, true] Sigh ... my suggested "minor edit" above produces a "map result" instead of the desired native array ... here is the **fix** by appending ".array" at the end (which is the suffix Steve originally offered). The following gives the desired **bool[]** result. ```d import std.array; auto z5 = jj["ba"].array.map!(v => v.get!bool).array; writeln("typeid(z5): ", typeid(z5), ", z5: ", z5); writeln("z5: ", z5); ``` Regards, James
Re: associative array with element type struct ?
Of course! And it's very common. <= lol Thanks Ali. Much appreciated!
Re: associative array with element type struct ?
On 9/23/21 3:06 PM, Paul wrote: > Can I have an associative array with the element type being a struct? Of course! And it's very common. :) > ...this > 26 // simple dual tone key struct > 27 struct keytones { ushort rowFreq; ushort colFreq; } > 28 > 29 // keypad associative array > 30 keytones[string] keypad; > 31 keypad["1"].rowFreq = 697; keypad["1"].colFreq = 1209; > > gets this response from DMD compiler > > aaa.d(30): Error: undefined identifier `keypad` I think you got that error from a different experiment because 'keypad' is clearly valid in that code. However, you are attempting to access members of a non-existing element: There is no element corresponding to "1" in the AA (yet). There are multiple ways of populating an AA but I usually and simply assign an rvalue in such cases: void main() { // simple dual tone key struct struct keytones { ushort rowFreq; ushort colFreq; } // keypad associative array keytones[string] keypad; keypad["1"] = keytones(697, 1209); } Ali
associative array with element type struct ?
Can I have an associative array with the element type being a struct? ..this 26 // simple dual tone key struct 27 struct keytones { ushort rowFreq; ushort colFreq; } 28 29 // keypad associative array 30 keytones[string] keypad; 31 keypad["1"].rowFreq = 697; keypad["1"].colFreq = 1209; ...gets this response from DMD compiler aaa.d(30): Error: undefined identifier `keypad` aaa.d(30): Error: declaration `aaa.main.keytones` is already defined aaa.d(27):`struct` `keytones` is defined here thanks for any assistance!
Re: Casting JSONValues arrays to native arrays ... ??? ...
On Thursday, 23 September 2021 at 19:18:11 UTC, james.p.leblanc wrote: On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote: On 9/23/21 2:20 PM, james.p.leblanc wrote: Dear D-ers, Here comes a minor update (small rearrangement in mapping/array ordering) for anyone who may be interested. With this small edit to the suggested code, it works just fine! Here: ```d auto z5 = jj["ba"].array.map!(v => v.get!bool); writeln("typeid(z5): ", typeid(z5), ", z5: ", z5); writeln("z5: ", z5); ``` Produces: typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399]} typeid(z5): question.main.MapResult!(__lambda2, JSONValue[]).MapResult, z5: [true, false, true] z5: [true, false, true] Cheers, James
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 19:32:12 UTC, eugene wrote: C (more elaborated) variant: http://zed.karelia.ru/mmedia/bin/edsm-g2-rev-h.tar.gz Sound, GUI? Easy, see http://zed.karelia.ru/mmedia/bin/xjiss4.tar.gz It's computer keyboard 'piano', based on the same engine. As I've already mentioned, I was inspired several years ago by very nice book, 'Modeling Software with Finite State Machines: A Practical Approach' by Wagner F. et al. I applied some ideas from the book to Posix/Linux API and developed EDSM - 'Event driven state machines' - and since I do not need no libev, libevent and alike. State machines per se are very powerful methodology to model program behavior. Also notice, that machines communicates with each other by messages - remember Alan Key main OOP principle? It's message exchange, not class hierarchy (inheritance blah-blah-blah). As to client-server echo pair in D, it is my 3rd (and most successful!!!) attempt to re-implement EDSM in some 'modern' language. Initially my criteria for choosing a lang were: - compiles to native code, goodby Java - no garbage collector, goodby almost all :) - no classes, only interfaces. - maybe, something else, do not remember The only language, that fit to my initial desires, was Rust. But borrow checker (and especially <'a>, expicite lifetimes) appeared to be the real hell for me. One can use raw pointers instead of references, but then your code is from head to toe is in unsafe {} blocks. Next one was (just accidentally) was c#. Here, I was not be able to make signals work properly and I dropped it. After reading some texts a la 'alternatives to c++', I decided to try D, despite it's 'unpopularity'.
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 18:53:25 UTC, Steven Schveighoffer wrote: On 9/23/21 1:44 PM, eugene wrote: Just a note: there is no 'signal handler' in the program. SIGINT/SIGTERM are **blocked**, notifications (POLLIN) are received via epoll_wait(). Oh interesting! I didn't read the code closely enough. "everything in Unix is a file" (c) All event sources (sockets, timers, signal, file system events) can be 'routed' through i/o multiplexing facilities, like select/poll(posix)/epoll(linux)/queue(freebsd) etc. Probably, a destructor for Signal class should be added, in which Yes, I would recommend that. Always good for a destructor to clean up any non-GC resources that haven't already been cleaned up. That's actually what class destructors are for. No, destructors are not necessary, since after SIGINT/SIRTERM program is about to terminate and all resources will be released anyway. In C I do same way - do not close fd, which live from start to end, do not free() pointers and so on, no need. So it gets written to the file descriptor instead? When signal happens (or timer expires, or file is deleted) process get EPOLLIN on corresponding file descriptor via epoll_wait() and then process has to read some info from these file descriptors. And nobody is there reading it, so it's just closed along with the process? Yes, as any other file descriptor. I've not done signals this way, it seems pretty clever and less prone to asynchronous issues. It's just great, thanks to Linux kernel developers. Look in to engine dir in the source. C (more elaborated) variant: http://zed.karelia.ru/mmedia/bin/edsm-g2-rev-h.tar.gz
Re: Casting JSONValues arrays to native arrays ... ??? ...
On Thursday, 23 September 2021 at 19:04:47 UTC, Steven Schveighoffer wrote: On 9/23/21 2:20 PM, james.p.leblanc wrote: Dear D-ers, In attempting to cast JSONValues that hold arrays to "native" How you really do this: ```d import std.algorithm : map; auto z5 = jj["ba"] // get the JSONValue that is at the key "ba" .map!(v => v.get!bool) // map each value into a boolean .array // create an array out of the results; assert z5 == [true, false, true]; ``` (warning, untested) -Steve Steve, Your thorough explanations have helped me understand quite much. Thank you for your energy and patience in these forum contributions. In fact, I had begun to think that "map", may be what was needed here and I had made a few naive attempts ... but this did not go so well. With your suggested solution, I believe I can make headway on this. Thanks Again, James
Re: Casting JSONValues arrays to native arrays ... ??? ...
On 9/23/21 2:20 PM, james.p.leblanc wrote: Dear D-ers, In attempting to cast JSONValues that hold arrays to "native" array types, I have hit some issues. Example code: ```d import std.stdio; import std.json; void main(){ JSONValue jj; jj["d"] = [ 1.234 ]; // a "dummy" double value jj["ba"] = [ true, false, true]; // "ba" boolean array Note that this creates a JSONValue array which *copies* the values of the boolean array, converting them to JSONValue (which is what a JSONValue array stores). writeln("typeid(jj): ", typeid(jj), ", jj: ", jj ); // various things that I thought might work, but do NOT auto z1 = cast(bool) jj["ba"]; // attempt #1 A JSONValue cannot be cast to a boolean (it does not provide the appropriate opCast) auto z2 = cast(bool[]) jj["ba"]; // attempt #2 auto z3 = cast(bool) jj["ba"].array; // attempt #3 These try to cast something that is not an array to an array or vice versa. These are not supported unless the type itself has an `opCast` overload. auto z4 = cast(bool[]) jj["ba"].array; // attempt #4 Casting one array type to another is like pointing at the array that represents the original type *as if* it were of the new type. No translation is made, you are pointing at the same memory! The length is adjusted based on the size of the original array element and the size of the new one. For instance: ```d int[] arr = [1]; auto a2 = cast(ubyte[])arr; assert(a2.length == 4); assert(a2 == cast(ubyte[])([1, 0, 0, 0])); // assuming little endian ``` However, if I comment out the offending attempts (1, 2, and 3), then it compiles, and can run ... but produces a result which I very much do NOT understand: typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399]} typeid(z4): bool[], z4: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false] This is an array of JSONValue, with each byte interpreted as if it were a bool. H... is there a standard way to push these JSONValues into nice native array types? (The real code is eventually going to be using traits and mixins ... but I do not think this should pose additional problems). How you really do this: ```d import std.algorithm : map; auto z5 = jj["ba"] // get the JSONValue that is at the key "ba" .map!(v => v.get!bool) // map each value into a boolean .array // create an array out of the results; assert z5 == [true, false, true]; ``` (warning, untested) -Steve
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 1:44 PM, eugene wrote: On Thursday, 23 September 2021 at 17:20:18 UTC, Steven Schveighoffer wrote: So imagine the sequence: With ease! 1. ctrl-c, signal handler triggers, shutting down the loop Just a note: there is no 'signal handler' in the program. SIGINT/SIGTERM are **blocked**, notifications (POLLIN) are received via epoll_wait(). Oh interesting! I didn't read the code closely enough. 2. main exits 3. GC finalizes all objects, including the Stopper and it's members Probably, a destructor for Signal class should be added, in which - close fd, obtained from signalfd() - unblock the signal (thus default signal handler is back again) Yes, I would recommend that. Always good for a destructor to clean up any non-GC resources that haven't already been cleaned up. That's actually what class destructors are for. 4. ctrl-c happens again, but you didn't unregister the signal handler, so it's run again, referencing the now-deleted object. At this point we have default signal handler 5. segfault It's theoretically a very very small window. But even without destructor, no segfault will happen, because **there is no signal handler** So it gets written to the file descriptor instead? And nobody is there reading it, so it's just closed along with the process? I've not done signals this way, it seems pretty clever and less prone to asynchronous issues. -Steve
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 18:43:36 UTC, Steven Schveighoffer wrote: With dmd -O -inline, there is a chance it will be collected. Inlining is key here. never mind, GC.addRoot() looks more trustworthy, anyway :)
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 2:18 PM, eugene wrote: On Thursday, 23 September 2021 at 17:16:23 UTC, Steven Schveighoffer wrote: On 9/23/21 12:58 PM, eugene wrote: On Thursday, 23 September 2021 at 15:56:16 UTC, Steven Schveighoffer wrote: See more details: https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks " This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available. " **Code this method at the end...** :) it is the same as proposed by jfondren simple writeln(stopper.sg0.number) in the end of main, right? Same effect, but writeln actually executes code to write data to the console, whereas KeepAlive doesn't do anything. ```d void keepAlive(Object o) { } void main(string[] args) { import core.memory : GC; auto Main = new Main(); Main.run(); auto stopper = new Stopper(); stopper.run(); writeln(" === Hello, world! === "); auto md = new MessageDispatcher(); md.loop(); keepAlive(Main); keepAlive(stopper); writeln(" === Goodbye, world! === "); } ``` works ok with dmd, stopper is not collected. With dmd -O -inline, there is a chance it will be collected. Inlining is key here. -Steve
Casting JSONValues arrays to native arrays ... ??? ...
Dear D-ers, In attempting to cast JSONValues that hold arrays to "native" array types, I have hit some issues. Example code: ```d import std.stdio; import std.json; void main(){ JSONValue jj; jj["d"] = [ 1.234 ]; // a "dummy" double value jj["ba"] = [ true, false, true]; // "ba" boolean array writeln("typeid(jj): ", typeid(jj), ", jj: ", jj ); // various things that I thought might work, but do NOT auto z1 = cast(bool) jj["ba"]; // attempt #1 auto z2 = cast(bool[]) jj["ba"]; // attempt #2 auto z3 = cast(bool) jj["ba"].array; // attempt #3 auto z4 = cast(bool[]) jj["ba"].array; // attempt #4 writeln("typeid(z4): ", typeid(z4), ", z4: ", z4 ); } ``` Attempts 1,2, and 3 yield compilation errors (which I somewhat understand): question.d(12): Error: cannot cast expression `jj.opIndex("ba")` of type `JSONValue` to `bool` question.d(13): Error: cannot cast expression `jj.opIndex("ba")` of type `JSONValue` to `bool[]` question.d(14): Error: cannot cast expression `jj.opIndex("ba").array()` of type `JSONValue[]` to `bool` However, if I comment out the offending attempts (1, 2, and 3), then it compiles, and can run ... but produces a result which I very much do NOT understand: typeid(jj): std.json.JSONValue, jj: {"ba":[true,false,true],"d":[1.23399]} typeid(z4): bool[], z4: [false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false] H... is there a standard way to push these JSONValues into nice native array types? (The real code is eventually going to be using traits and mixins ... but I do not think this should pose additional problems). All help and illumination thankfully received. Best Regards, James
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 17:16:23 UTC, Steven Schveighoffer wrote: On 9/23/21 12:58 PM, eugene wrote: On Thursday, 23 September 2021 at 15:56:16 UTC, Steven Schveighoffer wrote: See more details: https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks " This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available. " **Code this method at the end...** :) it is the same as proposed by jfondren simple writeln(stopper.sg0.number) in the end of main, right? Same effect, but writeln actually executes code to write data to the console, whereas KeepAlive doesn't do anything. ```d void keepAlive(Object o) { } void main(string[] args) { import core.memory : GC; auto Main = new Main(); Main.run(); auto stopper = new Stopper(); stopper.run(); writeln(" === Hello, world! === "); auto md = new MessageDispatcher(); md.loop(); keepAlive(Main); keepAlive(stopper); writeln(" === Goodbye, world! === "); } ``` works ok with dmd, stopper is not collected.
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 17:53:00 UTC, eugene wrote: On Thursday, 23 September 2021 at 17:49:43 UTC, eugene wrote: On Thursday, 23 September 2021 at 17:20:18 UTC, Steven Schveighoffer wrote: 1. ctrl-c, signal handler triggers, shutting down the loop 2. main exits 3. GC finalizes all objects, including the Stopper and it's members but both SIGINT and SIGTERM are still **blocked**, they just will not reach the process. oops.. no oops, that's all right. - when creating Signal instance, corresponding signal becames blocked ```d final class Signal : EventSource { enum int sigInt = SIGINT; enum int sigTerm = SIGTERM; ulong number; this(int signo) { super('S'); sigset_t sset; sigset_t old_sset; /* block the signal */ sigemptyset(); sigaddset(, signo); sigprocmask(SIG_BLOCK, , _sset); id = signalfd(-1, , SFD_CLOEXEC); ``` - upon receiving SIGINT stopperIdleS0() is called. now stop variable of EventQueue is false - next call to wait() method just return. (remember, signals are still blocked)
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 17:49:43 UTC, eugene wrote: On Thursday, 23 September 2021 at 17:20:18 UTC, Steven Schveighoffer wrote: 1. ctrl-c, signal handler triggers, shutting down the loop 2. main exits 3. GC finalizes all objects, including the Stopper and it's members but both SIGINT and SIGTERM are still **blocked**, they just will not reach the process. oops.. closing epoll fd should be moved from EventQueue dtor to stop() method, then everything will be Ok.
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 17:20:18 UTC, Steven Schveighoffer wrote: 1. ctrl-c, signal handler triggers, shutting down the loop 2. main exits 3. GC finalizes all objects, including the Stopper and it's members but both SIGINT and SIGTERM are still **blocked**, they just will not reach the process.
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 17:20:18 UTC, Steven Schveighoffer wrote: So imagine the sequence: With ease! 1. ctrl-c, signal handler triggers, shutting down the loop Just a note: there is no 'signal handler' in the program. SIGINT/SIGTERM are **blocked**, notifications (POLLIN) are received via epoll_wait(). 2. main exits 3. GC finalizes all objects, including the Stopper and it's members Probably, a destructor for Signal class should be added, in which - close fd, obtained from signalfd() - unblock the signal (thus default signal handler is back again) 4. ctrl-c happens again, but you didn't unregister the signal handler, so it's run again, referencing the now-deleted object. At this point we have default signal handler 5. segfault It's theoretically a very very small window. But even without destructor, no segfault will happen, because **there is no signal handler**
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 12:53 PM, eugene wrote: On Thursday, 23 September 2021 at 15:53:37 UTC, Steven Schveighoffer wrote: Technically, they should live past the end of main, because it's still possible to receive signals then. No, as soon as an application get SIGTERM/SIGINT, event queue is stopped and we do not need no more notifications from OS (POLLIN/POLLOUT I mean). Stopping event queue in this case is just closing file descriptor obtained from epoll_create(). After this getting POLLIN from any fd (including signal fd) is just impossible. That's not what is triggering the segfault though. The segfault is triggered by the signal handler referencing the destroyed object. So imagine the sequence: 1. ctrl-c, signal handler triggers, shutting down the loop 2. main exits 3. GC finalizes all objects, including the Stopper and it's members 4. ctrl-c happens again, but you didn't unregister the signal handler, so it's run again, referencing the now-deleted object. 5. segfault It's theoretically a very very small window. -Steve
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 12:58 PM, eugene wrote: On Thursday, 23 September 2021 at 15:56:16 UTC, Steven Schveighoffer wrote: See more details: https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks " This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available. " **Code this method at the end...** :) it is the same as proposed by jfondren simple writeln(stopper.sg0.number) in the end of main, right? Same effect, but writeln actually executes code to write data to the console, whereas KeepAlive doesn't do anything. Essentially, you get the side effect of keeping the object as live, without paying the penalty of inserting frivolous code. All my efforts to achieve the same via a library were thwarted by at least LDC (whose optimizer is very good). The only possible solution I can think of is to generate an opaque function that LDC cannot see into, in order to force it to avoid inlining, and have that function do nothing. However, there's always Link-Time-Optmization... -Steve
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 15:56:16 UTC, Steven Schveighoffer wrote: See more details: https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks " This method references the obj parameter, making that object ineligible for garbage collection from the start of the routine to the point, in execution order, where this method is called. Code this method at the end, not the beginning, of the range of instructions where obj must be available. " **Code this method at the end...** :) it is the same as proposed by jfondren simple writeln(stopper.sg0.number) in the end of main, right?
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 15:53:37 UTC, Steven Schveighoffer wrote: Technically, they should live past the end of main, because it's still possible to receive signals then. No, as soon as an application get SIGTERM/SIGINT, event queue is stopped and we do not need no more notifications from OS (POLLIN/POLLOUT I mean). Stopping event queue in this case is just closing file descriptor obtained from epoll_create(). After this getting POLLIN from any fd (including signal fd) is just impossible.
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 9:18 AM, eugene wrote: On Thursday, 23 September 2021 at 12:53:14 UTC, Steven Schveighoffer wrote: We need to add a better way to do that (similar to C# KeepAlive). Do you mean some function attribute?.. C# KeepAlive (and Go KeepAlive) are a mechanism to do exactly what you suggested -- use the object later. However, they are recognized by the compiler as an intrinsic which generates no code or side effects, but is not subject to elimination by the optimizer. See more details: https://docs.microsoft.com/en-us/dotnet/api/system.gc.keepalive?view=net-5.0#remarks -Steve
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 10:55 AM, eugene wrote: On Thursday, 23 September 2021 at 14:31:34 UTC, jfondren wrote: Nice. I thought of GC.addRoot several times but I was distracted by the general solution of using object lifetimes with it, so that a struct's destructor would call GC.removeRoot. For your case just pinning these and forgetting about them is the easiest way to do it. Yes, these two must live until the end of main(). Moreover, in real (C) programs I (usually) do not create state machines on the fly, instead I keep them in pools, like RX/TX machines pools in echo-server and in echo-client. Technically, they should live past the end of main, because it's still possible to receive signals then. But the chances of someone hitting ctrl-c in that window are quite small. -Steve
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 14:31:34 UTC, jfondren wrote: Nice. I thought of GC.addRoot several times but I was distracted by the general solution of using object lifetimes with it, so that a struct's destructor would call GC.removeRoot. For your case just pinning these and forgetting about them is the easiest way to do it. Yes, these two must live until the end of main(). Moreover, in real (C) programs I (usually) do not create state machines on the fly, instead I keep them in pools, like RX/TX machines pools in echo-server and in echo-client.
Re: Program crash: GC destroys an object unexpectedly
On Monday, 13 September 2021 at 17:18:30 UTC, eugene wrote: And the most strange thing is this - if using gdc with -Os flag, the program behaves exactly as when compiled with fresh dmd - destructors for sg0 and sg1 are called soon after program start. Now I guess, gdc optimization by size imply DSE.
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 14:23:40 UTC, eugene wrote: On Thursday, 23 September 2021 at 14:00:30 UTC, eugene wrote: For the moment I am personally quite happy ```d void main(string[] args) { import core.memory : GC; auto Main = new Main(); GC.addRoot(cast(void*)Main); Main.run(); auto stopper = new Stopper(); GC.addRoot(cast(void*)stopper); stopper.run(); ``` Fine, works! Nice. I thought of GC.addRoot several times but I was distracted by the general solution of using object lifetimes with it, so that a struct's destructor would call GC.removeRoot. For your case just pinning these and forgetting about them is the easiest way to do it.
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 14:00:30 UTC, eugene wrote: For the moment I am personally quite happy ```d void main(string[] args) { import core.memory : GC; auto Main = new Main(); GC.addRoot(cast(void*)Main); Main.run(); auto stopper = new Stopper(); GC.addRoot(cast(void*)stopper); stopper.run(); ``` Fine, works!
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 13:30:42 UTC, eugene wrote: So, in C it is MY (potentially wrong) code. In D, it is NOT MY code, it is GC. Actually in both cases it is MY+the compiler's code. A very similar example from C-land (without my digging up the exact details) is something like ```c for (int i = 0; i >= 0; i++) { // exit loop on signed integer overflow } ``` where gcc 2.95 would do what "MY code" said, but later gcc versions would 'optimize' into an infinite loop (followed by dead code that can now be removed): ```c for (;;) { // never exit loop } ``` Because in math, positive numbers never +1 into negative numbers. And in C this is undefined behavior which is (modern understanding:) complete license for the compiler to do anything at all. And on the specific architecture we are specifically compiling for there is specific behavior--but who cares about that, this is optimization! And if you complained about it, well you were a sloppy coder actually, for wanting the target architecture's actual behavior with your actual code as you actually wrote it. (If you feel like defending C's honor here, please, I've heard it already. Everybody thinks very highly of the nasal demons joke.) There are other cases where very security-minded software had defensive code that an optimizer decided would never be needed, that then exposed a software vulnerability, or there are 'unnecessary' writes that are intended to remove a password from memory: https://duckduckgo.com/?q=dead+code+elimination+security+vulnerability
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 12:53:14 UTC, Steven Schveighoffer wrote: With the caveat, of course, that the recommendation to "leave a pointer on the stack" is not as easy to follow as one might think with the optimizer fighting against that. We need to add a better way to do that (similar to C# KeepAlive). I made an [attempt](https://code.dlang.org/packages/keepalive), but I think it's not guaranteed to work, I've already found ways to prove it fails. For the moment I am personally quite happy with any reasonable workaround (use an object in the end of the main function, put the reference to an object into some AA, whatever), because now I firmly understand, that the source of strange GC behavior is DSE optimization (in this case).
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 13:05:07 UTC, Steven Schveighoffer wrote: UB in C leaves traps for the programmer, similar to this trap you have found in the GC. Where code doesn't do what you are expecting it to do. There is a difference, though. As I've already said, GC is a sort of 'environment', the code of GC exits by it's own. In C, no such code is 'inserted' by compiler into code written by a programmer. So, in C it is MY (potentially wrong) code. In D, it is NOT MY code, it is GC. From this point of view debugging *may* be harder (especially by beginners, like me)
Re: Program crash: GC destroys an object unexpectedly
On Thursday, 23 September 2021 at 12:53:14 UTC, Steven Schveighoffer wrote: Show me these rules! They are here: https://dlang.org/spec/interfaceToC.html#storage_allocation With the caveat, of course, that the recommendation to "leave a pointer on the stack" is not as easy to follow as one might think with the optimizer fighting against that. Yes, as you explained me, the root of the problem in my examples were dead store elimination. We need to add a better way to do that (similar to C# KeepAlive). Do you mean some function attribute?..
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 8:10 AM, eugene wrote: On Wednesday, 22 September 2021 at 18:38:34 UTC, Steven Schveighoffer wrote: I find it interesting how you blame yourself for C's idiosyncrasies Me? Blaming *myself* for C 'idiosyncrasies'? :) Where? "When my C program crashes, I'm 100% sure I made something stupid" One might argue that C's approach to memory management is a contributor to people writing code that fails. I would say C has far more pitfalls than D. No doubt - and I've never said C is "better" than D. I was going to try betterC subset (say, try to implement dynamic arrays), but did not have much free time yet. Your assertion that programming in GC languages may be harder than manual memory languages is what I was addressing. My point is that C has a lot more memory management pitfalls than D, not addressing any "better than" arguments. Check out the undefined behaviors for C. Nothing interesting... Most of UB in C are just programmer's sloppiness. C requires a programmer to be careful/punctual, much more careful, than ... a python, for ex. UB in C leaves traps for the programmer, similar to this trap you have found in the GC. Where code doesn't do what you are expecting it to do. -Steve
Re: Program crash: GC destroys an object unexpectedly
On 9/23/21 3:27 AM, eugene wrote: On Wednesday, 22 September 2021 at 18:38:34 UTC, Steven Schveighoffer wrote: Your experience is not typical though (clearly, as many of us long-time D users had no idea why it was happening). Oh, yeah - I have special trait of bumping against various low probability things :) But for sure if this turns you off, I can understand how it can be too frustrating to learn the new rules. Show me these rules! They are here: https://dlang.org/spec/interfaceToC.html#storage_allocation With the caveat, of course, that the recommendation to "leave a pointer on the stack" is not as easy to follow as one might think with the optimizer fighting against that. We need to add a better way to do that (similar to C# KeepAlive). I made an [attempt](https://code.dlang.org/packages/keepalive), but I think it's not guaranteed to work, I've already found ways to prove it fails. -Steve
Re: Program crash: GC destroys an object unexpectedly
On Wednesday, 22 September 2021 at 18:38:34 UTC, Steven Schveighoffer wrote: I find it interesting how you blame yourself for C's idiosyncrasies Me? Blaming *myself* for C 'idiosyncrasies'? :) Where? but not for D's ;) I've been learning D for about 3 months only. I would say C has far more pitfalls than D. No doubt - and I've never said C is "better" than D. I was going to try betterC subset (say, try to implement dynamic arrays), but did not have much free time yet. Check out the undefined behaviors for C. Nothing interesting... Most of UB in C are just programmer's sloppiness. C requires a programmer to be careful/punctual, much more careful, than ... a python, for ex.
Re: Scope with owner types
Yes, the `return` attribute is what should do it. You also need to compile the code with -dip1000 option.
Re: Program crash: GC destroys an object unexpectedly
On Wednesday, 22 September 2021 at 18:38:34 UTC, Steven Schveighoffer wrote: But realize that C has it's share of "shocks" as well Any language is just an instrument, most of the 'shocks' come not from languages themselves, but from the 'enviromment', so to say. An example that came to mind... Did you know that sending data via write()/send() to a socket, that is in CLOSE_WAIT state, results in sending data to nowhere and write() indicates no error? By the way, GC is a sort of 'environment' (not the language itself), it acts behind the scenes (unless you are using it directly) you are just more used to them (or maybe you have been lucky so far?) Once I've been very 'lucky' with unaligned pointer dereference on ARM...
Re: Program crash: GC destroys an object unexpectedly
On Wednesday, 22 September 2021 at 18:38:34 UTC, Steven Schveighoffer wrote: In terms of any kind of memory management, whether it be ARC, manual, GC, or anything else, there will always be pitfalls. It's just that you have to get used to the pitfalls and how to avoid them. 100% agree. I could see a person used to GC complaining that C requires you to free every pointer *exactly once*. C (at compiler level) does not require this. You can do it free()ly. ,) With a subsequent... yes, 'shock' after you see 'double free or corruption' message. Tell that imaginary person this: if (p) {free(p); p = NULL}, that's all.
Re: Program crash: GC destroys an object unexpectedly
On Wednesday, 22 September 2021 at 18:38:34 UTC, Steven Schveighoffer wrote: Your experience is not typical though (clearly, as many of us long-time D users had no idea why it was happening). Oh, yeah - I have special trait of bumping against various low probability things :) But for sure if this turns you off, I can understand how it can be too frustrating to learn the new rules. Show me these rules! Always use an object at the end of a function? Make a second reference to an object somewhere on the heap? The 'problem' here is that there is no clear rule. Any reasonable 'hack' will do.