That is the question. I called a function using a thread, but when I return to main control, main becomes in sleep status within a few moments.
Example (Look here is a line that calls a Win32API function GetMessage):
Start()
e = Thread.new { Look() }
for _i in 0..1000
puts e.status
end
This code should print the e's status 1000 times, but it just prints once. I cannot find a reasonable error. Look does not return until it gets a message, so I suspect that this could be causing an error.
My guess is that the status of e is nil, which puts displays as an empty string.
A nil status means the thread terminated abnormally.
Whatever is wrong, your code is printing something 1000 times, and you should put some visible text in the puts to show that.
Also, rather than use a throw-away variable _i, write
1000.times do
puts e.status
end
I solved thanks for all your help,
the error was in that i only need to change GetMessageA for PeekMessage
so thread doesn't interpret that is dead jeje
Some.Proxy
Related
I have a simple ruby script I would like to run in the rails console, using bundle exec rails c
ids = [1, 2]
if ids.length() > 5
puts "More than 5 ids, quitting"
exit
end
ids.each do |id|
puts id
end
The rails console quits the program as soon as it sees the exit command. What would be the best way around this?
One way to do this is to use throw/catch:
ids = [1, 2]
catch(:bail) do
if ids.length > 5
puts "More than 5 ids, quitting"
throw :bail
end
ids.each do |id|
puts id
end
end
catch :bail defines a block that is labeled with the symbol :bail (the name doesn't really matter). It will execute normally until you throw :bail at which point Ruby zips up the call stack until its finds a corresponding catch. If no matching catch is found an UncaughtThrowError will be raised.
This is somewhat like break but can be used in any context and to break out of deeply nested constructs.
Another alternative would be to simply define a method or raise (and rescue) an exception if the input is invalid.
Raise ArgumentError When Passed Arguments are Somehow Invalid
There are a lot of ways to fix your code, but your essential problem is that Kernel#exit will exit both a standard REPL session and an application, not simply return from a method or break out of an if-statement. Since your code doesn't show that you're inside a method, you can't use return because there's no appropriate method or closure to return from. You could possibly work around calling #exit with nested irb sessions or the like, but that's ultimately solving the wrong problem.
In most cases, you want to raise an exception with a useful message rather than simply exit when something goes unfixably wrong. The following would do that in your application, while in an irb or pry console it will raise the exception but remain within your REPL session:
ids = [1, 2]
raise ArgumentError, "More than 5 ids: #{ids}" if ids.length > 5
ids.each { |id| puts id }
If you were inside a method, you could use #warn and then return an appropriate return value instead. However, since you are presumably inside the top-level object of your REPL, there's nothing to return from, so this is likely your best general-purpose option.
In prep for hurricane irma I wrote a quick trash script to download a bunch of exercises off exercism.io. It works, but there's an error at the call to threads.each that I don't understand, all the code up until threads.each is synchronous if I understand correctly, so I'm not sure what the best way to fix is.:
rb:14:in '<main>': undefined method 'each' for nil:NilClass (NoMethodError)
It's interesting to me because I get the error but the program still runs as expected, so I'm sure I'm not writing this properly.
language = ARGV[0]
exercises = `exercism list #{#language}`.split("\n")
threads = exercises.map do |exercise|
break if exercise == ''
Thread.new do
system("exercism fetch #{language} #{exercise}")
end
end
threads.each(&:join)
Use next instead of break so that threads is still set if any exercises are blank. break will cancel the whole loop, but next will skip only the current iteration.
Then some threads could still be nil if their exercise is blank, because no thread has started for them. You can use threads.compact.each(&:join) to skip these nil values.
Or if you need the break, then add to threads inside the loop like:
threads = []
exercises.each do |exercise|
break if exercise == ''
threads << Thread.new do
system("exercism fetch #{language} #{exercise}")
end
end
Basically in my search for code which will loop, and terminate upon user input, i managed to find code here, and after some alteration, produced this:
#desired destination method, however loop persists!!
def desired_method
print "method entered"
end
Thread.new do
while line = STDIN.gets
break if line.chomp == "" # code detects user input
end
desired_method
end
# program will loop here until user presses enter
loop do
puts "foo"
sleep 1
end
This code is brilliant, and will enter the method 'desired_method' when i hit enter, however the loop persists!! printing 'foo' perpetually after "method entered"!!. I have done some research prior to posting this question on how to kill threads, which i believe may hold the answer. My attempts included naming the thread and using the 'thread.exit' function to kill it, however these techniques have remained unsuccessful.
Can anyone illustrate how i might enter the 'desired_method' method without the persisting "foo" print?
Thanks in advance, and greatly appreciated.
An easy solution here is to use semaphore, signalling between threads with a variable access to both places:
# This will be out stop flag, for signalling between threads.
#time_to_stop = false
def desired_method
print "method entered"
# Here we want the loop in the other thread to stop.
#time_to_stop = true
end
Thread.new do
while line = STDIN.gets
break if line.chomp == "" # code detects user input
end
desired_method
end
# program will loop here until user presses enter
loop do
puts "foo"
sleep 1
# only continue if the stop flag is not set.
break if #time_to_stop
end
Hope this helps.
jobs.each do |job|
msg job.name do
break if stop_all_jobs?
job.run!
end
end
def msg(msg, &block)
puts 'START ' + msg
yield
puts 'END ' + msg
end
In the above example break does not break out of the loop as expected. It only breaks out of the msg code block.
This seems a little odd, but I guess it is based on context, that said, how do I break out of the loop from code which is within a yielded code block?
One way is to use throw/catch. No, not exceptions, Ruby has a separate control-of-flow feature that works a bit like exceptions, without all the overhead (although I must admit I'm not sure that there isn't any overhead in using it):
catch :stop_all_jobs do
msg job.name do
throw :stop_all_jobs if stop_all_jobs?
job.run!
end
end
You can even pass a value as the second argument to throw which will be the result of the catch block.
A potentially more readable solution would, of course, be to pack the code up in a method and use return in place of break. But that wouldn't be as fun.
Use next instead of break.
I followed the example from http://www.ruby-doc.org/stdlib/libdoc/monitor/rdoc/index.html and modified the code a bit:
require 'monitor.rb'
buf = []
buf.extend(MonitorMixin)
empty_cond = buf.new_cond
producer = Thread.start do
# producer
line = "produce at #{Time.now}"
#while line
buf.synchronize do
puts "==> #{line}"
buf.push(line)
empty_cond.signal
end
sleep(2)
#line = "produce at #{Time.now}"
#end
end
loop do
buf.synchronize do
empty_cond.wait_while { buf.empty? }
item = buf.shift
puts "got #{item.inspect}"
end
end
I let the program run. Around 5 min later, it throws a "Segmentation fault". Something related to a deadlock?
/Jack
As your code stands (with the commented out while-statement in producer-loop) the producer thread simply runs through the loop once and exits. The consumer reads the one produced line from buf and then is left in a deadlock waiting for more lines that will never arrive.
Ruby's Thread scheduler has inbuilt deadlock-detection, so it will terminate the program when it sees that the 'consumer'-loop has deadlocked.
To see the deadlock for yourself, turn the producer into a global variable $producer and wrap the loop-statement with $consumer = Thread.start do ... end. Loading the code into irb and evaluating $producer should result in => #< Thread:0x000000010afb58 dead > (and $consumer in a sleeping thread)
Take out the comments relating to producer's while-loop and you'll have a working (infinite) loop that produces the current time at 2 second intervals.