threading in win32

S

Shea Martin

I have a TK app. On a button presses, I call a windows copy command
which can take up to 10 minutes to run. In order to not freeze the GUI,
so that the gui can be doing other stuff, I launch the command in a thread.

Running the command with system, does not let me get the output of the
command, thought it does not lock gui up at all. So, I tried two other
versions. I had the most success with version 3. I believe version 3 is
what the TkTextIo example uses in ruby 1.8.3. See below.

# callback for tk button press
def buttonPress
Thread.new do
Button.state( "disable" );
runCommand( "copy /Y bigfile1 dest1" );
runCommand( "copy /Y bigfile2 dest2" );
runCommand( "copy /Y bigfile3 dest3" );
Button.state( "active" );
end
end

#version 1
def runCommand( cmd )
system( cmd )
end

#version 2
def runCommand( cmd )
IO.popen( cmd ) do | cmd_out |
cmd_out.each_line do | line |
MyTextArea.insert( 'end', line, 'normal' )
end
end
end

#version 3
def runCommand( cmd )
cmd_pipe = IO.popen( cmd )
t = Thread.new { cmd_pipe.each{ | line | addText( line ) } }
t.join
end

Only version 1 allows teh gui to remain responsive. Unfortunately it
does not capture the output of the copy command. I would like to
something with a fork, though win32 ruby does not seem to support it.

Any ideas.

Thanks,

~S
 
A

Ara.T.Howard

I have a TK app. On a button presses, I call a windows copy command which
can take up to 10 minutes to run. In order to not freeze the GUI, so that the
gui can be doing other stuff, I launch the command in a thread.

Running the command with system, does not let me get the output of the
command, thought it does not lock gui up at all. So, I tried two other
versions. I had the most success with version 3. I believe version 3 is what
the TkTextIo example uses in ruby 1.8.3. See below.

# callback for tk button press
def buttonPress
Thread.new do
Button.state( "disable" );
runCommand( "copy /Y bigfile1 dest1" );
runCommand( "copy /Y bigfile2 dest2" );
runCommand( "copy /Y bigfile3 dest3" );
Button.state( "active" );
end
end

#version 1
def runCommand( cmd )
system( cmd )
end

#version 2
def runCommand( cmd )
IO.popen( cmd ) do | cmd_out |
cmd_out.each_line do | line |
MyTextArea.insert( 'end', line, 'normal' )
end
end
end

#version 3
def runCommand( cmd )
cmd_pipe = IO.popen( cmd )
t = Thread.new { cmd_pipe.each{ | line | addText( line ) } }
t.join
end

Only version 1 allows teh gui to remain responsive. Unfortunately it does
not capture the output of the copy command. I would like to something with a
fork, though win32 ruby does not seem to support it.

Any ideas.

i think reading from pipes should not block the whole process in windows, at
least this works fine on my win system:

Thread::abort_on_exception = true
t = Thread::new{ IO::popen("tree c:").read }
(4 << 2).times{ p 42; sleep 0.42 }
puts t.value

does it work on yours?

perhaps you need something like

#version 4
def runCommand( cmd )
Thread::new do
IO::popen("#{ cmd 2>&1 }"){|pipe| pipe.each{|line| addText line}}
end
end

which is to say spawn the thread and let it go. you'll need to prevent two
commands being run at once too...

hth.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
A

Ara.T.Howard

I have a TK app. On a button presses, I call a windows copy command which
can take up to 10 minutes to run. In order to not freeze the GUI, so that the
gui can be doing other stuff, I launch the command in a thread.

Running the command with system, does not let me get the output of the
command, thought it does not lock gui up at all. So, I tried two other
versions. I had the most success with version 3. I believe version 3 is what
the TkTextIo example uses in ruby 1.8.3. See below.

# callback for tk button press
def buttonPress
Thread.new do
Button.state( "disable" );
runCommand( "copy /Y bigfile1 dest1" );
runCommand( "copy /Y bigfile2 dest2" );
runCommand( "copy /Y bigfile3 dest3" );
Button.state( "active" );
end
end

#version 1
def runCommand( cmd )
system( cmd )
end

#version 2
def runCommand( cmd )
IO.popen( cmd ) do | cmd_out |
cmd_out.each_line do | line |
MyTextArea.insert( 'end', line, 'normal' )
end
end
end

#version 3
def runCommand( cmd )
cmd_pipe = IO.popen( cmd )
t = Thread.new { cmd_pipe.each{ | line | addText( line ) } }
t.join
end

Only version 1 allows teh gui to remain responsive. Unfortunately it does
not capture the output of the copy command. I would like to something with a
fork, though win32 ruby does not seem to support it.

Any ideas.

Thanks,

i just found this lying around - i thought i'd messed with it before - it runs
unmodified and without blocking on *nix and win*:

require "tk"

thread_runner = lambda do
Thread::new do
pipe = IO::popen "ruby", "rb+"
pipe.write <<-program
STDERR.reopen STDOUT
STDOUT.sync = true
loop{ p [Process::pid, Time::now]; sleep 0.42 }
program
pipe.close_write
pipe.each{|line| puts line}
end
end

TkButton::new(nil,
"text" => "start thread/process",
"command" => thread_runner).pack( "fill" =>"x")

TkButton::new(nil,
"text" => "say hello",
"command" => lambda{puts "HELLO"}).pack( "fill" =>"x")


TkButton::new(nil,
"text" => 'quit',
"command" => lambda{exit}).pack("fill" => "x")

Tk::mainloop

with it you can start multiple threads reading pipes connected to multiple
processes while the gui still responds.

hth.

-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
S

Shea Martin

Ara.T.Howard said:
i think reading from pipes should not block the whole process in
windows, at
least this works fine on my win system:

Thread::abort_on_exception = true
t = Thread::new{ IO::popen("tree c:").read }
(4 << 2).times{ p 42; sleep 0.42 }
puts t.value

does it work on yours?

perhaps you need something like

#version 4
def runCommand( cmd )
Thread::new do
IO::popen("#{ cmd 2>&1 }"){|pipe| pipe.each{|line| addText line}}
end
end

which is to say spawn the thread and let it go. you'll need to prevent two
commands being run at once too...

hth.

-a


Everything works for fine when cmd = "tree C:\ /F". The text area is
updating, with gobs of output, and I can still use the scroll bars, etc.
But when cmd = "copy /Y 1GB_FILE.BIG BAK_1GB_FILE.BIG", the problem
occurrs. Scrollbars don't work, etc. I guess this must be a windows
limitation. This is a 3.2 GHz machine with H/T enabled, and 2GB of RAM,
so I don't think it is a h/w limitation (though it is only IDE). Also,
if I use the system( cmd ), version it does not happen, and it does not
happen if I just copy the file (manually) while running the gui. I'll
have to try it when I get home tonight on my solaris and linux boxen.

What happens when you try cmd = "copy /Y 1GB_FILE.BIG BAK_1GB_FILE.BIG"?

~S
 
A

Ara.T.Howard

Everything works for fine when cmd = "tree C:\ /F". The text area is
updating, with gobs of output, and I can still use the scroll bars, etc.
But when cmd = "copy /Y 1GB_FILE.BIG BAK_1GB_FILE.BIG", the problem occurrs.
Scrollbars don't work, etc. I guess this must be a windows limitation. This
is a 3.2 GHz machine with H/T enabled, and 2GB of RAM, so I don't think it is
a h/w limitation (though it is only IDE). Also, if I use the system( cmd ),
version it does not happen, and it does not happen if I just copy the file
(manually) while running the gui. I'll have to try it when I get home
tonight on my solaris and linux boxen.

What happens when you try cmd = "copy /Y 1GB_FILE.BIG BAK_1GB_FILE.BIG"?

it hangs. but this is no fault of ruby - it is some crappy detail of copy
and/or the way windows interacts with it. for instance, this program manages
to copy huge files just fine while background threads/processes continue to
run, new ones can be started, and the gui works all the while:

require "tk"
require "fileutils"

gb = 2 ** 30
mb = 2 ** 20
kb = 2 ** 10

unless test ?e, 'gb'
puts "writing file 'gb'. be patient..."
buf = "\0" * mb
open('gb', 'w'){|f| kb.times{ f.write buf }}
puts "wrote 'gb'"
end

FileUtils::rm_rf 'gb2'

Thread::abort_on_exception = true

thread_runner = lambda do
Thread::new do
program = <<-ruby
STDERR.reopen STDOUT
STDOUT.sync = true
loop{ p [Process::pid, Time::now]; sleep 0.42 }
ruby
pipe = IO::popen "ruby", "rb+"
pipe.write program
pipe.close_write
pipe.each do |line|
puts line
end
end
end

thread_runner2 = lambda do
Thread::new do
loop do
if test ?e, 'gb2'
size = File::stat('gb2').size
p 'gb2' => size
break if size == gb
end
sleep 0.42
end
end
Thread::new do
FileUtils::cp 'gb', 'gb2'
end
end

TkButton::new(nil,
"text" => "start thread/process",
"command" => thread_runner).pack( "fill" =>"x")

TkButton::new(nil,
"text" => "huge copy",
"command" => thread_runner2).pack( "fill" =>"x")

TkButton::new(nil,
"text" => "say hello",
"command" => lambda{puts "HELLO"}).pack( "fill" =>"x")


TkButton::new(nil,
"text" => 'quit',
"command" => lambda{exit}).pack("fill" => "x")


Tk::mainloop


hth.


-a
--
===============================================================================
| email :: ara [dot] t [dot] howard [at] noaa [dot] gov
| phone :: 303.497.6469
| Your life dwells amoung the causes of death
| Like a lamp standing in a strong breeze. --Nagarjuna
===============================================================================
 
S

Shea Martin

Ara.T.Howard said:
it hangs. but this is no fault of ruby - it is some crappy detail of copy
and/or the way windows interacts with it. for instance, this program
manages
to copy huge files just fine while background threads/processes continue to
run, new ones can be started, and the gui works all the while:

require "tk"
require "fileutils"

gb = 2 ** 30
mb = 2 ** 20
kb = 2 ** 10

unless test ?e, 'gb'
puts "writing file 'gb'. be patient..."
buf = "\0" * mb
open('gb', 'w'){|f| kb.times{ f.write buf }}
puts "wrote 'gb'"
end

This works, but my problem is I was using 'copy' as an example, b/c I
was pretty sure you would have that command.

The actual command is xbcp which is xbox copy. It comes with
Microsoft's Xbox development software, and is to copy files to a
microsft xbox debug machine. I don't know what the protocol used is, so
I am kind of locked into the command. I suspect, that it is ftp or
samba, but I am getting OT.

Your point a good one, and I guess someday when I have more time, I will
look into implementing xbcp in ruby. (Should be easy if protocol is
indeed FTP).

Thanks for your help, it is much appreciated.

~S
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,776
Messages
2,569,603
Members
45,216
Latest member
topweb3twitterchannels

Latest Threads

Top