Class: BarkestCore::GlobalStatus
- Inherits:
-
Object
- Object
- BarkestCore::GlobalStatus
- Defined in:
- app/models/barkest_core/global_status.rb
Overview
An interface to a global status/lock file.
The global status/lock file is a simple two line file. The first line is the global status message. The second line is the global status progress.
The real magic comes when we take advantage of exclusive locks. The process that will be managing the status takes an exclusive lock on the status/lock file. This prevents any other process from taking an exclusive lock. It does not prevent other processes from reading from the file.
So the main process can update the file at any time, until it releases the lock. The other processes can read the file at any time, and test for the lock state to determine if the main process is still busy.
Constant Summary collapse
- FailureToLock =
The exception raised in the
lock_for
method whenraise_on_failure
is set. Class.new(StandardError)
Class Method Summary collapse
-
.current ⇒ Object
Gets the current status from the status/lock file.
-
.lock_for(raise_on_failure = false, &block) ⇒ Object
Runs the provided block with a lock on the status/lock file.
-
.locked? ⇒ Boolean
Determines if any process currently holds the lock on the status/lock file.
Instance Method Summary collapse
-
#acquire_lock ⇒ Object
Acquires the lock on the status/lock file.
-
#get_message ⇒ Object
Gets the current status message from the status/lock file.
-
#get_percentage ⇒ Object
Gets the current progress from the status/lock file.
-
#get_status ⇒ Object
Gets the current status from the status/lock file.
-
#have_lock? ⇒ Boolean
Determines if this instance has a lock on the status/lock file.
-
#initialize(status_file = nil) ⇒ GlobalStatus
constructor
Creates a new GlobalStatus object.
-
#is_locked? ⇒ Boolean
Determines if any process has a lock on the status/lock file.
-
#lock_file_path ⇒ Object
Gets the path to the global lock file.
-
#release_lock ⇒ Object
Releases the lock on the status/lock file if this instance holds the lock.
-
#set_message(value) ⇒ Object
Sets the status message if this instance has a lock on the status/lock file.
-
#set_percentage(value) ⇒ Object
Sets the status progress if this instance has a lock on the status/lock file.
-
#set_status(message, percentage) ⇒ Object
Sets the status message and progress if this instance has a lock on the status/lock file.
-
#status_file_path ⇒ Object
Gets the path to the global status file.
Constructor Details
#initialize(status_file = nil) ⇒ GlobalStatus
Creates a new GlobalStatus object.
If you specify a status file, then that file will be used for the locking and status reporting. Otherwise, the “global_lock” file will be used.
30 31 32 33 34 |
# File 'app/models/barkest_core/global_status.rb', line 30 def initialize(status_file = nil) @lock_handle = nil @stat_handle = nil @status_file_path = status_file end |
Class Method Details
.current ⇒ Object
Gets the current status from the status/lock file.
See #get_status for a description of the returned hash.
228 229 230 |
# File 'app/models/barkest_core/global_status.rb', line 228 def self.current global_instance.get_status end |
.lock_for(raise_on_failure = false, &block) ⇒ Object
Runs the provided block with a lock on the status/lock file.
If a lock can be acquired, a GlobalStatus object is yielded to the block. The lock will automatically be released when the block exits.
If a lock cannot be acquire, then false is yielded to the block. The block needs to test for this case to ensure that the appropriate error handling is performed.
The only time that the block is not called is if raise_on_failure
is set, in which case an exception is raised instead of yielding to the block.
245 246 247 248 249 250 251 252 253 254 255 256 257 258 |
# File 'app/models/barkest_core/global_status.rb', line 245 def self.lock_for(raise_on_failure = false, &block) return unless block_given? status = GlobalStatus.new if status.acquire_lock begin yield status ensure status.release_lock end else raise BarkestCore::GlobalStatus::FailureToLock.new if raise_on_failure yield false end end |
.locked? ⇒ Boolean
Determines if any process currently holds the lock on the status/lock file.
Returns true if the file is locked, otherwise returns false.
219 220 221 |
# File 'app/models/barkest_core/global_status.rb', line 219 def self.locked? global_instance.is_locked? end |
Instance Method Details
#acquire_lock ⇒ Object
Acquires the lock on the status/lock file.
Returns true on success or if this instance already holds the lock. Returns false if another process holds the lock.
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'app/models/barkest_core/global_status.rb', line 189 def acquire_lock return true if @lock_handle begin @lock_handle = File.open(lock_file_path, File::RDWR | File::CREAT) raise StandardError.new('Already locked') unless @lock_handle.flock(File::LOCK_EX | File::LOCK_NB) @lock_handle.rewind @lock_handle.truncate 0 @stat_handle = File.open(status_file_path, File::RDWR | File::CREAT) raise StandardError.new('Failed to open status') unless @stat_handle @stat_handle.rewind @stat_handle.truncate 0 rescue if @stat_handle @stat_handle.close rescue nil end if @lock_handle @lock_handle.flock(File::LOCK_UN) rescue nil @lock_handle.close rescue nil end @stat_handle = nil @lock_handle = nil end !!@lock_handle end |
#get_message ⇒ Object
Gets the current status message from the status/lock file.
68 69 70 |
# File 'app/models/barkest_core/global_status.rb', line 68 def get_status[:message] end |
#get_percentage ⇒ Object
Gets the current progress from the status/lock file.
74 75 76 77 |
# File 'app/models/barkest_core/global_status.rb', line 74 def get_percentage r = get_status[:percent] r.blank? ? nil : r.to_i end |
#get_status ⇒ Object
Gets the current status from the status/lock file.
Returns a hash with three elements:
- message
-
The current status message.
- percent
-
The current status progress.
- locked
-
The current lock state of the status/lock file. (true for locked, false for unlocked)
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 |
# File 'app/models/barkest_core/global_status.rb', line 93 def get_status r = {} if have_lock? @stat_handle.rewind r[:message] = (@stat_handle.eof? ? 'The current process is busy.' : @stat_handle.readline.strip) r[:percent] = (@stat_handle.eof? ? '' : @stat_handle.readline.strip) r[:locked] = true elsif is_locked? if File.exist?(status_file_path) begin File.open(status_file_path, 'r') do |f| r[:message] = (f.eof? ? 'The system is busy.' : f.readline.strip) r[:percent] = (f.eof? ? '' : f.readline.strip) end rescue r[:message] = 'The system appears busy.' r[:percent] = '' end else r[:message] = 'No status file.' r[:percent] = '' end r[:locked] = true else r[:message] = 'The system is no longer busy.' r[:percent] = '-' r[:locked] = false end r end |
#have_lock? ⇒ Boolean
Determines if this instance has a lock on the status/lock file.
50 51 52 |
# File 'app/models/barkest_core/global_status.rb', line 50 def have_lock? !!@lock_handle end |
#is_locked? ⇒ Boolean
Determines if any process has a lock on the status/lock file.
56 57 58 59 60 61 62 63 64 |
# File 'app/models/barkest_core/global_status.rb', line 56 def is_locked? begin return true if have_lock? return true unless acquire_lock ensure release_lock end false end |
#lock_file_path ⇒ Object
Gets the path to the global lock file.
44 45 46 |
# File 'app/models/barkest_core/global_status.rb', line 44 def lock_file_path @lock_file_path ||= WorkPath.path_for('global_lock') end |
#release_lock ⇒ Object
Releases the lock on the status/lock file if this instance holds the lock.
Returns true.
169 170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'app/models/barkest_core/global_status.rb', line 169 def release_lock return true unless @lock_handle begin '' @lock_handle.flock(File::LOCK_UN) ensure @stat_handle.close rescue nil @lock_handle.close rescue nil @stat_handle = @lock_handle = nil end true end |
#set_message(value) ⇒ Object
Sets the status message if this instance has a lock on the status/lock file.
Returns true after successfully setting the message. Returns false if this instance does not currently hold the lock.
130 131 132 133 134 |
# File 'app/models/barkest_core/global_status.rb', line 130 def (value) return false unless have_lock? cur = get_status set_status(value, cur[:percent]) end |
#set_percentage(value) ⇒ Object
Sets the status progress if this instance has a lock on the status/lock file.
Returns true after successfully setting the progress. Returns false if this instance does not currently hold the lock.
142 143 144 145 146 |
# File 'app/models/barkest_core/global_status.rb', line 142 def set_percentage(value) return false unless have_lock? cur = get_status set_status(cur[:message], value) end |
#set_status(message, percentage) ⇒ Object
Sets the status message and progress if this instance has a lock on the status/lock file.
Returns true after successfully setting the status. Returns false if this instance does not currently hold the lock.
154 155 156 157 158 159 160 161 162 |
# File 'app/models/barkest_core/global_status.rb', line 154 def set_status(, percentage) return false unless have_lock? @stat_handle.rewind @stat_handle.truncate 0 @stat_handle.write(.to_s.strip + "\n") @stat_handle.write(percentage.to_s.strip + "\n") @stat_handle.flush true end |
#status_file_path ⇒ Object
Gets the path to the global status file.
38 39 40 |
# File 'app/models/barkest_core/global_status.rb', line 38 def status_file_path @status_file_path ||= WorkPath.path_for('global_status') end |