1. Phix+EuGTK
- Posted by euphoric (admin) Sep 11, 2019
- 3303 views
Has anybody ever tried, or is it possible, to use EuGTK for Phix?
Oops. I did just find this.
2. Re: Phix+EuGTK
- Posted by _tom (admin) Sep 11, 2019
- 3322 views
EuGtk for Phix uses the Euphoria std/lib a lot. It also seems to use unique Euphoria syntax. Looks like translating euGtk to Phix will take a lot of work.
Console programs will work on Euphoria and Phix.
IUP gui will work on Euphoria and Phix. The wrappers are different but IUP remains the same. (My frustration with IUP is that 32bit Linux is no longer supported.)
JAPI I think will work on Euphoria and Phix. (You need to install Java for this.) The JAPI interface is a bit old looking.
Tcl/tk can be used on Euphoria and Phix. (The tk gui is satisfactory, and it works on 32 bit Linux)
_tom
3. Re: Phix+EuGTK
- Posted by dr_can Sep 12, 2019
- 3286 views
The JAPI2 interface uses Java Swing, which, although now superceded by JavaFX, looks, in my view, cleaner than IUP, wxwidgets, win32lib, tcl/tk, etc.
I haven't tried it on Phix though!
4. Re: Phix+EuGTK
- Posted by irv Sep 12, 2019
- 3245 views
- Last edited Sep 15, 2019
No. I have tried to convert EuGtk to work with Phix, and got a basic window and button to work, but as noted above, it would be a major job. Different libs, etc.
If I recall correctly, there were some things not yet implemented in phix which were "show stoppers". Those would require writing significant amounts of code to implement and test.
5. Re: Phix+EuGTK
- Posted by petelomax Dec 05, 2020
- 2666 views
I'm now looking at this thread wondering why I never asked what...
6. Re: Phix+EuGTK
- Posted by irv Dec 05, 2020
- 2653 views
I'm now looking at this thread wondering why I never asked what...
It's been a while, I can't remember what the problem was. However, I would like to see EuGTK work with phix, so I'll give it another try.
7. Re: Phix+EuGTK
- Posted by irv Dec 13, 2020
- 2515 views
So far, not so good...
I get a seg fault, core dumped when I try to call a function contained in a library as a method.
Note that calling "gtk_window_new" to create a public "handle" to the new window works fine. Calling "gtk_widget_show" (or any other shared library routine) from within a class procedure fails.
Maybe there's something I am doing completely wrong.
constant LIB = open_dll("libgtk-3.so.0"), I = C_INT, -- INTEGER P = C_PTR, -- POINTER integer init = define_c_func(LIB,"gtk_init_check",{P,P},P) if init < 1 or c_func(init,{0,0})=0 then crash("Failed to initialize GTK library!") end if class Window public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) procedure show() puts(1,"This handle: ") ? this.handle c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{this.handle}) -- seg fault -- define_c_proc is the part that fails when inside a procedure end procedure end class Window win = new() puts(1,"Win handle: ") ? win.handle win.show() main() procedure main() c_proc(define_c_proc(LIB,"gtk_main",{})) end procedure
8. Re: Phix+EuGTK
- Posted by petelomax Dec 13, 2020
- 2509 views
So far, not so good...
I get a seg fault, core dumped when I try to call a function contained in a library as a method.
Works for me, on windows and linux (only tested 64 bit on the latter, 32 and 64 on windows).
I believe there's a thing with the linux kernel... see https://openeuphoria.org/forum/m/134873.wc
On the machine I tested it on, uname -r yields 3.5.0-54-generic, if that helps any.
9. Re: Phix+EuGTK
- Posted by irv Dec 14, 2020
- 2490 views
4.15.0-54-generic
I don't know if that is the same problem, I can successfully load routines from .so files. define_c_proc works when inside a class function, but not when inside a class procedure. Code below works:
class Window public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) function show() puts(1,"This handle: ") ? this.handle c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{this.handle}) return 1 end function end class
10. Re: Phix+EuGTK
- Posted by petelomax Dec 14, 2020
- 2470 views
- Last edited Dec 15, 2020
4.15.0-54-generic
define_c_proc works when inside a class function, but not when inside a class procedure.
Right. I've now got the same crash (proc not func) on Ubuntu 20.04.1 LTS with a uname -r of 5.4.0-58-generic
building edb...
Some small progress, I think it's this (sort of) thing, from builtins\VM\pcfunc.e, line 308:
[PE64] mov rcx,rsp -- put 2 copies of rsp onto the stack... push rsp push rcx or rsp,8 -- [rsp] is now 1st or 2nd copy: -- if on entry rsp was xxx8: both copies remain on the stack -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) -- obviously rsp is now xxx8, whatever alignment we started with mov rax,[lib] -- mov r15,h4 sub rsp,8*5 -- minimum 4 param shadow space, and align mov rdx,[name] call :%pLoadMint -- (rax:=(int64)rax; rdx preserved) shl rdx,2 -- lpProcName mov rcx,rax -- hModule call "kernel32.dll","GetProcAddress" lea rdi,[addr] -- add rsp,8*5 -- pop rsp mov rsp,[rsp+8*5] -- equivalent to the add/pop call :%pStoreMint [ELF64] mov rax,[lib] mov rsi,[name] call :%pLoadMint -- (rax:=(int64)rax) shl rsi,2 -- symbol mov rdi,rax -- handle call "libdl.so.2", "dlsym" lea rdi,[addr] call :%pStoreMint []
The windows code is aligning the stack, but the Linux code isn't...
There's a movaps instruction which is expecting the stack to be aligned,
and I guess some kernels were built with whatever-compiler-flag it is
that causes it to emit movups instructions instead. And I originally ported
Phix to Linux on such a machine.
Anyway, it's fixed! The above needs to become
[64] mov rcx,rsp -- put 2 copies of rsp onto the stack... push rsp push rcx or rsp,8 -- [rsp] is now 1st or 2nd copy: -- if on entry rsp was xxx8: both copies remain on the stack -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) -- obviously rsp is now xxx8, whatever alignment we started with mov rax,[lib] -- mov r15,h4 sub rsp,8*5 -- minimum 4 param shadow space, and align [PE64] mov rdx,[name] call :%pLoadMint -- (rax:=(int64)rax; rdx preserved) shl rdx,2 -- lpProcName mov rcx,rax -- hModule call "kernel32.dll","GetProcAddress" [ELF64] mov rsi,[name] call :%pLoadMint -- (rax:=(int64)rax) shl rsi,2 -- symbol mov rdi,rax -- handle call "libdl.so.2", "dlsym" [64] lea rdi,[addr] -- add rsp,8*5 -- pop rsp mov rsp,[rsp+8*5] -- equivalent to the add/pop call :%pStoreMint []
and further down, also in builtins\VM\pcfunc.e, line 1115, replace
if platform()=WINDOWS then if la<5 then args &= repeat(0,5-la) argdefs &= repeat(#01000004,5-la) elsif remainder(la,2)!=1 then args &= 0 argdefs &= #01000004 -- (C_INT) end if else -- LINUX if la<6 then args &= repeat(0,6-la) argdefs &= repeat(#01000004,6-la) end if end if #ilASM{ [PE64]
with
if platform()=WINDOWS then if la<5 then args &= repeat(0,5-la) argdefs &= repeat(#01000004,5-la) la = 5 end if else -- LINUX -- if la<6 then if la<7 then args &= repeat(0,7-la) argdefs &= repeat(#01000004,7-la) la = 7 end if end if if remainder(la,2)!=1 then args &= 0 argdefs &= #01000004 -- (C_INT) end if #ilASM{ --14/12/20 -- [PE64] [64]
Obviously, a quick './p -c p' and you should be good.
I'll have a quick trawl for other places that need the same treatment.
11. Re: Phix+EuGTK
- Posted by petelomax Dec 15, 2020
- 2449 views
Two more in pcfunc.e. line 206
#ilASM{ [PE32] mov eax,[filename] push ebx --(=0) (for fild qword) shl eax,2 push eax -- lpLibFileName call "kernel32.dll","LoadLibraryA" push eax lea edi,[res] fild qword[esp] add esp,8 call :%pStoreFlt -- ([edi]:=ST0) [PE64] mov rcx,rsp -- put 2 copies of rsp onto the stack... push rsp push rcx or rsp,8 -- [rsp] is now 1st or 2nd copy: -- if on entry rsp was xxx8: both copies remain on the stack -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) -- obviously rsp is now xxx8, whatever alignment we started with mov rcx,[filename] sub rsp,8*5 -- minimum 4 param shadow space, and align shl rcx,2 -- lpLibFileName call "kernel32.dll","LoadLibraryA" mov [rsp],rax lea rdi,[res] fild qword[rsp] -- add rsp,8*5 -- pop rsp mov rsp,[rsp+8*5] -- equivalent to the add/pop call :%pStoreFlt -- ([rdi]:=ST0) [ELF32] mov eax,[filename] -- push 1 -- flags (RTLD_LAZY) push 0x00101 -- flags (RTLD_GLOBAL|RTLD_LAZY) shl eax,2 -- ref->raw push eax -- library name call "libdl.so.2", "dlopen" add esp,8 lea edi,[res] call :%pStoreMint -- [edi]:=eax, as float if rqd [ELF64] mov rdi,[filename] mov rsi,0x00101 -- flags (RTLD_GLOBAL|RTLD_LAZY) shl rdi,2 -- ref->raw (library name) call "libdl.so.2", "dlopen" lea rdi,[res] call :%pStoreMint -- [rdi]:=rax, as float if rqd [] }
should be replaced with this
#ilASM{ [PE32] mov eax,[filename] push ebx --(=0) (for fild qword) shl eax,2 push eax -- lpLibFileName call "kernel32.dll","LoadLibraryA" push eax lea edi,[res] fild qword[esp] add esp,8 call :%pStoreFlt -- ([edi]:=ST0) [ELF32] mov eax,[filename] push 0x00101 -- flags (RTLD_GLOBAL|RTLD_LAZY) shl eax,2 -- ref->raw push eax -- library name call "libdl.so.2", "dlopen" add esp,8 lea edi,[res] call :%pStoreMint -- [e/rdi]:=e/rax, as float if rqd [64] mov rcx,rsp -- put 2 copies of rsp onto the stack... push rsp push rcx or rsp,8 -- [rsp] is now 1st or 2nd copy: -- if on entry rsp was xxx8: both copies remain on the stack -- if on entry rsp was xxx0: or rsp,8 effectively pops one of them (+8) -- obviously rsp is now xxx8, whatever alignment we started with [PE64] mov rcx,[filename] sub rsp,8*5 -- minimum 4 param shadow space, and align shl rcx,2 -- lpLibFileName call "kernel32.dll","LoadLibraryA" [ELF64] mov rdi,[filename] sub rsp,8*5 -- minimum 4 param shadow space, and align mov rsi,0x00101 -- flags (RTLD_GLOBAL|RTLD_LAZY) shl rdi,2 -- ref->raw (library name) call "libdl.so.2", "dlopen" [64] lea rdi,[res] -- add rsp,8*5 -- pop rsp mov rsp,[rsp+8*5] -- equivalent to the add/pop call :%pStoreMint -- [e/rdi]:=e/rax, as float if rqd [] }
and in call(), line 900ish, there are two [PE64] which should be deleted (and then no need to repeat the [64] guards after the first one)
There is a mmap call in builtins\VM\pHeap.e line 1058, and a pthread_exit call in builtins\VM\pThreadN.e line 322 that may need stack alignment, but I have left them alone for now.
Otherwise (surprisingly) I think that's it for the stack alignment issues
12. Re: Phix+EuGTK
- Posted by petelomax Dec 15, 2020
- 2427 views
OOPS - those mods seemed fine on linux, but they've spannered me windows tests...
OK, that's better now - I've updated post #11, make sure you've got the replacement with [PE32]/pStoreFlt in it.
13. Re: Phix+EuGTK
- Posted by irv Dec 15, 2020
- 2415 views
So far, so good. Now a class procedure which calls a shared lib function works. Here's what I'm testing with:
constant LIB = open_dll("libgtk-3.so.0") constant P = C_PTR, I = C_INT integer init = define_c_func(LIB,"gtk_init_check",{P,P},P) if init < 1 or c_func(init,{0,0})=0 then crash("Failed to initialize GTK library!") end if class Window public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) procedure showp() ? "show proc:" ? handle c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{handle}) end procedure function showf() ? "show func:" ? handle c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{handle}) return 1 end function end class procedure main() c_proc(define_c_proc(LIB,"gtk_main",{})) end procedure Window w1 = new() ? w1 ? w1.handle w1.showp() -- was seg fault, now works ? w1.showf() -- works main()
Calling either the showp procedure or the showf function displays the window.
14. Re: Phix+EuGTK
- Posted by irv Dec 15, 2020
- 2397 views
Next question: how to create new instances?
constant LIB = open_dll("libgtk-3.so.0") constant P = C_PTR, I = C_INT integer init_rid = define_c_func(LIB,"gtk_init_check",{P,P},P) if init_rid < 1 or c_func(init_rid,{0,0})=0 then crash("Failed to initialize GTK library!") end if class Window public atom handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) procedure show() c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{handle}) end procedure end class procedure main() c_proc(define_c_proc(LIB,"gtk_main",{})) end procedure Window w1 = new() Window w2 = new() ? w1 ? w1.handle ? w2 ? w2.handle -- same as w1 w1.show() w2.show() -- already shown main()
{"struct","Window",2,1} 30917232 {"struct","Window",2,2} 30917232
Both windows have the same "handle". How can I change my code so each call to new() will call the "gtk_window_new" function again to get a new unique handle?
15. Re: Phix+EuGTK
- Posted by petelomax Dec 15, 2020
- 2396 views
Both windows have the same "handle". How can I change my code so each call to new() will call the "gtk_window_new" function again to get a new unique handle?
Yeah, inline field defaults like that are evaluated precisely once.
The sure-fire solution looks like this
class window integer handle procedure show() ? handle end procedure end class window w1 = new({rand(99)}), w2 = new({rand(99)}) w1.show() w2.show()
I ran out of steam on constructors, so they're incomplete, undocumented, and possibly subject to change (eg, dunno why I made em functions).
class window integer handle function window() this.handle = rand(99) return this end function procedure show() ? handle end procedure end class window w1 = new(), w2 = new() w1.show() w2.show()
Optional parameters aren't as flexible in Phix as they are in OE, they only support entirely fixed constants and single-level calls of length(), command_line(), routine_id(), platform(), machine_bits(), and machine_word(), rather than full expressions. I could perhaps take another run at that if it's genuinely going to help, but I'll probably just run into the same reasons that made me run away screaming the first time.
The other [related] area of doubt is how a constructor might mix and match optional/defaulted bits with its own code - given the above you can't put the gtk_window_new() on the parameter itself, but maybe you could use say null or -1 as a sentinel [I'm sticking with the likewise-also-illegal rand() for this demo]
class window integer handle function window(integer r=-1) -- ?{"r",r,this.handle} if r=-1 then r = rand(99) end if this.handle = r --or if r!=-1 then this.handle = r end if return this end function procedure show() ? handle end procedure end class window w1 = new(), w2 = new({rand(99)}) w1.show() w2.show()
PS I can tell you from today's experience that errors in constructors generate the same level of helpfulness you'd expect from C++
16. Re: Phix+EuGTK
- Posted by irv Dec 15, 2020
- 2391 views
This works to open multiple instances of a class (handles returned by GTK are unique)
constant LIB = open_dll("libgtk-3.so.0") constant P = C_PTR, I = C_INT integer init_rid = define_c_func(LIB,"gtk_init_check",{P,P},P) if init_rid < 1 or c_func(init_rid,{0,0})=0 then crash("Failed to initialize GTK library!") end if class Widget public atom handle public integer sigid procedure show() c_proc(define_c_proc(LIB,"gtk_widget_show",{P}),{this.handle}) end procedure procedure connect(object sig, object fn, object data=0, object closure=0, integer flags=0) integer rid sig = allocate_string(sig) if string(fn) then rid = routine_id(fn) if rid > 0 then fn = call_back(rid) if fn < 1 then crash("Invalid callback for -> %s",{fn}) end if else crash("Can't locate function -> %s",{fn}) end if end if if string(data) then data = allocate_string(data) end if sigid = c_func(define_c_func(LIB,"g_signal_connect_data",{P,P,P,P,P,I},I), {this.handle,sig,fn,data,closure,flags}) end procedure end class class Window extends Widget function Window() this.handle = c_func(define_c_func(LIB,"gtk_window_new",{I},P),{0}) return this end function procedure title(sequence t) c_proc(define_c_proc(LIB,"gtk_window_set_title",{P,P}),{handle,t}) end procedure procedure size(integer w, integer h) c_proc(define_c_proc(LIB,"gtk_window_set_default_size",{P,I,I}),{handle,w,h}) end procedure end class function Quit() c_proc(define_c_proc(LIB,"gtk_main_quit",{})) return 0 end function procedure main() c_proc(define_c_proc(LIB,"gtk_main",{})) end procedure -------------------------------------------------------------------- Window w1 = new() ? w1.handle Window w2 = new() ? w2.handle w1.title("I'm Number One!") w2.title("but I'm on top") w1.size(500,300) w1.connect("destroy","Quit") w1.show() w2.show() main()
Window handles:
10781296 10781968
17. Re: Phix+EuGTK
- Posted by petelomax Dec 15, 2020
- 2389 views
This works
This works on windoze:
constant LIB = open_dll(iff(platform()=LINUX?"libgtk-3.so.0":"libgtk-3-0.dll")), LIBGOBJECT = open_dll(iff(platform()=LINUX?"libgobject-2.0.so.0?":"libgobject-2.0-0.dll")) ... procedure connect(string sig, object fn, data=0, closure=0, integer flags=0) -- integer rid -- sig = allocate_string(sig) if string(fn) then integer rid = routine_id(fn) if rid <= 0 then crash("Can't locate function -> %s",{fn}) end if integer cb = call_back(rid) if cb < 1 then crash("Invalid callback for -> %s",{fn}) end if fn = cb end if -- if string(data) then data = allocate_string(data) end if sigid = c_func(define_c_func(LIBGOBJECT,"g_signal_connect_data",{P,P,P,P,P,I},I), {this.handle,sig,fn,data,closure,flags}) end procedure
Linux is generally lax about .so names, as long as an open_dll has succeeded somewhere, it'll hunt things down. (I've seen lnx/OE code use open_dll("") before now.)
However Winderrs kicks up a right old fuss at the slightest slip-up. (Though some dlls re-export some of their imports, sometimes.)
You don't need allocate_string() if it's already a string (though you do if it's a d/qword sequence of 4/8 bytes per 8-bit char).
I figured it was probably best to only overwrite fn once it was no longer needed for the the error message. Everything else stayed the same.
18. Re: Phix+EuGTK
- Posted by irv Dec 15, 2020
- 2379 views
Great, thanks.
I'm finding Phix classes really make a difference. I've implemented some of the most used widgets and gotten GTK to display a nice window with a label, image, and a couple of buttons. Looks good, and the coding is much easier.
Of course, there are about 300 more to go.
19. Re: Phix+EuGTK
- Posted by irv Dec 16, 2020
- 2332 views
This is working really well.
Question: is there a way to retrieve a class instance if all you have is the handle?
Example: For buttons the first parameter passed to a function linked to the button click event is the handle (a unique pointer supplied by GTK). The second parameter is optional data.
I could do as I had to do in EuGTK, and build a registry of control instances, but I have a suspicion that such a thing is already in phix.
20. Re: Phix+EuGTK
- Posted by petelomax Dec 16, 2020
- 2334 views
Question: is there a way to retrieve a class instance if all you have is the handle?
No. There is a table of fields in builtins\structs.e, but it's private, you'd need to faff about with struct_dx() and field_dx() to decipher it, and the delete_routine()'d reference you're after has been given over to your application, and trying to replicate a "fake copy" would no doubt end in tears.
Here's what I'd do:
class window nullable integer handle function get_handle() return handle end function end class integer hdict = new_dict("handle lookup") function new_window(integer handle) window w = new({handle}) setd(w.get_handle(),w,hdict) return w end function function get_by_handle(integer handle) return getd(handle,hdict) end function function destroy_window(window w) deld(w.get_handle(),hdict) delete(w) return null end function window w = new_window(17) ?w ?get_by_handle(17) w = destroy_window(w)
PS Here's a bugfix to builtins/structs.e, line 536, in case you need it:
--16/12/20: [struct(0)/class(0) and similar should return false, not crash] -- elsif s=NULL then elsif s=NULL and rid!=0 then
21. Re: Phix+EuGTK
- Posted by petelomax Dec 16, 2020
- 2321 views
Actually, this might will be more to your liking
class Widget public atom handle end class integer hdict = new_dict("handle lookup") class Window extends Widget function Window() this.handle = 17 setd(this.handle,this,hdict) return this end function procedure ~Window() deld(this.handle,hdict) end procedure end class function get_by_handle(integer handle) return getd(handle,hdict) end function Window w = new() ?w ?get_by_handle(17) ?dict_size(hdict) -- 1 delete(w) ?dict_size(hdict) -- 0
22. Re: Phix+EuGTK
- Posted by irv Dec 16, 2020
- 2321 views
Super!
Phix is full of tricks.
And faster than a politician pocketing a bribe
23. Re: Phix+EuGTK
- Posted by irv Dec 17, 2020
- 2275 views
Next problem: passing a value to/from a function in a shared lib (.so) which expects a C_DOUBLE doesn't work on Linux 64 bit.
- Edit - problem was in my code.
24. Re: Phix+EuGTK
- Posted by petelomax Dec 17, 2020
- 2291 views
Next problem: passing a value to a function in a shared lib (.so) which expects a C_DOUBLE doesn't work on Linux 64 bit.
Does not really surprise me. With just a quick scan, I spotted this on line 1554 of builtins\VM\pcfunc.e:
fstp dword[rsp]
I'm pretty sure that should be qword. Let me know it if helps.
Ugh. Ignore that. The one on line 1450 is correct, so that's not it.
25. Re: Phix+EuGTK
- Posted by irv Dec 17, 2020
- 2243 views
For testing purposes, I created a shared library in C:
/* libmytest */ #include <stdio.h> double hello(double x) { printf("Hello! Your number was %g\n",x); return x*2.0; }Euphoria:
include std/dll.e atom mylib = open_dll("libmytest.so") atom fn = define_c_func(mylib,"hello",{C_DOUBLE},C_DOUBLE) printf(1,"Got back: %g\n", c_func(fn,{123.456}))
Can anybody explain why I get two hello's?
$ eui eulibtest.ex Hello! Your number was 123.456 Hello! Your number was 123.456 Got back: 246.912Phix:
atom mylib = open_dll("libmytest.so") atom fn = define_c_func(mylib,"hello",{C_DOUBLE},C_DOUBLE) printf(1,"Got back: %g\n", c_func(fn,{123.456}))
$ p plibtest.ex Hello! Your number was 123.456 Got back: 246.912I appears that passing doubles to and from C libraries does work in Phix, and works twice as hard in Euphoria. The problem lies elsewhere in my program. Will keep digging.
26. Re: Phix+EuGTK
- Posted by petelomax Dec 17, 2020
- 2257 views
double hello(double x)Can anybody explain why I get two hello's?
LOL.
Anyway, erm, it will be worth checking what happens when you pass an integer from Phix into a C_DOUBLE.
And then I'd check {1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8,9.9,10.10} are all present and correct.
Forked into: External functions returning C_DOUBLE are called twice (bug?)
27. Re: Phix+EuGTK
- Posted by irv Dec 17, 2020
- 2225 views
double hello(double x)Can anybody explain why I get two hello's?
LOL.
Forked into: External functions returning C_DOUBLE are called twice (bug?)
This is the FIRST TIME EVER that C has done exactly what I told it to do!
28. Re: Phix+EuGTK
- Posted by irv Jan 27, 2021
- 1853 views
Constructing GTK classes works fine, using the syntax below. What I need to find out is what the deconstructor does i.e. ~Window()
- When or how is it called?
- Do I have to connect every widget's "destroy" signal to call the deconstructor for that class of widget?
- What does it do besides remove the Window instance from the dictionary?
class Window extends Widget function Window() this.handle = 17 setd(this.handle,this,hdict) return this end function procedure ~Window() deld(this.handle,hdict) end procedure end class
29. Re: Phix+EuGTK
- Posted by petelomax Jan 27, 2021
- 1848 views
When or how is it called?
The private destroy_instance() in builtins\structs.e is called when the reference count of an instance drops to zero, as per delete_routine(), or when delete(instance) is explicitly invoked.
Should a destructor be defined, it is invoked out of courtesy so the app can do any needed housekeeping.
Do I have to connect every widget's "destroy" signal to call the deconstructor for that class of widget?
I wouldn't think so, delete_routine() should take care of it, eventually - in some cases an explicit delete(instance) might fix some race condition.
Generally speaking all reference counts are tidied up when a routine returns, but an active loop might squirrel away a reference in a hidden temp (or explicit local var) until the next iteration.
Obviously, should you invoke delete(instance) but keep a reference, that is explicitly, and try to keep on using it, that's on you: sometimes it'll crash, sometimes it'll clobber a (new) sibling.
Here I'm just using it so that get_by_handle() does not return (long-)dead window handles, and in such simple code race conditions would seem quite unlikely.
What does it do besides remove the Window instance from the dictionary?
destroy_instance() places the slot in the (also private) instance table on a free list for re-use (ditto now-unused dicts from any dynamic class instances).
If it didn't do that some applications might exhibit a continuous memory leak.
30. Re: Phix+EuGTK
- Posted by irv Jan 28, 2021
- 1792 views
- Last edited Jan 29, 2021
Ok, this is strange. I had to devise a test to see what is going on. I created a simple window with a label and a button. Click the button, and a dialog pops up. Here's what happens:
"---------- BEFORE ------------" hdict before: {24327152,26215072,26239360,26284368,26325424} 1 24327152 -> {"struct","Label",11,1} 2 26215072 -> {"struct","Window",7,1} 3 26239360 -> {"struct","Button",15,1} 4 26284368 -> {"struct","ButtonBox",12,1} 5 26325424 -> {"struct","Box",9,1} "----------- DIALOG CREATED ------------" hdict before: {24327152,26215072,26239360,26284368,26325424} hdict now: {24327152,24328400,24328800,26215072,26239360,26284368,26325424,26325776,28222240} 1 24327152 -> {"struct","Label",11,1} 2 24328400 -> {"struct","Label",11,2} 3 24328800 -> {"struct","Expander",18,1} 4 26215072 -> {"struct","Window",7,1} 5 26239360 -> {"struct","Button",15,1} 6 26284368 -> {"struct","ButtonBox",12,1} 7 26325424 -> {"struct","Box",9,1} 8 26325776 -> {"struct","Box",9,2} 9 28222240 -> {"struct","MessageDialog",17,1} "********** DIALOG CLOSED **************" Deleting Label 24328400 {"struct","Label",11,2} Deleting Box 26325776 {"struct","Box",9,2} Deleting Expander 24328800 {"struct","Expander",18,1} Deleting Message Dialog 28222240 {"struct","MessageDialog",17,1} "========== AFTER =============" hdict at start: {24327152,26215072,26239360,26284368,26325424} hdict now: {24327152,26215072,26239360,26284368,26325424} 1 24327152 -> {"struct","Label",11,1} 2 26215072 -> {"struct","Window",7,1} 3 26239360 -> {"struct","Button",15,1} 4 26284368 -> {"struct","ButtonBox",12,1} 5 26325424 -> {"struct","Box",9,1}
So far, so good, the Before and After match, just as it should be. Now for another run, and a different outcome:
"---------- BEFORE ------------" hdict before: {23229424,25060000,25395632,25436496,25477504} 1 23229424 -> {"struct","Label",11,1} 2 25060000 -> {"struct","Window",7,1} 3 25395632 -> {"struct","Box",9,1} 4 25436496 -> {"struct","ButtonBox",12,1} 5 25477504 -> {"struct","Button",15,1} "----------- DIALOG CREATED ------------" hdict before: {23229424,25060000,25395632,25436496,25477504} hdict now: {23229424,23230672,23231072,25060000,25395632,25395984,25436496,25477504,27124512} 1 23229424 -> {"struct","Label",11,1} 2 23230672 -> {"struct","Label",11,2} 3 23231072 -> {"struct","Expander",18,1} 4 25060000 -> {"struct","Window",7,1} 5 25395632 -> {"struct","Box",9,1} 6 25395984 -> {"struct","Box",9,2} 7 25436496 -> {"struct","ButtonBox",12,1} 8 25477504 -> {"struct","Button",15,1} 9 27124512 -> {"struct","MessageDialog",17,1} "********** DIALOG CLOSED **************" Deleting Label 23230672 {"struct","Label",11,2} Deleting Box 25395984 {"struct","Box",9,2} Deleting Expander 23231072 {"struct","Expander",18,1} Deleting Message Dialog 27124512 {"struct","MessageDialog",17,1} "========== AFTER =============" hdict at start: {23229424,25060000,25395632,25436496,25477504} hdict now: {23229424,25060000,25395632,25436496,25477504} 1 23229424 -> {"struct","Label",11,1} 2 25060000 -> {"struct","Label",11,2} -- NOTE: this is the "handle" previously assigned to the main window. 3 25395632 -> {"struct","Box",9,1} 4 25436496 -> {"struct","ButtonBox",12,1} 5 25477504 -> {"struct","Button",15,1} ?? where's my window gone ??
With no change to my program, every run comes out different, with something not deleted that should have been, and something deleted that should not have been. Sometimes it works properly. Sometimes isn't good enough. What to do?
The delete routine for each of the widgets is similar to this one:
procedure ~Label() --(replacing Label here and below with the appropriate class name) printf(1,"Deleting Label %d ",this.handle) ? this deld(this.handle,hdict) this.destroy() end procedure
31. Re: Phix+EuGTK
- Posted by petelomax Jan 28, 2021
- 1841 views
I can't reproduce that here. The first question I would ask is do dict.e and struct.e match.
Below, I've created handles 171717, 181818, 191919, 202020, and deleted the last two, and done a fair bit of manual editing to the ex.err to make things line up nicely.
C:\Program Files (x86)\Phix\builtins\dict.e: trees[3][01..05] = {171717,{`struct`,`Window`,3,1},0,1,0} trees[3][06..10] = {181818,{`struct`,`Label` ,4,1},1,2,0} trees[3][11..15] = { 16,{`struct`,`Window`,3,2},0,1,0} trees[3][16..20] = { 0,{`struct`,`Label` ,4,2},0,1,0} treenames = {`1`,``,`handle lookup`} freelists = {0,0,11} C:\Program Files (x86)\Phix\builtins\structs.e: instances[3] = {2,{{171717,2545,2550},0}} instances[4] = {2,{{181818,2579,2584},0}}
So, hdict has a freelist of {11,16,0} and the right handles left in place.
instances[3,4] have freelists of {2,0} and the right handles remain.
PS the exact order in which things are created might be helpful, similar to the "Deleting Xxx" messages - should help me reproduce this.
PPS "every run comes out different" is prolly to be expected, as everything comes out in handle order - and will still be so after whatever bug this is gets fixed.
32. Re: Phix+EuGTK
- Posted by petelomax Jan 28, 2021
- 1768 views
Oops, yesterday I said "when the reference count drops to zero, or when delete(instance) is explicitly invoked" - but I forgot that hdict will stop the refcount ever naturally dropping to zero, so you probably will need explicit delete(instance) calls all hooked up.
33. Re: Phix+EuGTK
- Posted by irv Jan 28, 2021
- 1766 views
The problem is very random, changing the order that controls are declared sometimes causes this to fail more frequently. The only thing that is repeatable is that the deleted control always takes on the handle of the control that follows it in the dictionary. This can be anything, since the controls don't show up in the dictionary in the same order that they were declared.
In addition, if I find a combination that fails fairly regularly, adding a ? to the code causes the problem to go away. Maybe that's a clue.
I wouldn't be concerned about this, except that if it is indeed changing the "handle" to an object, that could cause bigger problems later on.
34. Re: Phix+EuGTK
- Posted by petelomax Jan 29, 2021
- 1771 views
I wouldn't be concerned about this
I am. The good news is I've just reproduced it - and the problem is in either dict.e or structs.e
Update: The problem is somewhere in dict.e
Update: pretty sure this is it, in dict.e routine deleteNode, line 228:
trees[tid][root+KEY] = key --bugfix 29/1/21: trees[tid][root+DATA] = trees[tid][temp+DATA]
It was copying the key but not the data. I will have to run quite a few more tests, but so far that looks good.
Update: test\t67dicts.exw added: detects it properly if I comment out that fix, no other problems found.
Many thanks for finding a very nasty bug in a critical component!
35. Re: Phix+EuGTK
- Posted by irv Jan 29, 2021
- 1756 views
Great! That seems to have fixed the problem. Testing will continue.
Deleting Label 13712400 {"struct","Label",11,2} Deleting Box 15661136 {"struct","Box",9,2} Deleting Expander 13712800 {"struct","Expander",18,1} Deleting Message Dialog 17613600 {"struct","MessageDialog",17,1} "========== AFTER =============" hdict at start: {13710320,15615152,15659376,15991168,15991616,15992064,17384032} hdict now: {13710320,15615152,15659376,15991168,15991616,15992064,17384032} 1 13710320 -> {"struct","Label",11,1} 2 15615152 -> {"struct","ButtonBox",12,1} 3 15659376 -> {"struct","Box",9,1} 4 15991168 -> {"struct","Button",15,1} 5 15991616 -> {"struct","Button",15,2} 6 15992064 -> {"struct","Button",15,3} 7 17384032 -> {"struct","Window",7,1}All correct, and seems repeatable regardless of the order in which controls are added.
-- just out of curiosity, I then add: ? bx ? ans ? dlg
Results:
{"struct","Box",9,2} {"struct","Label",11,2} {"struct","MessageDialog",17,1}If I try to access the handle of a deleted control:
? bx.handle
I get a crash (as expected), since it's been deleted:
/usr/local/bin/builtins/structs.e:1110 in function fetch_field() type check failure, s is {"struct","Box",9,2} s = {"struct","Box",9,2} field_name = "handle" context = 0 sdx = <novalue> stype = <novalue> fdx = <novalue> cdii = <novalue> getter = <novalue> flags = <novalue> getfn = <novalue> maxp = <novalue> minp = <novalue>
It seems possible that some code might someday try to access a field of a deleted instance, and crash. Is there a better way to prevent a crash than using, for example:
try ? dlg.handle catch e ? "It's dead, Jim" end try
36. Re: Phix+EuGTK
- Posted by petelomax Jan 29, 2021
- 1719 views
- Last edited Jan 30, 2021
Yes, it's probably better as well as easier and faster to just test it first:
--if Box(bx) then -- best, if/when you can --if struct(bx) then -- or Widget or whatever (see note below) if is_struct(bx,0) then -- (equivalent to struct) --if is_struct(bx,Box) then -- same as Box() --if is_struct(bx,routine_id("Box")) then -- ditto ?bx.handle end if
However, I think you know this, there will (probably) come a day when it succeeds, because {"struct","Box",5,2} or close enough has been re-created for something else, and you or someone else will sorely wish it hadn't. [DONE, ready for next release]
Actually, though, there is something I could and should do to improve upon that: implement a new builtin (and use it in is_struct(), and therefore in both struct() and Box() too)
bool bRes = still_has_delete_routine(object x)
I'd still recommend doing everything you can to nullify all delete()'d instance refs/vars, tho.
Update: Added still_has_delete_routine(), used it, and fixed the glitch in using struct() ready for the next release.
I also cleaned up the original comments and added a couple extra options for testing.
Should you hit that struct() bug, the fix is in pmain.e, DoSubScripts() line 6495:
--31/1/21: -- integer stype = symtab[N][S_ltype] integer stype = symtab[N][S_vtype]