Class: OsCtld::LockRegistry

Inherits:
Object
  • Object
show all
Includes:
OsCtl::Lib::Utils::Exception, OsCtl::Lib::Utils::Log, Singleton
Defined in:
lib/osctld/lock_registry.rb

Overview

Global registry of all inclusive/exclusive logs for debugging purposes

All inclusive/exclusive locks that are attempted and held are registered in this class. The idea is that when a deadlock occurs, you can inspect the data available within this class to see what threads have acquired what locks on what objects and get backtraces.

Defined Under Namespace

Classes: Lock

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeLockRegistry

Returns a new instance of LockRegistry.



31
32
33
34
35
36
37
# File 'lib/osctld/lock_registry.rb', line 31

def initialize
  @enabled = nil
  @mutex = Mutex.new
  @queue = Queue.new
  @registry = []
  @last_id = Concurrent::AtomicFixnum.new(0)
end

Instance Attribute Details

#registryObject (readonly, protected)

Returns the value of attribute registry.



115
116
117
# File 'lib/osctld/lock_registry.rb', line 115

def registry
  @registry
end

Instance Method Details

#do_register(lock) ⇒ Object (protected)



130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
# File 'lib/osctld/lock_registry.rb', line 130

def do_register(lock)
  sync do
    case lock.state
    when :waiting
      if lock.type != :exclusive || !registry.detect { |v| v == lock && v.state == :waiting }
        registry << lock
      end

    when :locked
      waiting = registry.detect { |v| v == lock && v.state == :waiting }

      if waiting
        waiting.state = :locked
        waiting.backtrace = lock.backtrace
      else
        registry << lock
      end

    when :unlocked, :timeout
      registry.delete_if { |v| v == lock }

    else
      registry << lock
    end
  end
end

#dumpObject



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/osctld/lock_registry.rb', line 90

def dump
  unless @enabled
    log(:debug, 'locks', 'Lock registry disabled')
    return
  end

  log(:debug, 'locks', 'Dumping lock registry')

  export.each do |lock|
    log(
      :debug,
      "id=#{lock[:id]},thread=#{lock[:thread]},type=#{lock[:type]},"+
      "state=#{lock[:state]}"
    )
    log(:debug, denixstorify(lock[:backtrace]).join("\n"))
  end

  log(:debug, 'locks', 'End of dump')
end

#enabled?Boolean

Returns:

  • (Boolean)


48
49
50
# File 'lib/osctld/lock_registry.rb', line 48

def enabled?
  @enabled
end

#exportObject



84
85
86
87
88
# File 'lib/osctld/lock_registry.rb', line 84

def export
  return [] unless @enabled

  sync { registry.map(&:to_h) }
end

#log_typeObject



110
111
112
# File 'lib/osctld/lock_registry.rb', line 110

def log_type
  'locks'
end

#register(object, type, state) ⇒ Object

Parameters:

  • object (Object)
  • type (:inclusive, :exclusive)
  • state (:waiting, :locked, :unlocked, :timeout)


70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/osctld/lock_registry.rb', line 70

def register(object, type, state)
  return unless @enabled

  @queue << Lock.new(
    @last_id.increment,
    Time.now,
    Thread.current,
    object,
    type,
    state,
    caller[1..-1]
  )
end

#runObject (protected)



117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/osctld/lock_registry.rb', line 117

def run
  loop do
    v = @queue.pop

    if v.is_a?(Lock)
      do_register(v)

    elsif v == :stop
      break
    end
  end
end

#setup(enabled) ⇒ Object



39
40
41
42
43
44
45
46
# File 'lib/osctld/lock_registry.rb', line 39

def setup(enabled)
  unless @enabled === nil
    fail 'programming error: setup can be called only once'
  end

  @enabled = enabled
  start if enabled
end

#startObject



52
53
54
55
56
# File 'lib/osctld/lock_registry.rb', line 52

def start
  return unless @enabled

  @thread = Thread.new { run }
end

#stopObject



58
59
60
61
62
63
64
65
# File 'lib/osctld/lock_registry.rb', line 58

def stop
  if @thread
    @queue.clear
    @queue << :stop
    @thread.join
    @thread = nil
  end
end

#sync(&block) ⇒ Object (protected)



157
158
159
160
161
162
163
# File 'lib/osctld/lock_registry.rb', line 157

def sync(&block)
  if @mutex.owned?
    yield
  else
    @mutex.synchronize(&block)
  end
end