1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
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
108
109
110
111
112
113
114
115
116
|
#
# Author:: Serdar Sutay (<serdar@chef.io>)
# Copyright:: Copyright 2013-2016, Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
require "chef/win32/api/synchronization"
require "chef/win32/unicode"
class Chef
module ReservedNames::Win32
class Mutex
include Chef::ReservedNames::Win32::API::Synchronization
def initialize(name)
@name = name
create_system_mutex
end
attr_reader :handle
attr_reader :name
#####################################################
# Attempts to grab the mutex.
# Returns true if the mutex is grabbed or if it's already
# owned; false otherwise.
def test
WaitForSingleObject(handle, 0) == WAIT_OBJECT_0
end
#####################################################
# Attempts to grab the mutex and waits until it is acquired.
def wait
loop do
wait_result = WaitForSingleObject(handle, 1000)
case wait_result
when WAIT_TIMEOUT
# We are periodically waking up in order to give ruby a
# chance to process any signal it got while we were
# sleeping. This condition shouldn't contain any logic
# other than sleeping.
sleep 0.1
when WAIT_ABANDONED
# Previous owner of the mutex died before it can release the
# mutex. Log a warning and continue.
Chef::Log.debug "Existing owner of the mutex exited prematurely."
break
when WAIT_OBJECT_0
# Mutex is successfully acquired.
break
else
Chef::Log.error("Failed to acquire system mutex '#{name}'. Return code: #{wait_result}")
Chef::ReservedNames::Win32::Error.raise!
end
end
end
#####################################################
# Releaes the mutex
def release
# http://msdn.microsoft.com/en-us/library/windows/desktop/ms685066(v=vs.85).aspx
# Note that release method needs to be called more than once
# if mutex is acquired more than once.
unless ReleaseMutex(handle)
# Don't fail things in here if we can't release the mutex.
# Because it will be automatically released when the owner
# of the process goes away and this class is only being used
# to synchronize chef-clients runs on a node.
Chef::Log.error("Can not release mutex '#{name}'. This might cause issues \
if other threads attempt to acquire the mutex.")
Chef::ReservedNames::Win32::Error.raise!
end
end
private
def create_system_mutex
# First check if there exists a mutex in the system with the
# given name. We need only synchronize rights if a mutex is
# already created.
# InheritHandle is set to true so that subprocesses can
# inherit the ownership of the mutex.
@handle = OpenMutexW(SYNCHRONIZE, true, name.to_wstring)
if @handle == 0
# Mutext doesn't exist so create one.
# In the initial creation of the mutex initial_owner is set to
# false so that mutex will not be acquired until someone calls
# acquire.
# In order to call "*W" windows apis, strings needs to be
# encoded as wide strings.
@handle = CreateMutexW(nil, false, name.to_wstring)
# Looks like we can't create the mutex for some reason.
# Fail early.
if @handle == 0
Chef::Log.error("Failed to create system mutex with name'#{name}'")
Chef::ReservedNames::Win32::Error.raise!
end
end
end
end
end
end
|