Class: ZK::Locker::LockerBase

Inherits:
Object
  • Object
show all
Includes:
Exceptions, ZK::Logging
Defined in:
lib/zk/locker/locker_base.rb

Overview

Common code for the shared and exclusive lock implementations

One thing to note about this implementation is that the API unfortunately does not follow the convention where bang ('!') methods raise exceptions when they fail. This was an oversight on the part of the author, and it may be corrected sometime in the future.

Direct Known Subclasses

ExclusiveLocker, SharedLocker

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from ZK::Logging

#logger, set_default

Constructor Details

#initialize(client, name, root_lock_node = nil) ⇒ LockerBase

Create a new lock instance.

Parameters:

  • client (Client::Threaded)

    a client instance

  • name (String)

    Unique name that will be used to generate a key. All instances created with the same root_lock_node and name will be holding the same lock.

  • root_lock_node (String) (defaults to: nil)

    the root path on the server under which all locks will be generated, the default is Locker.default_root_lock_node



48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# File 'lib/zk/locker/locker_base.rb', line 48

def initialize(client, name, root_lock_node=nil) 
  @zk = client
  @root_lock_node = root_lock_node || Locker.default_root_lock_node

  @path           = name
  @locked         = false
  @waiting        = false
  @lock_path      = nil
  @parent_stat    = nil
  @root_lock_path = "#{@root_lock_node}/#{@path.gsub("/", "__")}"

  @mutex  = Monitor.new
  @cond   = @mutex.new_cond
  @node_deletion_watcher = nil
end

Instance Attribute Details

#lock_pathString (readonly)

our absolute lock node path

Examples:


'/_zklocking/foobar/__blah/lock000000007'

Returns:

  • (String)


24
25
26
# File 'lib/zk/locker/locker_base.rb', line 24

def lock_path
  @lock_path
end

Instance Method Details

#acquirable?Boolean

Note:

It should be obvious, but there is no way to guarantee that between the time this method checks the server and taking any action to acquire the lock, another client may grab the lock before us (or converseley, another client may release the lock). This is simply meant as an advisory, and may be useful in some cases.

  • If this instance holds the lock is true we return true (as we have already succeeded in acquiring the lock)
  • If this instance doesn't hold the lock, we'll do a check on the server to see if there are any participants who hold the lock and would prevent us from acquiring the lock.
    • If this instance could acquire the lock we will return true.
    • If another client would prevent us from acquiring the lock, we return false.

Returns:

  • (Boolean)

Raises:

  • (NotImplementedError)


118
119
120
# File 'lib/zk/locker/locker_base.rb', line 118

def acquirable?
  raise NotImplementedError
end

#assert!Object

This is for users who wish to check that the assumption is correct that they actually still hold the lock. (check for session interruption, perhaps a lock is obtained in one method and handed to another)

This, unlike #locked? will actually go and check the conditions that constitute "holding the lock" with the server.

Examples:


def process_jobs
  @lock.with_lock do
    @jobs.each do |j| 
      @lock.assert!
      perform_job(j)
    end
  end
end

def perform_job(j)
  puts "hah! he thinks we're workin!"
  sleep(60)
end

Raises:



234
235
236
237
238
239
240
241
242
243
# File 'lib/zk/locker/locker_base.rb', line 234

def assert!
  @mutex.synchronize do
    raise LockAssertionFailedError, "have not obtained the lock yet"            unless locked?
    raise LockAssertionFailedError, "not connected"                             unless zk.connected?
    raise LockAssertionFailedError, "lock_path was #{lock_path.inspect}"        unless lock_path
    raise LockAssertionFailedError, "the lock path #{lock_path} did not exist!" unless zk.exists?(lock_path)
    raise LockAssertionFailedError, "the parent node was replaced!"             unless root_lock_path_same?
    raise LockAssertionFailedError, "we do not actually hold the lock"          unless got_lock?
  end
end

#lock(blocking = false) ⇒ true, ...

Parameters:

  • blocking (true, false) (defaults to: false)

    if true we block the caller until we can obtain a lock on the resource

Returns:

  • (true)

    if we're already obtained a shared lock, or if we were able to obtain the lock in non-blocking mode.

  • (false)

    if we did not obtain the lock in non-blocking mode

  • (void)

    if we obtained the lock in blocking mode.

Raises:

  • (InterruptedSession)

    raised when blocked waiting for a lock and the underlying client's session is interrupted.

See Also:



165
166
167
# File 'lib/zk/locker/locker_base.rb', line 165

def lock(blocking=false)
  raise NotImplementedError
end

#lock!(blocking = false) ⇒ true, ...

Deprecated.

the use of lock! is deprecated and may be removed or have its semantics changed in a future release

Parameters:

  • blocking (true, false) (defaults to: false)

    if true we block the caller until we can obtain a lock on the resource

Returns:

  • (true)

    if we're already obtained a shared lock, or if we were able to obtain the lock in non-blocking mode.

  • (false)

    if we did not obtain the lock in non-blocking mode

  • (void)

    if we obtained the lock in blocking mode.

Raises:

  • (InterruptedSession)

    raised when blocked waiting for a lock and the underlying client's session is interrupted.

See Also:



172
173
174
# File 'lib/zk/locker/locker_base.rb', line 172

def lock!(blocking=false)
  lock(blocking)
end

#lock_basenamenil, String

the basename of our lock path

Examples:


> locker.lock_path
# => '/_zklocking/foobar/__blah/lock000000007'
> locker.lock_basename
# => 'lock000000007'

Returns:

  • (nil)

    if lock_path is not set

  • (String)

    last path component of our lock path



86
87
88
# File 'lib/zk/locker/locker_base.rb', line 86

def lock_basename
  synchronize { lock_path and File.basename(lock_path) }
end

#locked?true, false

returns our current idea of whether or not we hold the lock, which does not actually check the state on the server.

The reason for the equivocation around thinking we hold the lock is to contrast our current state and the actual state on the server. If you want to make double-triple certain of the state of the lock, use #assert!

Returns:

  • (true)

    if we hold the lock

  • (false)

    if we don't hold the lock



100
101
102
# File 'lib/zk/locker/locker_base.rb', line 100

def locked?
  synchronize { !!@locked }
end

#unlocktrue, false

Note:

There is more than one way you might not "own the lock" see issue #34

Returns:

  • (true)

    if we held the lock and this method has unlocked it successfully

  • (false)

    if we did not own the lock.



130
131
132
133
134
135
136
137
138
139
140
141
142
# File 'lib/zk/locker/locker_base.rb', line 130

def unlock
  rval = false
  @mutex.synchronize do
    if @locked
      logger.debug { "unlocking" }
      rval = cleanup_lock_path!
      @locked = false
      @node_deletion_watcher = nil
      @cond.broadcast
    end
  end
  rval
end

#unlock!true, false

Deprecated.

the use of unlock! is deprecated and may be removed or have its semantics changed in a future release

Note:

There is more than one way you might not "own the lock" see issue #34

Returns:

  • (true)

    if we held the lock and this method has unlocked it successfully

  • (false)

    if we did not own the lock.



147
148
149
# File 'lib/zk/locker/locker_base.rb', line 147

def unlock!
  unlock
end

#with_lockObject

block caller until lock is aquired, then yield

there is no non-blocking version of this method



68
69
70
71
72
73
# File 'lib/zk/locker/locker_base.rb', line 68

def with_lock
  lock(true)
  yield
ensure
  unlock
end