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
# File 'lib/osctld/lock_registry.rb', line 31

def initialize
  @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



90
91
92
# File 'lib/osctld/lock_registry.rb', line 90

def registry
  @registry
end

Instance Method Details

#do_register(lock) ⇒ Object (protected)



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/osctld/lock_registry.rb', line 105

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



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

def dump
  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

#exportObject



66
67
68
# File 'lib/osctld/lock_registry.rb', line 66

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

#log_typeObject



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

def log_type
  'locks'
end

#register(object, type, state) ⇒ Object

Parameters:

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


54
55
56
57
58
59
60
61
62
63
64
# File 'lib/osctld/lock_registry.rb', line 54

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

#runObject (protected)



92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/osctld/lock_registry.rb', line 92

def run
  loop do
    v = @queue.pop

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

    elsif v == :stop
      break
    end
  end
end

#startObject



38
39
40
# File 'lib/osctld/lock_registry.rb', line 38

def start
  @thread = Thread.new { run }
end

#stopObject



42
43
44
45
46
47
48
49
# File 'lib/osctld/lock_registry.rb', line 42

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

#sync(&block) ⇒ Object (protected)



132
133
134
135
136
137
138
# File 'lib/osctld/lock_registry.rb', line 132

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