TCL Destructor is not called on window close - tcl

I have a class DataDialog, which contains a destructor like
destructor {
puts "DataDialog has been destructed"
#further code
}
If I close the application via the X-window-button this destructor is not called. If I close it over file->close it is called.
On the toplevel I have the following
wm protocol . WM_DELETE_WINDOW {
Exit 0
}
How can I change this behaviour to call all destructors (or at least the one of my class DataDialog)?

How about
wm protocol . WM_DELETE_WINDOW {
DataDialog destroy
Exit 0
}

If you call exit (or if you delete the interpreter) then Tcl does not guarantee to call destructors. That's because tearing down everything in memory can be surprisingly expensive. Critical resources typically have extra exit handlers registered at the C level to ensure that they get cleaned up correctly, but they're very much the exception; the only ones you likely use on a common basis are channels (which are flushed on exit). There isn't any Tcl-level for doing this; those handlers are usually called at points where it is no longer safe to call Tcl commands.
However, the default behaviour for handling cooperative window closure is effectively to send a <Destroy> message to the window. Those aren't entirely interceptable (the window will go away) but you can bind to them to find out when they occur. Be aware of one quirk though: toplevel windows also listen to all the events of their children (though they don't get killed by passing <Destroy>s unless they're sent to them directly). Check that %W actually refers to the window that you think you're really listening to before taking special action.

Related

Dealing with invalid filehandles (and maybe other invalid objects too)

As indicated by Tom Browder in this issue, the $*ARGFILES dynamic variable might contain invalid filehandles if any of the files mentioned in the command line is not present.
for $*ARGFILES.handles -> $fh {
say $fh;
}
will fail with and X::AdHoc exception (this should probably be improved too):
Failed to open file /home/jmerelo/Code/perl6/my-perl6-examples/args/no-file: No such file or directory
The problem will occur as soon as the invalid filehandle is used for anything. Would there be a way of checking if the filehandle is valid before incurring in an exception?
You can check if something is a Failure by checking for truthiness or definedness without the Failure throwing:
for $*ARGFILES.handles -> $fh {
say $fh if $fh; # check truthiness
.say with $fh; # check definedness + topicalization
}
If you still want to throw the Exception that the Failure encompasses, then you can just .throw it.
TL;DR I thought Liz had it nailed but it seems like there's a bug or perhaps Ugh.
A bug?
It looks like whenever the IO::CatHandle class's .handles method reaches a handle that ought by rights produce a Failure (delaying any exception throw) it instead immediately throws an exception (perhaps the very one that would work if it were just delayed or perhaps something broken).
This seems either wrong or very wrong.
Ugh
See the exchange between Zoffix and Brad Gilbert and Zoffix's answer to the question How should I handle Perl 6 $*ARGFILES that can't be read by lines()?
Also:
https://github.com/rakudo/rakudo/issues/1313
https://github.com/rakudo/rakudo/search?q=argfiles&type=Issues
https://github.com/rakudo/rakudo/search?q=cathandle&type=Issues
A potential workaround is currently another bug?
In discussing "Implement handler for failed open on IO::CatHandle" Zoffix++ closed it with this code as a solution:
.say for ($*ARGFILES but role {
method next-handle {
loop {try return self.IO::CatHandle::next-handle}
}
})
I see that tbrowder has reopened this issue as part of the related issue this SO is about saying:
If this works, it would at least be a usable example for the $*ARGFILES var in the docs.
But when I run it in 6.d (and see similar results for a 6.c), with or without valid input, I get:
say not yet implemented
(similar if I .put or whatever).
This is nuts and suggests something gutsy is getting messed up.
I've searched rt and gh/rakudo issues for "not yet implemented" and see no relevant matches.
Another workaround?
Zoffix clearly intended their code as a permanent solution, not merely a workaround. But it unfortunately doesn't seem to work at all for now.
The best I've come up with so far:
try {$*ARGFILES} andthen say $_ # $_ is a defined ArgFiles instance
orelse say $!; # $! is an error encountered inside the `try`
Perhaps this works as a black-and-white it either all works or none of it does solution. (Though I'm not convinced it's even that.)
What the doc has to say about $*ARGFILES
$*ARGFILES says it is an instance of
IO::ArgFiles which is doc'd as a class which
exists for backwards compatibility reasons and provides no methods.
And
All the functionality is inherited from
IO::CatHandle which is subtitled as
Use multiple IO handles as if they were one
and doc'd as a class that is
IO::Handle which is subtitled as
Opened file or stream
and doc'd as a class that doesn't inherit from any other class (so defaults to inheriting from Any) or do any role.
So, $*ARGFILES is (exactly functionally the same as) a IO::CatHandle object which is (a superset of the functionality of) an IO::Handle object, specifically:
The IO::CatHandle class provides a means to create an IO::Handle that seamlessly gathers input from multiple IO::Handle and IO::Pipe sources. All of IO::Handle's methods are implemented, and while attempt to use write methods will (currently) throw an exception, an IO::CatHandle is usable anywhere a read-only IO::Handle can be used.
Exploring the code for IO::CatHandle
(To be filled in later?)

Access higher level from script called by fileevent

I'm trying to draw on a canvas that is in the top level of my Tcl/Tk script, but from inside a call by fileevent like this:
canvas .myCanvas {}
proc plot_Data { myC inp } { $myC create rectangle {} }
fileevent $inp readable [list plot_Data .myCanvas $inp ]
pack .myCanvas
I have found out that the script called by fileevent (plot_Data) lives in a different space.
The script for a file event is executed at global level (outside the context of any Tcl procedure) in the interpreter in which the fileevent command was invoked.
I cannot make the two meet. I have definitely narrowed it down to this: plot_Data just can't access .myCanvas . Question: How can the fileevent script plot on the canvas?
The goal of this is live plotting, by the way. $inp is a pipe to a C-program that reads data from a measurement device. It is imho rightly configured with fconfigure $inp -blocking 0 -buffering none.
Callback scripts (except for traces, which you aren't using) are always called from the context of the global namespace. They cannot see any stack frames above them. This is because they are called at times that aren't closely controlled; there's no telling what the actual stack would be, so it is forced into a known state.
However, canvases (and other widgets) have names in the global namespace as well. Your callbacks most certainly can access them, provided the widget has not been destroyed, and might indeed be working. You just happen to have given it an empty list of coordinates to create, which is not usually legal to use with a canvas item.
Since you are using non-blocking I/O, you need to be aware that gets may return the empty string when reading an incomplete line. Use fblocked to determine if a partial read happened; if it does, the data is in a buffer on the Tcl side waiting for the rest of the line to turn up, and it is usually best to just go to sleep and wait for the next fileevent to fire.
A problem that might bite you overall is if the C program is in fully buffered mode; this is true by default when writing output from C to a pipe. Setting the buffering on the Tcl side won't affect it; you need to use setvbuf on the C side, or insert regular fflush calls, or use Expect (which pretends to be an interactive destination, though at quite a lot of cost of complexity of interaction) or even unbuffer (if you can find a copy).

Using a tcl proc to login to devices

I have a tcl (expect) script to log into devices and transfer files. Unfortunately, the files are large, and during the transfer period the ssh connection ends (the files are still transferred though). So I basically have to login again before I can perform more actions on the device. Since the whole login process is long, I put it in a proc. The issue is that the proc logs into the device, but after the login, the script sends the commands to the terminal as, for some reason, the commands no longer reach the device. I cant figure out why the session I logged into in the proc does not carry over to the rest of the script.
proc login {} {
#login code - it works because I took it from the main script (which works).
# variables are all declared as global, no errors are thrown. Login is successful
}
login
send "show\r" ;# this command is not sent to the device,
#instead it prints to the terminal. When in the main script,
#these commands would not be printed to the terminal window.
Is there a command I am missing to maybe return the login session to the rest of the script? something similar to the interact command, but to the rest of the script.
This is a tricky one. The expect man page says this:
Expect takes a rather liberal view of scoping. In particular, variables read by commands specific
to the Expect program will be sought first from the local scope, and if not found, in the global
scope. For example, this obviates the need to place "global timeout" in every procedure you write
that uses expect. On the other hand, variables written are always in the local scope (unless a
"global" command has been issued). The most common problem this causes is when spawn is executed in
a procedure. Outside the procedure, spawn_id no longer exists, so the spawned process is no longer
accessible simply because of scoping. Add a global spawn_id to such a procedure.
So, add global spawn_id as the first line of the login proc
To make a procedure that evaluates code in its caller's scope, you need to use the uplevel command inside it. This lets you do what is essentially a macro very easily:
proc login {} {
uplevel {
# Put your code in here, which might look like this
spawn ssh user#host ...
expect Password:
send $thePassword\r
expect "bash$"
}
}
Then, when you use login it will work exactly like the commands you have inside the uplevel in the procedure, as if they'd been typed in place of the login call.
This isn't usually a particularly recommended approach, as it is very easy to make code that is inflexible and inclined to break unexpectedly, but in your case it is a very easy approach since you can easily guarantee to only call login at a sensible place in the overall program structure. (The uplevel command is more commonly used with scripts passed in with arguments — it's just like you're passing in a block — but that's not what you need.)

Object initialization in RubyMotion

I am new to RubyMotion and trying to understand how object initialization works. Suppose a simple class with one class and one instance method:
class Something
def self.getSomething
BubbleWrap::HTTP.post("http://example.com") do |response|
p response
end
end
def getSomething
BubbleWrap::HTTP.post("http://example.com") do |response|
p response
end
end
end
Now, why does the following work:
Something.getSomething
And the next snippet not, well, sometimes (ran this snippet and the runtime crashed 8 out of 10 times).
something = Something.new
something.getSomething
I am doing it wrong. Any pointers in the right direction?
Use instance variables:
#something = Something.new
#something.getSomething
RubyMotion has a handful of bugs related to local variables and blocks. You're assigning to something and then calling something.getSomething, which then uses BubbleWrap's asynchronous HTTP.post method. The BubbleWrap HTTP block runs, but in the meantime, the method you're calling something.getSomething from has completed execution. Since something is a local variable, it gets garbage collected when the method exits. So when the HTTP request completes and the block is called, the block no longer exists.
You're probably seeing random inconsistent errors (and once in a while an actual working request) because each time, the memory location where the block was stored was reclaimed for something else (or once in a while, it wasn't reclaimed at all so the block is still there). None of this happens when you use an instance variable instead, because when the calling method finishes execution, the instance variable sticks around.
This behavior is definitely unexpected; I know a couple issues have been filed (myself included) to get this fixed.

Flex WebService invokeAllPending never called

I been using the WebService and Operation classes of Flex Framework for a while, and after some ups and downs (more downs than ups, haha) I'm in process of refactoring all its uses with some utility classes/wrappers.
After browsing a little of the code of mx.rpc.soap.Operation I noticed that when you use the method "send" and the web service is not ready then the call is queued to an internal array (pendingInvocations:Array in line 1142). But the funny thing is that the invocations in the queue are never called again.
This is a bug or there is something I'm doing wrong?
I'm considering extending mx.rpc.soap.Operation, overriding "send" and testing if there are invocation queued, calling invokeAllPending (a mx_internal method that pops all the queued invocations) my self.
But the other problem is that that method is mx_internal, so I don't know if Adobe is gonna change it any time soon.
Any advice?
Thanks in advance
It's not a bug. Take a look at the definition for AbstractWebService; it defines a method called unEnqueueCalls (which is right up near the top of the list of awkward method names that I've seen :)). This method loops through all the operations in the webservice and invokes the pending calls for each operation by calling that invokeAllPending method you found.
unEnqueueCalls is itself called from the WebService class, in the wsdlFault and wsdlHandler methods, one of which runs when your WSDL is finished loading.
So, everything is all accounted for; you don't need to override anything.