Ruby check whether program is currently being closed
See Below taken from my answer Here but seems more pertinent to your question than the one it is currently attached to:
Your best bet is probably a bit easier than signal trapping. The Kernel
Module
actually offers you an #at_exit
method that will be executed just prior to the program actually exiting.
Usage: (from Kernel#at_exit
Docs)
def do_at_exit(str1)
at_exit { print str1 }
end
at_exit { puts "cruel world" }
do_at_exit("goodbye ")
exit
"produces:"
goodbye cruel world
as you can see you can define multiple handlers which will be executed in reverse order when the program exits.
Since Kernel
is included in Object
you can handle Object
specifics as well like
class People
at_exit {puts "The #{self.name} have left"}
end
exit
# The People have left
or even on instances
p = People.new
p.send(:at_exit, &->{puts "We are leaving"})
# We are leaving
# The People have left
Additionally for more specific Object
based implementations you can take a look at ObjectSpace.define_finalizer
.
example of usage:
class Person
def self.finalize(name)
proc {puts "Goodbye Cruel World -#{name}"}
end
def initialize(name)
@name = name
ObjectSpace.define_finalizer(self, self.class.finalize(@name))
end
end
Usage:
p = Person.new("engineersmnky")
exit
# Goodbye Cruel World -engineersmnky
This may not be specifically what you want as this will fire when an Object
is garbage collected as well (not great for ephemeral objects) but if you have objects that should exist throughout the entire application this could still be used similar to an at_exit . Example
# requiring WeakRef to allow garbage collection
# See: https://ruby-doc.org/stdlib-2.3.3/libdoc/weakref/rdoc/WeakRef.html
require 'weakref' #
p1 = Person.new("Engineer")
p2 = Person.new("Engineer's Monkey")
p2 = WeakRef.new(p2)
GC.start # just for this example
# Goodbye Cruel World -Engineer's Monkey
#=> nil
p2
#=> WeakRef::RefError: Invalid Reference - probably recycled
exit
# Goodbye Cruel World -Engineer
As you can see the defined finalizer for p2
fired because the Person
was gc'd but the program has not exited yet. p1
's finalizer waited until exit to fire because it retained its reference throughout the application.
Ruby: how to check if a file is still open?
Reasonable (=all) OSs close fds automatically when a program ends, no matter what the code does.
As for ruby code trying to be nice during execution, Ruby has finalizers that autoclose when a variable that used to reference a filedescriptor (or an encapsulated filedescriptor) gets garbage-collected.
On a UNIX system, you can check open files with lsof.
The code below demonstrates the concepts:
rb.rb:
def func()
fd = IO.sysopen("file.txt", "a")
myios = IO.new(fd)
myios.puts "new line"
end
func
sleep 3 #most likely open here unless the GC managed to run
GC.start #should be closed after this point
sleep 3
Now if you invoke it with:
$ ruby rb.rb & pid=$!; while kill -0 $pid; do if lsof -p $pid | grep -q file.txt; then echo open; else echo closed; fi; sleep 0.3; done
you'll probably get one "closed" (before the ruby code catches up), 3 seconds of open and then 3 seconds of closed.
If you don't want to rely on finalizers (which are will run at indeterministic times, because they rely on the garbage collecter), then the block syntax for opening files is really nice in ruby -- the end of the block will deterministically close the file at the very point where the block ends.
How to check if the file is still locked by current thread?
If f.flock(File::LOCK_EX | File::LOCK_NB)
returns non false
value then f
IS locked. It will keep the lock until you close the file or explicitly call f.flock(File::LOCK_UN)
. You don't have to check whether it is locked again. To explain what really happens there we need to look into a file system internals and related system calls first:
File Descriptor Table Open File Table i-node Table Directory Index
╒════════════════════╕ ╒═════════════╕ ╒════════════╕ ╒═════════════╕
┃3 ..................┣━━━━━━▷┃ open file1 ┣━━┳━━━▷┃ /tmp/file1 ┃◃━━━━┫ file1 ┃
┃4 ..................┣━━━━━━▷┃ open file1 ┣━━┚ ┏━▷┃ /tmp/file2 ┃◃━━━━┫ file2 ┃
┃5 ..................┣━━━┳━━▷┃ open file2 ┣━━━━┚
┃6 ..................┣━━━┚
The key point in this diagram is that there are two different and unrelated entry points into the i-node Table: Open File Table and Directory Index. Different system calls work with different entry points:
- open(file_path) => finds i-node number from Directory Index and creates an entry in Open File Table referenced by File Descriptor Table (one table per process), then increments ref_counter in the related i-node Table entry.
- close(file_descriptor) => closes (frees) related File Descriptor Table entry and related entry from Open File Table (unless there are other referencing File Descriptors), then decrements ref_counter in related i-node Table entry (unless Open File entry stays open)
- unlink(file_path) => there is no Delete system call! Unlinks i-node Table from Directory Index by removing entry from Directory Index. Decrements counter in the related i-node Table entry (unaware of Open File Table!)
- flock(file_desriptor) => apply/remove lock on entries in Open File Table (unaware of Directory Index!)
- i-node Table entry is removed (practically deleting a file) IFF ref_counter becomes Zero. It can happen after close() or after unlink()
The key point here is that unlink not necessarily deletes a file(the data) immediately! It only unlinks Directory Index and i-node Table. It means that even after unlink the file may still be open with active locks on it!
Keeping that in mind, imagine the following scenario with 2 threads, trying to synchronise on a file using open/flock/close and trying to cleanup using unlink:
THREAD 1 THREAD 2
==================================================
| |
| |
(1) OPEN (file1, CREATE) |
| (1) OPEN (file1, CREATE)
| |
(2) LOCK-EX (FD1->i-node-1) |
[start work] (2) LOCK-EX (FD2->i-node-1) <---
| . |
| . |
(3) work . |
| (3) waiting loop |
| . |
[end work] . |
(4) UNLINK (file1) . -----------------------
(5) CLOSE (FD1)--------unlocked------> [start work]
| |
| |
(6) OPEN (file1, CREATE) |
| |
| (5) work
(7) LOCK-EX (FD1->i-node-2) |
[start work] !!! does not wait |
| |
(8) work |
| |
- (1) both threads open(potentially create) the same file. As a result there is a link from Directory Index to i-node Table. Each thread gets its own File Descriptor.
- (2) both threads try to get an exclusive lock using File Descriptor they get from an open call
- (3) first thread gets a lock and second thread is blocked (or is trying to get a lock in a loop)
- (4) first thread finishes a task and deletes (unlink) a file. At this point link from Directory Index to i-node is removed and we won't see it in the directory listing. BUT, the file is still there and is open in two threads with an active lock! It simply lost its name.
- (5) first thread closes File Descriptor and as a result releases a lock. Thus second thread gets a lock and starts working on a task
- (6) first thread repeats and tries to open a file with the same name. But is it the same file as before? No. Because at this point there is no file with a given name in Directory Index. So it creates a NEW file instead! new i-node Table entry.
- (7) first thread gets a lock on a NEW file!
- (8) and we get two threads with a lock on two different files and UNsynchronised
The problem in the above scenario is that open/unlink work on Directory Index, while lock/close work on File Descriptors, which are not related to each other.
To solve this issue we need to synchronise these operations through some central entry point. It can be implemented by introducing a singleton service which will provide this synchronisation using a Mutex or primitives from Concurrent Ruby.
Here is one possible PoC implementation:
class FS
include Singleton
def initialize
@mutex = Mutex.new
@files = {}
end
def open(path)
path = File.absolute_path(path)
file = nil
@mutex.synchronize do
file = File.open(path, File::CREAT | File::RDWR)
ref_count = @files[path] || 0
@files[path] = ref_count + 1
end
yield file
ensure
@mutex.synchronize do
file.close
ref_count = @files[path] - 1
if ref_count.zero?
FileUtils.rm(path, force: true)
@files.delete(path)
else
@files[path] = ref_count
end
end
end
end
And here is your re-written example from the question:
FS.instance.open('a.txt') do |f|
if f.flock(File::LOCK_EX | File::LOCK_NB)
# you can be sure that you have a lock
end
# 'a.txt' will finally be deleted
end
Check if socket-client is still connected
I don't have experience programming ruby. If it was .Net, what you pretend is not possible without some kind of keep-alive protocol. If the server does not get data from client after x seconds it assumes the client disconnected. The client must have a timer to send something to the server when not communicating.
How do I let the user know whether X or O won?
I recommend that you create a Board
class that knows how to draw itself based on the turns. This would remove a lot of the code from Game
and make it easier to follow.
To answer your question, the code you have here:
if str == "xxx" or str == "ooo"
return true
end
doesn't distinguish between X winning or O winning. Here you can modify the code to check separately for X win and O win, and display the appropriate message.
How can I launch a application with Ruby?
Run an application in various ways:
Kernel#system
runs my_program in a subshell and returns true
if the subshell exits successfully, false
otherwise.
system("my_program")
Kernel#exec
replaces the currently executing process with my_program.
exec("my_program")
%x()
will run the program and return the output.
%x(my_program)
Related Topics
I Want to Return a Single Result Using .Find() with Ruby on Rails
Adding Comment to Yaml Programmatically
Ruby - How to Retrieve Sum in Array Group by Multiple Keys with Condition Max
Utc Time Resets to 2000-01-01 (Ruby). How to Prevent the Time from Resetting
Ruby Autoload Conflicts Between Gmail and Parse_Resource Gems
Why Rails Instance Method Can Be Used as Class Method in Rspec
Don't the Ruby Methods Instance_Eval() and Send() Negate the Benefits of Private Visibility
How to Get a Reference to a 'Dynamic' Object Call
Selenium Webdriver Take Screenshot of Viewport Only
Connection Refused Using Sunspot and Solr in Rails
Why Must I Explicitly Call Self on Accessor When Using the Array Union Operator |= in Ruby
Rails 4 Use Application Helpers Inside Initializers
{|_, E| E.Length>1} What Is the Use of Underscore ( _ ) in Ruby