xref: /titanic_50/usr/src/lib/libshell/common/scripts/filemutexdemo1.sh (revision b5a2d8455dfa3190fc977c4bec53e91c99012767)
1#!/usr/bin/ksh93
2
3#
4# CDDL HEADER START
5#
6# The contents of this file are subject to the terms of the
7# Common Development and Distribution License (the "License").
8# You may not use this file except in compliance with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23
24#
25# Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
26# Use is subject to license terms.
27#
28
29#
30# filemutexdemo1 - a simple locking demo which supports read/write
31# locks and critical sections (like JAVA's "syncronized" keyword)
32#
33
34# Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
35export PATH=/usr/xpg6/bin:/usr/xpg4/bin:/bin:/usr/bin
36
37# Make sure all math stuff runs in the "C" locale to avoid problems
38# with alternative # radix point representations (e.g. ',' instead of
39# '.' in de_DE.*-locales). This needs to be set _before_ any
40# floating-point constants are defined in this script).
41if [[ "${LC_ALL}" != "" ]] ; then
42    export \
43        LC_MONETARY="${LC_ALL}" \
44        LC_MESSAGES="${LC_ALL}" \
45        LC_COLLATE="${LC_ALL}" \
46        LC_CTYPE="${LC_ALL}"
47        unset LC_ALL
48fi
49export LC_NUMERIC=C
50
51# Definition for a mutex which uses the filesystem for locking
52typeset -T filemutex_t=(
53	typeset name
54
55	typeset lock_dirname
56
57	typeset locked_exclusive="false"
58	typeset locked_shared="false"
59
60	# keep track of subshell level. The problem is that we do not know a
61	# way to figure out whether someone calls "unlock" in a subshell and then
62	# leaves the subshell and calls "unlock" again
63	integer subshell=-1
64
65	typeset lock_dirname
66
67	# create a filemutex instance (including lock directory)
68	function create
69	{
70		# make sure we return an error if the init didn't work
71		set -o errexit
72
73		[[ "$1" == "" ]] && return 1
74
75		_.name="$1"
76		_.lock_dirname="/tmp/filemutex_t_${_.name}.lock"
77
78		mkdir "${_.lock_dirname}"
79
80		# last entry, used to mark the mutex as initalised+valid
81		(( _.subshell=.sh.subshell ))
82		return 0
83	}
84
85	# use a filemutex instance (same as "create" but without creating
86	# the lock directory)
87	function create_child
88	{
89		# make sure we return an error if the init didn't work
90		set -o errexit
91
92		[[ "$1" == "" ]] && return 1
93
94		_.name="$1"
95		_.lock_dirname="/tmp/filemutex_t_${_.name}.lock"
96
97		# last entry, used to mark the mutex as initalised+valid
98		(( _.subshell=.sh.subshell ))
99		return 0
100	}
101
102	function check_subshell
103	{
104		(( _.subshell == .sh.subshell )) && return 0
105		print -u2 -f "filemutex_t.%s(%s): Wrong subshell level\n" "$1" "${_.name}"
106		return 1
107	}
108
109	function try_lock_shared
110	{
111		_.check_subshell "try_lock_shared" || return 1
112
113		mkdir "${_.lock_dirname}/shared_${PPID}_$$" 2>/dev/null || return 1
114		_.locked_shared="true"
115		return 0
116	}
117
118	function lock_shared
119	{
120		float interval=0.2
121
122		_.check_subshell "lock_shared" || return 1
123
124		while ! _.try_lock_shared ; do sleep ${interval} ; (( interval+=interval/10. )) ; done
125		return 0
126	}
127
128	function try_lock_exclusive
129	{
130		_.check_subshell "try_lock_exclusive" || return 1
131
132		rmdir "${_.lock_dirname}" 2>/dev/null || return 1
133		_.locked_exclusive="true"
134		return 0
135	}
136
137	function lock_exclusive
138	{
139		float interval=0.2
140
141		_.check_subshell "lock_exclusive" || return 1
142
143		while ! _.try_lock_exclusive ; do sleep ${interval} ; (( interval+=interval/10. )) ; done
144		return 0
145	}
146
147	# critical section support (like java's "synchronized" keyword)
148	function synchronized
149	{
150		integer retcode
151
152		_.check_subshell "synchronized" || return 1
153
154		_.lock_exclusive
155
156		"$@"
157		(( retcode=$? ))
158
159		_.unlock
160
161		return ${retcode}
162	}
163
164	# critical section support with shared lock
165	function synchronized_shared
166	{
167		integer retcode
168
169		_.check_subshell "synchronized_shared" || return 1
170
171		_.lock_shared
172
173		"$@"
174		(( retcode=$? ))
175
176		_.unlock
177
178		return ${retcode}
179	}
180
181	function unlock
182	{
183		# return an error if rmdir/mkdir/check_subshell fail...
184		set -o errexit
185
186		_.check_subshell "unlock"
187
188		if ${_.locked_shared} ; then
189			rmdir "${_.lock_dirname}/shared_${PPID}_$$"
190			_.locked_shared="false"
191			return 0
192		elif ${_.locked_exclusive} ; then
193			mkdir "${_.lock_dirname}"
194			_.locked_exclusive="false"
195			return 0
196		fi
197
198		print -u2 -f "filemutex_t.unlock(%s): mutex '%s' not locked." "$1" "${_.name}"
199		return 1
200	}
201
202	# destroy mutex if noone is using it anymore (not the same as "unset" !!))
203	function destroy
204	{
205		_.check_subshell "destroy" || return 1
206
207		(${_.locked_exclusive} || ${_.locked_shared}) && _.unlock
208		rmdir "${_.lock_dirname}"
209		return 0
210	}
211)
212
213# main
214builtin mkdir
215builtin rmdir
216
217print "## Start."
218
219typeset -r mymutexname="hello_world"
220
221filemutex_t fs
222
223fs.create "${mymutexname}" || print -u2 "Mutex init failed."
224
225print "# Starting child which keeps an exclusive lock for 10 seconds..."
226(
227	filemutex_t child_fs
228
229	child_fs.create_child "${mymutexname}"
230
231	child_fs.lock_exclusive
232	sleep 10
233	child_fs.unlock
234) &
235
236sleep 1
237
238printf "%T: # Waiting to obtain a shared lock...\n"
239fs.lock_shared
240printf "%T: # Obtained shared lock\n"
241
242printf "fs.locked_exclusive=%s, fs.locked_shared=%s\n" "${fs.locked_exclusive}" "${fs.locked_shared}"
243
244ls -lad /tmp/filemutex*/*
245
246printf "%T: # Executing child which runs printf '|%%s|\\\n' 'hello' 'world' inside a synchronized section\n"
247(
248	filemutex_t child_fs
249
250	child_fs.create_child "${mymutexname}"
251
252	child_fs.synchronized printf '|%s|\n' 'hello' 'world'
253) &
254
255printf "%T: # Sleeping 5 secs while holding the shared lock...\n"
256sleep 5.
257
258printf "%T: # Releasing shared lock...\n"
259fs.unlock
260
261sleep 5.
262print "# Destroying lock..."
263fs.destroy
264
265print "## Done."
266
267exit 0
268