Class: Incline::GlobalStatus

Inherits:
Object
  • Object
show all
Defined in:
lib/incline/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.

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeGlobalStatus

Creates a new GlobalStatus object.



23
24
25
# File 'lib/incline/global_status.rb', line 23

def initialize
  @handle = nil
end

Class Method Details

.currentObject

Gets the current status from the status/lock file.

See #get_status for a description of the returned hash.



200
201
202
# File 'lib/incline/global_status.rb', line 200

def self.current
  global_instance.get_status
end

.lock_for(&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.



214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/incline/global_status.rb', line 214

def self.lock_for(&block)
  return unless block_given?
  status = GlobalStatus.new
  if status.acquire_lock
    begin
      yield status
    ensure
      status.release_lock
    end
  else
    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.

Returns:

  • (Boolean)


191
192
193
# File 'lib/incline/global_status.rb', line 191

def self.locked?
  global_instance.is_locked?
end

Instance Method Details

#acquire_lockObject

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.



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
# File 'lib/incline/global_status.rb', line 169

def acquire_lock
  return true if @handle
  begin
    @handle = File.open(status_file_path, File::RDWR | File::CREAT)
    raise StandardError.new('Already locked') unless @handle.flock(File::LOCK_EX | File::LOCK_NB)
    @handle.rewind
    @handle.truncate 0
  rescue
    if @handle
      @handle.flock(File::LOCK_UN)
      @handle.close
    end
    @handle = nil
  end
  !!@handle
end

#get_messageObject

Gets the current status message from the status/lock file.



53
54
55
# File 'lib/incline/global_status.rb', line 53

def get_message
  get_status[:message]
end

#get_percentageObject

Gets the current progress from the status/lock file.



59
60
61
62
# File 'lib/incline/global_status.rb', line 59

def get_percentage
  r = get_status[:percent]
  r.blank? ? nil : r.to_i
end

#get_statusObject

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)



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/incline/global_status.rb', line 78

def get_status
  r = {}
  if have_lock?
    @handle.rewind
    r[:message] = (@handle.eof? ? 'The current process is busy.' : @handle.readline.strip)
    r[:percent] = (@handle.eof? ? '' : @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.

Returns:

  • (Boolean)


35
36
37
# File 'lib/incline/global_status.rb', line 35

def have_lock?
  !!@handle
end

#is_locked?Boolean

Determines if any process has a lock on the status/lock file.

Returns:

  • (Boolean)


41
42
43
44
45
46
47
48
49
# File 'lib/incline/global_status.rb', line 41

def is_locked?
  return true if have_lock?
  begin
    return true unless acquire_lock
  ensure
    release_lock
  end
  false
end

#release_lockObject

Releases the lock on the status/lock file if this instance holds the lock.

Returns true.



154
155
156
157
158
159
160
161
# File 'lib/incline/global_status.rb', line 154

def release_lock
  return true unless @handle
  set_message ''
  @handle.flock(File::LOCK_UN)
  @handle.close
  @handle = nil
  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.



115
116
117
118
119
# File 'lib/incline/global_status.rb', line 115

def set_message(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.



127
128
129
130
131
# File 'lib/incline/global_status.rb', line 127

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.



139
140
141
142
143
144
145
146
147
# File 'lib/incline/global_status.rb', line 139

def set_status(message, percentage)
  return false unless have_lock?
  @handle.rewind
  @handle.truncate 0
  @handle.write(message.to_s.strip + "\n")
  @handle.write(percentage.to_s.strip + "\n")
  @handle.flush
  true
end

#status_file_pathObject

Gets the path to the global status/lock file.



29
30
31
# File 'lib/incline/global_status.rb', line 29

def status_file_path
  @status_file_path ||= WorkPath.path_for('global_lock')
end